commit 3785bdb3bb9ab4cf2addca4ff70f33ee68594fb4 Author: Chopper Date: Thu May 13 10:41:46 2021 +0800 commit message diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1f9b9468 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +/.idea/ +target +*.iml +*.class +.DS_Store +*.classpath +*.project +*.log +.settings/ +.factorypath + +log/ +*.factorypath +*.log +lili-shop/src/main/java/cn/lili/generator/CodeGenerator.java +lili-logs + + +### 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/ diff --git a/README.md b/README.md new file mode 100644 index 00000000..3ac223be --- /dev/null +++ b/README.md @@ -0,0 +1,204 @@ +## Lilishop B2B2C商城系统 + +### 介绍 +**官网**:https://pickmall.cn + +Lilishop 是一款Java开发,基于SpringBoot的B2B2C多用户商城,前端使用 Vue、uniapp-app开发 **系统全端全部代码开源** + +商城展示端包含 PC、H5、小程序、APP。 + +商城包含 会员模块、**第三方登录模块**、**第三方支付模块**、**楼层装修模块**、订单模块、分销模块、文章模块、系统设置模块、流量分析模块 + +系统包含各种中间件、搜索引擎、多级缓存、分布式事务、分布式任务调度等,支持Docker,支持k8s。是一款高性能,支持高并发等商城系统。 + +开箱即用,简单配置即可部署一套属于您的系统。 + +完美支持二开、学生毕业设计答辩等各个场景 + +### 文档 + +**产品文档**(需求、架构、使用、部署、开发):https://docs.pickmall.cn + + +### 项目链接 + +**Java后台**:https://gitee.com/beijing_hongye_huicheng/lili-shop.git + +**Vue后台前端**: https://gitee.com/beijing_hongye_huicheng/lili-shop-ui.git + +**Uni-app**:https://gitee.com/beijing_hongye_huicheng/lili-shop-uniapp.git + +**docker一键部署**:https://gitee.com/beijing_hongye_huicheng/docker.git + +### 演示地址 + +**运营后台**:https://admin-b2b2c.pickmall.cn 账号:admin/123456 + +**店铺后台**:https://store-b2b2c.pickmall.cn 账号:13011111111/111111 + +**用户前台**:https://pc-b2b2c.pickmall.cn + +**移动端**:https://m-b2b2c.pickmall.cn + +![image-20210511171611793](https://pickmall.cn/assets/imgs/h5-qrcode.png) + +### 3行命令搭建本地环境 + +#### 下载docker脚本 +`git clone https://gitee.com/beijing_hongye_huicheng/docker.git ` +##### 部署基础环境 +`docker-compose up -d` +##### 部署应用 +`docker-compose -f docker-compose-application.yml up -d` + +### 交流群 + +**QQ群**:961316482 + + + +### 技术选型 + +##### Java后台 + +| 说明 | 框架 | +| -------------- | --------------- | +| 基础框架 | Spring Boot | +| MVC框架 | Spring MVC | +| 持久框架 | Mybatis-Plus | +| 程序构建 | Maven | +| 关系型数据库 | MySQL | +| 消息中间件AMQP | RocketMQ | +| 缓存 | Redis +MongoDB | +| 搜索引擎 | Elasticsearch | +| 安全框架 | Spring Security | +| 数据库连接池 | Druid | +| 数据库分库分表 | sharding | +| 定时任务 | xxl-job | +| 负载均衡 | Nginx | +| 静态资源 | 阿里云OSS | +| 短信 | 阿里云短信 | +| 日志处理 | Log4j | +| 接口规范 | RESTful | +| 接口文档 | Swagger | +| 认证 | JWT | + +##### 前端-运营后台、店铺后台 + +| 说明 | 框架 | +| ---------- | ---------- | +| 构建工具 | webpack | +| JS版本 | ES6 | +| 基础JS框架 | Vue.js | +| 视频播放器 | Dplayer | +| 路由管理 | Vue Router | +| 状态管理 | Vuex | +| 基础UI库 | iView | +| UI界面基于 | iView | +| 网络请求 | axios | +| CSS预处理 | scss | +| 代码检查 | ESLint | +| 数据可视化 | AntV g2 | +| 地图引擎 | amap | + +##### 前端-移动端 + +| 说明 | 架构 | +| --------- | ------- | +| 基础UI库 | uViewui | +| 基础框架 | uni-app | +| CSS预处理 | scss | +| 地图引擎 | amap | + + + +### 功能列表 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
运营后台功能
首页平台统计、待办事项、流量统计
会员会员列表
评价列表
积分历史
会员资金
充值记录
订单商品订单
订单售后
交易投诉
售后原因
收款流水
退款流水
商品商品列表
商品审核
商品分类
商品品牌
商品规格
计量单位
促销优惠券
秒杀活动
拼团活动
积分商品
积分分类
店铺店铺管理
店铺结算
店铺结算
店铺结算
店铺对账
运营店铺对账
PC端楼层装修
移动端楼层装修
分销管理
文章管理
意见反馈
站内信
短信管理
APP版本管理
统计会员统计
订单统计
商品统计
流量统计
设置用户管理
菜单管理
部门管理
系统设置
OSS资源
行政地区
物流公司
信任登录
支付设置
验证码管理
敏感词管理
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
店铺后台功能列表
首页店铺信息
待办事项
平台公告
商品商品发布
商品列表
店铺商品分类
订单商品订单
退货管理
退款管理
投诉管理
评价管理
财务财务对账
店铺结算
发票管理
促销拼团管理
秒杀活动
满额活动
优惠券
分销商品
统计商品统计
订单统计
设置配送模板
物流公司
店铺设置
自提点管理
系统消息
+ +### 授权 +Lilishop学习免费,限制商用,如果需要商业使用请联系我们。QQ3409056806 \ No newline at end of file diff --git a/admin/.gitignore b/admin/.gitignore new file mode 100644 index 00000000..549e00a2 --- /dev/null +++ b/admin/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### 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/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/admin/.mvn/wrapper/MavenWrapperDownloader.java b/admin/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..a45eb6ba --- /dev/null +++ b/admin/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * 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 + * + * https://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. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/admin/.mvn/wrapper/maven-wrapper.jar b/admin/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 00000000..2cc7d4a5 Binary files /dev/null and b/admin/.mvn/wrapper/maven-wrapper.jar differ diff --git a/admin/.mvn/wrapper/maven-wrapper.properties b/admin/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..642d572c --- /dev/null +++ b/admin/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/admin/pom.xml b/admin/pom.xml new file mode 100644 index 00000000..e188f455 --- /dev/null +++ b/admin/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + + cn.lili + lili-shop-parent + 1.0.1 + + + admin + + + + de.codecentric + spring-boot-admin-starter-server + 2.3.1 + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/admin/src/main/java/cn/lili/admin/AdminApplication.java b/admin/src/main/java/cn/lili/admin/AdminApplication.java new file mode 100644 index 00000000..d88d42e3 --- /dev/null +++ b/admin/src/main/java/cn/lili/admin/AdminApplication.java @@ -0,0 +1,50 @@ +package cn.lili.admin; + +import de.codecentric.boot.admin.server.config.AdminServerProperties; +import de.codecentric.boot.admin.server.config.EnableAdminServer; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +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 java.util.UUID; + +@Configuration +@EnableAutoConfiguration +@EnableAdminServer +public class AdminApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminApplication.class, args); + } + + @Configuration + public class SecuritySecureConfig extends WebSecurityConfigurerAdapter { + + private final AdminServerProperties adminServer; + + 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().antMatchers("/instances**").permitAll(); + http.authorizeRequests( + (authorizeRequests) -> authorizeRequests.antMatchers(this.adminServer.path("/assets/**")).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()) // 启用HTTP基本支持。这是Spring Boot Admin Client注册所必需的。 + .csrf().disable() + .rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600)); + } + + } +} diff --git a/admin/src/main/resources/application.properties b/admin/src/main/resources/application.properties new file mode 100644 index 00000000..8e6cdb8a --- /dev/null +++ b/admin/src/main/resources/application.properties @@ -0,0 +1,20 @@ +# 应用程序名称 +spring.application.name=SpringBootAdmin +# 应用程序端口 +server.port=8000 +management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=always +#账号密码 +spring.security.user.name=admin +spring.security.user.password=admin +spring.mail.host=smtp.qq.com +# to和from都要配置,否则发送邮件时会报错 +spring.boot.admin.notify.mail.to=1814994716@qq.com +spring.boot.admin.notify.mail.from=1814994716@qq.com +# 邮件的用户名和密码 +spring.mail.username=1814994716@qq.com +spring.mail.password=abcdefg123456!@#$%^ +# 日志文件路径 +logging.file.path=lili-logs/admin +# 文件格式 +logging.pattern.file=%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx diff --git a/buyer-api/pom.xml b/buyer-api/pom.xml new file mode 100644 index 00000000..7499e497 --- /dev/null +++ b/buyer-api/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + 4.3 + + buyer-api + + cn.lili + lili-shop-parent + 1.0.1 + + + + + cn.lili + framework + 1.0.1 + + + + com.jfinal + enjoy + ${enjoy.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/buyer-api/src/main/java/cn/lili/BuyerApiApplication.java b/buyer-api/src/main/java/cn/lili/BuyerApiApplication.java new file mode 100644 index 00000000..3e8f283b --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/BuyerApiApplication.java @@ -0,0 +1,36 @@ +package cn.lili; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.core.task.TaskExecutor; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +/** + * 买家API + * + * @author Chopper + * @date 2020/11/16 10:03 下午 + */ +@SpringBootApplication +@EnableJpaAuditing +@EnableCaching +@EnableAsync +public class BuyerApiApplication { + + + @Primary + @Bean + public TaskExecutor primaryTaskExecutor() { + return new ThreadPoolTaskExecutor(); + } + + public static void main(String[] args) { + System.setProperty("es.set.netty.runtime.available.processors", "false"); + SpringApplication.run(BuyerApiApplication.class, args); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/SwaggerBootstrapUiDemoApplication.java b/buyer-api/src/main/java/cn/lili/controller/SwaggerBootstrapUiDemoApplication.java new file mode 100644 index 00000000..446255b5 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/SwaggerBootstrapUiDemoApplication.java @@ -0,0 +1,23 @@ +package cn.lili.controller; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import springfox.documentation.spring.web.SpringfoxWebMvcConfiguration; + +/** + * SwaggerBootstrapUiDemoApplication + * + * @author Chopper + * @version v1.0 + * 2020-12-09 20:09 + */ +@ConditionalOnClass(SpringfoxWebMvcConfiguration.class) +public class SwaggerBootstrapUiDemoApplication implements WebMvcConfigurer { + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + } +} \ No newline at end of file diff --git a/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionBuyerController.java new file mode 100644 index 00000000..d434ef12 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionBuyerController.java @@ -0,0 +1,68 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dos.Distribution; +import cn.lili.modules.distribution.entity.dos.DistributionOrder; +import cn.lili.modules.distribution.entity.vos.DistributionOrderSearchParams; +import cn.lili.modules.distribution.service.DistributionOrderService; +import cn.lili.modules.distribution.service.DistributionService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +/** + * 买家端,分销员接口 + * + * @author pikachu + * @date: 2020/11/16 10:03 下午 + */ +@RestController +@Api(tags = "买家端,分销员接口") +@RequestMapping("/buyer/distribution") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionBuyerController { + + /** + * 分销员 + */ + private final DistributionService distributionService; + /** + * 分销员订单 + */ + private final DistributionOrderService distributionOrderService; + + //申请分销员 + @ApiOperation(value = "申请分销员") + @ApiImplicitParams({ + @ApiImplicitParam(name = "name", value = "姓名", required = true, paramType = "query", dataType = "String"), + @ApiImplicitParam(name = "idNumber", value = "身份证号", required = true, paramType = "query", dataType = "String") + }) + @PostMapping + public ResultMessage applyDistribution(@RequestParam String name, @RequestParam String idNumber) { + return ResultUtil.data(distributionService.applyDistribution(name, idNumber)); + } + + @ApiOperation(value = "获取分销员分页订单列表") + @GetMapping("/distributionOrder") + public ResultMessage> distributionOrderPage(DistributionOrderSearchParams distributionOrderSearchParams) { + distributionOrderSearchParams.setDistributionId(UserContext.getCurrentUser().getId()); + return ResultUtil.data(distributionOrderService.getDistributionOrderPage(distributionOrderSearchParams)); + } + + @ApiOperation(value = "获取当前会员的分销员信息", notes = "可根据分销员信息查询待提现金额以及冻结金额等信息") + @GetMapping + public ResultMessage getDistribution() { + //检查分销开关 + distributionService.checkDistributionSetting(); + + return ResultUtil.data(distributionService.getDistribution()); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionCashBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionCashBuyerController.java new file mode 100644 index 00000000..d6038795 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionCashBuyerController.java @@ -0,0 +1,63 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dos.DistributionCash; +import cn.lili.modules.distribution.service.DistributionCashService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import springfox.documentation.annotations.ApiIgnore; + +import javax.validation.constraints.NotNull; + + +/** + * 买家端,分销商品佣金提现接口 + * + * @author pikachu + * @date: 2020/11/16 10:03 下午 + */ +@RestController +@Api(tags = "买家端,分销商品佣金提现接口") +@RequestMapping("/buyer/distribution/cash") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionCashBuyerController { + + /** + * 分销佣金 + */ + private final DistributionCashService distributionCashService; + /** + * 分销员提现 + */ + private final DistributionCashService distributorCashService; + + + @ApiOperation(value = "分销员提现") + @ApiImplicitParams({ + @ApiImplicitParam(name = "price", value = "申请金额", required = true, paramType = "query", dataType = "double") + }) + @PostMapping + public ResultMessage cash(@NotNull @ApiIgnore Double price) { + Boolean result = distributionCashService.cash(price); + return ResultUtil.data(result); + } + + @ApiOperation(value = "分销员提现历史") + @GetMapping + public ResultMessage> casHistory(PageVO page) { + return ResultUtil.data(distributorCashService.getDistributionCash(page)); + } + + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionGoodsBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionGoodsBuyerController.java new file mode 100644 index 00000000..b651ad41 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionGoodsBuyerController.java @@ -0,0 +1,61 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dto.DistributionGoodsSearchParams; +import cn.lili.modules.distribution.entity.vos.DistributionGoodsVO; +import cn.lili.modules.distribution.service.DistributionGoodsService; +import cn.lili.modules.distribution.service.DistributionSelectedGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotNull; + +/** + * 买家端,分销商品接口 + * + * @author Bulbasaur + * @date: 2020/11/16 10:06 下午 + */ +@RestController +@Api(tags = "买家端,分销商品接口") +@RequestMapping("/buyer/distributionGoods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionGoodsBuyerController { + + /** + * 分销商品 + */ + private final DistributionGoodsService distributionGoodsService; + /** + * 选择分销商品 + */ + private final DistributionSelectedGoodsService distributionSelectedGoodsService; + + + @ApiOperation(value = "获取分销商商品列表") + @GetMapping + public ResultMessage> distributionGoods(DistributionGoodsSearchParams distributionGoodsSearchParams) { + return ResultUtil.data(distributionGoodsService.goodsPage(distributionGoodsSearchParams)); + } + + @ApiOperation(value = "选择分销商品") + @ApiImplicitParam(name = "distributionGoodsId", value = "分销ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/checked/{distributionGoodsId}") + public ResultMessage> distributionCheckGoods( + @NotNull(message = "分销商品不能为空") @PathVariable("distributionGoodsId") String distributionGoodsId) { + if (distributionSelectedGoodsService.add(distributionGoodsId)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionOrderBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionOrderBuyerController.java new file mode 100644 index 00000000..4f8585a5 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/distribution/DistributionOrderBuyerController.java @@ -0,0 +1,50 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dos.DistributionOrder; +import cn.lili.modules.distribution.entity.vos.DistributionOrderSearchParams; +import cn.lili.modules.distribution.service.DistributionOrderService; +import cn.lili.modules.distribution.service.DistributionService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + + +/** + * 买家端,分销商品佣金提现接口 + * + * @author pikachu + * @date: 2020/11/16 10:03 下午 + */ +@RestController +@Api(tags = "买家端,分销订单接口") +@RequestMapping("/buyer/distribution/order") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionOrderBuyerController { + + /** + * 分销订单 + */ + private final DistributionOrderService distributionOrderService; + /** + * 分销员 + */ + private final DistributionService distributionService; + + + @ApiOperation(value = "分销员订单") + @GetMapping + public ResultMessage> casHistory(DistributionOrderSearchParams distributionOrderSearchParams) { + //获取当前登录的分销员 + distributionOrderSearchParams.setDistributionId(distributionService.getDistribution().getId()); + return ResultUtil.data(distributionOrderService.getDistributionOrderPage(distributionOrderSearchParams)); + } + + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/goods/CategoryBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/goods/CategoryBuyerController.java new file mode 100644 index 00000000..ced66375 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/goods/CategoryBuyerController.java @@ -0,0 +1,44 @@ +package cn.lili.controller.goods; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.vos.CategoryVO; +import cn.lili.modules.goods.service.CategoryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotNull; +import java.util.List; + + +/** + * 买家端,商品分类接口 + * + * @author Chopper + * @date: 2020/11/16 10:05 下午 + */ +@RestController +@Api(tags = "买家端,商品分类接口") +@RequestMapping("/buyer/category") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategoryBuyerController { + /** + * 商品分类 + */ + private final CategoryService categoryService; + + @ApiOperation(value = "获取商品分类列表") + @ApiImplicitParam(name = "parentId", value = "上级分类ID,全部分类为:0", required = true, dataType = "Long", paramType = "path") + @GetMapping(value = "/get/{parentId}") + public ResultMessage> list(@NotNull(message = "分类ID不能为空") @PathVariable String parentId) { + + return ResultUtil.data(categoryService.listAllChildren(parentId)); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/goods/GoodsBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/goods/GoodsBuyerController.java new file mode 100644 index 00000000..81d61d89 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/goods/GoodsBuyerController.java @@ -0,0 +1,124 @@ +package cn.lili.controller.goods; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.service.DistributionService; +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.dto.GoodsSearchParams; +import cn.lili.modules.goods.entity.vos.GoodsVO; +import cn.lili.modules.goods.service.GoodsService; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.entity.dos.EsGoodsRelatedInfo; +import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO; +import cn.lili.modules.search.service.EsGoodsSearchService; +import cn.lili.modules.statistics.aop.PageViewPoint; +import cn.lili.modules.statistics.aop.enums.PageViewEnum; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.Map; + +/** + * 买家端,商品接口 + * + * @author Chopper + * @date 2020/11/16 10:06 下午 + */ +@Api(tags = "买家端,商品接口") +@RestController +@RequestMapping("/buyer/goods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsBuyerController { + + /** + * 商品 + */ + private final GoodsService goodsService; + /** + * 商品SKU + */ + private final GoodsSkuService goodsSkuService; + /** + * ES商品搜索 + */ + private final EsGoodsSearchService goodsSearchService; + /** + * 分销员 + */ + private final DistributionService distributionService; + + + @ApiOperation(value = "通过id获取商品信息") + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, paramType = "path", dataType = "Long") + @GetMapping(value = "/get/{goodsId}") + public ResultMessage get(@NotNull(message = "商品ID不能为空") @PathVariable("goodsId") String id) { + return ResultUtil.data(goodsService.getGoodsVO(id)); + } + + @ApiOperation(value = "通过id获取商品信息") + @ApiImplicitParams({ + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, paramType = "path"), + @ApiImplicitParam(name = "skuId", value = "skuId", required = true, paramType = "path"), + @ApiImplicitParam(name = "distributionId", value = "分销员ID", dataType = "String", paramType = "query") + }) + @GetMapping(value = "/sku/{goodsId}/{skuId}") + @PageViewPoint(type = PageViewEnum.SKU, id = "#id") + public ResultMessage> getSku(@NotNull(message = "商品ID不能为空") @PathVariable("goodsId") String goodsId, + @NotNull(message = "SKU ID不能为空") @PathVariable("skuId") String skuId, + String distributionId) { + + Map map = goodsSkuService.getGoodsSkuDetail(goodsId, skuId); + + //判断如果传递分销员则进行记录 + if (CharSequenceUtil.isNotEmpty(distributionId)) { + distributionService.bindingDistribution(distributionId); + } + + return ResultUtil.data(map); + } + + @ApiOperation(value = "获取商品分页列表") + @GetMapping + public ResultMessage> getByPage(GoodsSearchParams goodsSearchParams) { + return ResultUtil.data(goodsService.queryByParams(goodsSearchParams)); + } + + @ApiOperation(value = "从ES中获取商品信息") + @GetMapping("/es") + public ResultMessage> getGoodsByPageFromEs(EsGoodsSearchDTO goodsSearchParams, PageVO pageVO) { + pageVO.setNotConvert(true); + Page esGoodsIndices = goodsSearchService.searchGoods(goodsSearchParams, pageVO); + return ResultUtil.data(esGoodsIndices); + } + + @ApiOperation(value = "从ES中获取相关商品品牌名称,分类名称及属性") + @GetMapping("/es/related") + public ResultMessage getGoodsRelatedByPageFromEs(EsGoodsSearchDTO goodsSearchParams, PageVO pageVO) { + pageVO.setNotConvert(true); + EsGoodsRelatedInfo selector = goodsSearchService.getSelector(goodsSearchParams, pageVO); + return ResultUtil.data(selector); + } + + @ApiOperation(value = "获取搜索热词") + @GetMapping("/hot-words") + public ResultMessage> getGoodsHotWords(Integer start, Integer end) { + List hotWords = goodsSearchService.getHotWords(start, end); + return ResultUtil.data(hotWords); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/CouponBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/CouponBuyerController.java new file mode 100644 index 00000000..eb132dc4 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/CouponBuyerController.java @@ -0,0 +1,98 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import cn.lili.modules.promotion.entity.vos.CouponSearchParams; +import cn.lili.modules.promotion.entity.vos.CouponVO; +import cn.lili.modules.promotion.service.CouponService; +import cn.lili.modules.promotion.service.MemberCouponService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotNull; + +/** + * 买家端,买家优惠券接口 + * + * @author paulG + * @date 2020/11/17 3:35 下午 + */ +@RestController +@Api(tags = "买家端,买家优惠券接口") +@RequestMapping("/buyer/promotion/coupon") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CouponBuyerController { + + /** + * 优惠券 + */ + private final CouponService couponService; + + /** + * 会员优惠券 + */ + private final MemberCouponService memberCouponService; + + @GetMapping + @ApiOperation(value = "获取可领取优惠券列表") + public ResultMessage> getCouponList(CouponSearchParams queryParam, PageVO page) { + IPage canUseCoupons = couponService.getCanReceiveCoupons(queryParam, page); + return ResultUtil.data(canUseCoupons); + } + + @ApiOperation(value = "获取当前会员的优惠券列表") + @GetMapping("/getCoupons") + public ResultMessage> getCoupons(CouponSearchParams param, PageVO pageVo) { + param.setMemberId(UserContext.getCurrentUser().getId()); + return ResultUtil.data(memberCouponService.getMemberCoupons(param, pageVo)); + } + + @ApiOperation(value = "获取当前会员的对于当前商品可使用的优惠券列表") + @GetMapping("/canUse") + public ResultMessage> getCouponsByCanUse(CouponSearchParams param, Double totalPrice, PageVO pageVo) { + param.setMemberId(UserContext.getCurrentUser().getId()); + return ResultUtil.data(memberCouponService.getMemberCouponsByCanUse(param, totalPrice, pageVo)); + } + + @ApiOperation(value = "获取当前会员可使用的优惠券数量") + @GetMapping("/getCouponsNum") + public ResultMessage getMemberCouponsNum() { + return ResultUtil.data(memberCouponService.getMemberCouponsNum()); + } + + @ApiOperation(value = "会员领取优惠券") + @ApiImplicitParams({ + @ApiImplicitParam(name = "couponId", value = "优惠券ID", required = true, dataType = "Long", paramType = "path") + }) + @GetMapping("/receive/{couponId}") + public ResultMessage receiveCoupon(@NotNull(message = "优惠券ID不能为空") @PathVariable("couponId") String couponId) { + memberCouponService.checkCouponLimit(couponId, UserContext.getCurrentUser().getId()); + memberCouponService.receiveCoupon(couponId, UserContext.getCurrentUser().getId(), UserContext.getCurrentUser().getNickName()); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "通过id获取") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "优惠券ID", required = true, dataType = "Long", paramType = "path") + }) + @GetMapping(value = "/get/{id}") + public ResultMessage get(@NotNull(message = "优惠券ID不能为空") @PathVariable("id") String id) { + MemberCoupon memberCoupon = memberCouponService.getById(id); + return ResultUtil.data(memberCoupon); + } + + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/FootprintController.java b/buyer-api/src/main/java/cn/lili/controller/member/FootprintController.java new file mode 100644 index 00000000..1f42daa2 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/FootprintController.java @@ -0,0 +1,68 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.service.FootprintService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; +import java.util.List; + + +/** + * 买家端,浏览历史接口 + * + * @author Chopper + * @date: 2020/11/16 10:06 下午 + */ +@RestController +@Api(tags = "买家端,浏览历史接口") +@RequestMapping("/buyer/footprint") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FootprintController { + + /** + * 会员足迹 + */ + private final FootprintService footprintService; + + @ApiOperation(value = "分页获取") + @GetMapping + public ResultMessage> getByPage(PageVO page) { + return ResultUtil.data(footprintService.footPrintPage(page)); + } + + @ApiOperation(value = "根据id删除") + @ApiImplicitParam(name = "ids", value = "商品ID", required = true, allowMultiple = true, dataType = "String", paramType = "path") + @DeleteMapping(value = "/delByIds/{ids}") + public ResultMessage delAllByIds(@NotNull(message = "商品ID不能为空") @PathVariable("ids") List ids) { + if (footprintService.deleteByIds(ids)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "清空足迹") + @DeleteMapping + public ResultMessage deleteAll() { + if (footprintService.clean()) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "获取当前会员足迹数量") + @GetMapping(value = "/getFootprintNum") + public ResultMessage getFootprintNum() { + return ResultUtil.data(footprintService.getFootprintNum()); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/MemberAddressBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/MemberAddressBuyerController.java new file mode 100644 index 00000000..c890b208 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/MemberAddressBuyerController.java @@ -0,0 +1,86 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.base.service.RegionService; +import cn.lili.modules.member.entity.dos.MemberAddress; +import cn.lili.modules.promotion.service.MemberAddressService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + + +/** + * 买家端,会员地址接口 + * + * @author Bulbasaur + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "买家端,会员地址接口") +@RequestMapping("/buyer/memberAddress") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberAddressBuyerController { + + /** + * 会员收件地址 + */ + private final MemberAddressService memberAddressService; + + private final RegionService regionService; + + @ApiOperation(value = "获取会员收件地址分页列表") + @GetMapping + public ResultMessage> page(PageVO page) { + return ResultUtil.data(memberAddressService.getAddressByMember(page, UserContext.getCurrentUser().getId())); + } + + @ApiOperation(value = "根据ID获取会员收件地址") + @ApiImplicitParam(name = "id", value = "会员地址ID", dataType = "String", paramType = "path") + @GetMapping(value = "/get/{id}") + public ResultMessage getShippingAddress(@PathVariable String id) { + return ResultUtil.data(memberAddressService.getMemberAddress(id)); + } + + @ApiOperation(value = "获取当前会员默认收件地址") + @GetMapping(value = "/get/default") + public ResultMessage getDefaultShippingAddress() { + return ResultUtil.data(memberAddressService.getDefaultMemberAddress()); + } + + @ApiOperation(value = "新增会员收件地址") + @PostMapping + public ResultMessage addShippingAddress(@Valid MemberAddress shippingAddress) { + //添加会员地址 + shippingAddress.setMemberId(UserContext.getCurrentUser().getId()); + return ResultUtil.data(memberAddressService.saveMemberAddress(shippingAddress)); + } + + @ApiOperation(value = "修改会员收件地址") + @PutMapping + public ResultMessage editShippingAddress(@Valid MemberAddress shippingAddress) { + //修改会员地址 + shippingAddress.setMemberId(UserContext.getCurrentUser().getId()); + return ResultUtil.data(memberAddressService.updateMemberAddress(shippingAddress)); + } + + @ApiOperation(value = "删除会员收件地址") + @ApiImplicitParam(name = "id", value = "会员地址ID", dataType = "String", paramType = "path") + @DeleteMapping(value = "/delById/{id}") + public ResultMessage delShippingAddressById(@PathVariable String id) { + if (memberAddressService.removeMemberAddress(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/MemberCollectionController.java b/buyer-api/src/main/java/cn/lili/controller/member/MemberCollectionController.java new file mode 100644 index 00000000..13065f9a --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/MemberCollectionController.java @@ -0,0 +1,91 @@ +package cn.lili.controller.member; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.service.GoodsCollectionService; +import cn.lili.modules.member.service.StoreCollectionService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 买家端,会员收藏接口 + * + * @author Chopper + * @date: 2020/11/17 2:32 下午 + */ +@RestController +@Api(tags = "买家端,会员收藏接口") +@RequestMapping("/buyer/member/collection") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberCollectionController { + + /** + * 会员商品收藏 + */ + private final GoodsCollectionService goodsCollectionService; + /** + * 会员店铺 + */ + private final StoreCollectionService storeCollectionService; + + @ApiOperation(value = "查询会员收藏列表") + @ApiImplicitParam(name = "type", value = "类型", dataType = "String", paramType = "path", example = "GOODS:商品,STORE:店铺") + @GetMapping("/{type}") + public ResultMessage goodsList(@PathVariable String type, PageVO page) { + if (type.equals("GOODS")) { + return ResultUtil.data(goodsCollectionService.goodsCollection(page)); + } + return ResultUtil.data(storeCollectionService.storeCollection(page)); + } + + @ApiOperation(value = "添加会员收藏") + @ApiImplicitParams({ + @ApiImplicitParam(name = "type", value = "类型", dataType = "String", paramType = "path", example = "GOODS:商品,STORE:店铺"), + @ApiImplicitParam(name = "num", value = "值", dataType = "Long", paramType = "path") + }) + @PostMapping("/add/{type}/{id}") + public ResultMessage addGoodsCollection(@PathVariable String type, + @NotNull(message = "值不能为空") @PathVariable String id) { + if (type.equals("GOODS")) { + return ResultUtil.data(goodsCollectionService.addGoodsCollection(id)); + } + return ResultUtil.data(storeCollectionService.addStoreCollection(id)); + + } + + @ApiOperation(value = "删除会员收藏") + @ApiImplicitParams({ + @ApiImplicitParam(name = "type", value = "类型", dataType = "String", paramType = "path", example = "GOODS:商品,STORE:店铺"), + @ApiImplicitParam(name = "num", value = "值", dataType = "Long", paramType = "path") + }) + @DeleteMapping(value = "/delete/{type}/{id}") + public ResultMessage deleteGoodsCollection(@PathVariable String type, + @NotNull(message = "值不能为空") @PathVariable String id) { + if (type.equals("GOODS")) { + return ResultUtil.data(goodsCollectionService.deleteGoodsCollection(id)); + } + return ResultUtil.data(storeCollectionService.deleteStoreCollection(id)); + } + + @ApiOperation(value = "查询会员是否收藏") + @ApiImplicitParams({ + @ApiImplicitParam(name = "type", value = "类型", dataType = "String", paramType = "path", example = "GOODS:商品,STORE:店铺"), + @ApiImplicitParam(name = "id", value = "值", dataType = "String", paramType = "path") + }) + @GetMapping(value = "/isCollection/{type}/{id}") + public ResultMessage isCollection(@PathVariable String type, + @NotNull(message = "值不能为空") @PathVariable String id) { + if (type.equals("GOODS")) { + return ResultUtil.data(this.goodsCollectionService.isCollection(id)); + } + return ResultUtil.data(this.storeCollectionService.isCollection(id)); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/MemberEvaluationBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/MemberEvaluationBuyerController.java new file mode 100644 index 00000000..c30c6f63 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/MemberEvaluationBuyerController.java @@ -0,0 +1,78 @@ +package cn.lili.controller.member; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import cn.lili.modules.member.entity.dto.EvaluationQueryParams; +import cn.lili.modules.member.entity.dto.MemberEvaluationDTO; +import cn.lili.modules.member.entity.vo.EvaluationNumberVO; +import cn.lili.modules.member.entity.vo.MemberEvaluationVO; +import cn.lili.modules.member.service.MemberEvaluationService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * 买家端,会员商品评价接口 + * + * @author Bulbasaur + * @date: 2020/11/16 10:08 下午 + */ +@RestController +@Api(tags = "买家端,会员商品评价接口") +@RequestMapping("/buyer/memberEvaluation") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberEvaluationBuyerController { + + /** + * 会员商品评价 + */ + private final MemberEvaluationService memberEvaluationService; + + @ApiOperation(value = "添加会员评价") + @PostMapping + public ResultMessage save(@Valid MemberEvaluationDTO memberEvaluationDTO) { + return ResultUtil.data(memberEvaluationService.addMemberEvaluation(memberEvaluationDTO)); + } + + @ApiOperation(value = "查看会员评价详情") + @ApiImplicitParam(name = "id", value = "评价ID", required = true, paramType = "path") + @GetMapping(value = "/get/{id}") + public ResultMessage save(@NotNull(message = "评价ID不能为空") @PathVariable("id") String id) { + return ResultUtil.data(memberEvaluationService.queryById(id)); + + } + + @ApiOperation(value = "查看当前会员评价列表") + @GetMapping + public ResultMessage> queryMineEvaluation(EvaluationQueryParams evaluationQueryParams) { + //设置当前登录会员 + evaluationQueryParams.setMemberId(UserContext.getCurrentUser().getId()); + return ResultUtil.data(memberEvaluationService.queryByParams(evaluationQueryParams)); + } + + @ApiOperation(value = "查看某一个商品的评价列表") + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, dataType = "Long", paramType = "path") + @GetMapping(value = "/{goodsId}/goodsEvaluation") + public ResultMessage> queryGoodsEvaluation(EvaluationQueryParams evaluationQueryParams, + @NotNull @PathVariable("goodsId") String goodsId) { + //设置查询查询商品 + evaluationQueryParams.setGoodsId(goodsId); + return ResultUtil.data(memberEvaluationService.queryByParams(evaluationQueryParams)); + } + + @ApiOperation(value = "查看某一个商品的评价数量") + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, dataType = "Long", paramType = "path") + @GetMapping(value = "/{goodsId}/evaluationNumber") + public ResultMessage queryEvaluationNumber(@NotNull @PathVariable("goodsId") String goodsId) { + return ResultUtil.data(memberEvaluationService.getEvaluationNumber(goodsId)); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/MemberMessageBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/MemberMessageBuyerController.java new file mode 100644 index 00000000..58b5c15f --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/MemberMessageBuyerController.java @@ -0,0 +1,56 @@ +package cn.lili.controller.member; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.message.entity.enums.MessageStatusEnum; +import cn.lili.modules.member.entity.dos.MemberMessage; +import cn.lili.modules.member.entity.vo.MemberMessageQueryVO; +import cn.lili.modules.member.service.MemberMessageService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 买家端,会员站内消息接口 + * + * @author Bulbasaur + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "买家端,会员站内消息接口") +@RequestMapping("/buyer/member/message") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberMessageBuyerController { + + /** + * 会员站内消息 + */ + private final MemberMessageService memberMessageService; + + @ApiOperation(value = "分页获取会员站内消息") + @GetMapping + public ResultMessage> page(MemberMessageQueryVO memberMessageQueryVO, PageVO page) { + return ResultUtil.data(memberMessageService.getPage(memberMessageQueryVO, page)); + } + + @ApiOperation(value = "消息已读") + @ApiImplicitParam(name = "messageId", value = "会员消息id", required = true, paramType = "path") + @PutMapping + public ResultMessage read(@PathVariable String messageId) { + return ResultUtil.data(memberMessageService.editStatus(MessageStatusEnum.ALREADY_READY.name(), messageId)); + } + + @ApiOperation(value = "消息删除") + @ApiImplicitParam(name = "messageId", value = "会员消息id", required = true, paramType = "path") + @DeleteMapping + public ResultMessage deleteMessage(@PathVariable String messageId) { + return ResultUtil.data(memberMessageService.deleteMessage(messageId)); + } + + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/MemberReceiptController.java b/buyer-api/src/main/java/cn/lili/controller/member/MemberReceiptController.java new file mode 100644 index 00000000..6894b899 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/MemberReceiptController.java @@ -0,0 +1,60 @@ +package cn.lili.controller.member; + + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.vo.MemberReceiptAddVO; +import cn.lili.modules.member.entity.vo.MemberReceiptVO; +import cn.lili.modules.member.service.MemberReceiptService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +/** + * 买家端,会员发票接口 + * + * @author paulG + * @date: 2021-03-29 14:10:16 + */ +@RestController +@Api(tags = "买家端,会员发票接口") +@RequestMapping("/buyer/member/receipt") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberReceiptController { + + private final MemberReceiptService memberReceiptService; + + @ApiOperation(value = "查询会员发票列表") + @GetMapping + public ResultMessage page(MemberReceiptVO memberReceiptVO, PageVO page) { + return ResultUtil.data(memberReceiptService.getPage(memberReceiptVO, page)); + } + + @ApiOperation(value = "新增会员发票") + @PostMapping + public ResultMessage add(MemberReceiptAddVO memberReceiptAddVO) { + return ResultUtil.data(memberReceiptService.addMemberReceipt(memberReceiptAddVO, UserContext.getCurrentUser().getId())); + } + + @ApiOperation(value = "修改会员发票") + @ApiImplicitParam(name = "id", value = "会员发票id", required = true, paramType = "path") + @PutMapping + public ResultMessage update(@PathVariable String id, MemberReceiptAddVO memberReceiptAddVO) { + memberReceiptAddVO.setId(id); + return ResultUtil.data(memberReceiptService.editMemberReceipt(memberReceiptAddVO, id)); + } + + @ApiOperation(value = "会员发票删除") + @ApiImplicitParam(name = "id", value = "会员发票id", required = true, paramType = "path") + @DeleteMapping + public ResultMessage deleteMessage(@PathVariable String id) { + return ResultUtil.data(memberReceiptService.deleteMemberReceipt(id)); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/MemberSignBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/MemberSignBuyerController.java new file mode 100644 index 00000000..f6c091ae --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/MemberSignBuyerController.java @@ -0,0 +1,44 @@ +package cn.lili.controller.member; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.MemberSign; +import cn.lili.modules.member.service.MemberSignService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 会员签到控制器 + * + * @author pikachu + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "买家端,会员签到API") +@RequestMapping("/buyer/members/sign") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberSignBuyerController { + + private final MemberSignService memberSignService; + + @PostMapping + @ApiOperation(value = "会员签到") + public ResultMessage memberSign() { + return ResultUtil.data(memberSignService.memberSign()); + } + + @GetMapping + @ApiOperation(value = "根据时间查询会员签到表,类型是YYYYmm") + public ResultMessage> memberSign(String time) { + return ResultUtil.data(memberSignService.getMonthSignDay(time)); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/MemberWalletBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/MemberWalletBuyerController.java new file mode 100644 index 00000000..10c21c7b --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/MemberWalletBuyerController.java @@ -0,0 +1,130 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.verification.enums.VerificationEnums; +import cn.lili.common.verification.service.VerificationService; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberWallet; +import cn.lili.modules.member.entity.vo.MemberWalletVO; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.member.service.MemberWalletService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.Pattern; + +/** + * 买家端,会员余额接口 + * + * @author pikachu + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "买家端,会员余额接口") +@RequestMapping("/buyer/members/wallet") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberWalletBuyerController { + + /** + * 会员 + */ + private final MemberService memberService; + /** + * 会员余额 + */ + private final MemberWalletService memberWalletService; + /** + * 验证码 + */ + private final VerificationService verificationService; + + @GetMapping + @ApiOperation(value = "查询会员预存款余额") + public ResultMessage get() { + AuthUser authUser = UserContext.getCurrentUser(); + if (authUser != null) { + return ResultUtil.data(memberWalletService.getMemberWallet(authUser.getId())); + } + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + + @PostMapping(value = "/set-password") + @ApiOperation(value = "设置支付密码") + @ApiImplicitParams({ + @ApiImplicitParam(name = "password", value = "支付密码", required = true, dataType = "String", paramType = "query") + }) + public ResultMessage setPassword(String password, @RequestHeader String uuid) { + AuthUser authUser = UserContext.getCurrentUser(); + //校验当前用户是否存在 + Member member = memberService.getById(authUser.getId()); + if (member == null) { + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + //校验验证码 + if (verificationService.check(uuid, VerificationEnums.WALLET_PASSWORD)) { + memberWalletService.setMemberWalletPassword(member, password); + return ResultUtil.error(ResultCode.SUCCESS); + } else { + return ResultUtil.error(ResultCode.VERIFICATION_ERROR); + } + + } + + @PostMapping(value = "/update-password/ordinary") + @ApiOperation(value = "普通方式进行支付密码的修改") + @ApiImplicitParams({ + @ApiImplicitParam(name = "oldPassword", value = "旧支付密码", required = true, dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "newPassword", value = "新支付密码", required = true, dataType = "String", paramType = "query") + }) + public ResultMessage updatePassword(@RequestParam @Pattern(regexp = "[a-fA-F0-9]{32}", message = "旧密码格式不正确") String oldPassword, + @RequestParam @Pattern(regexp = "[a-fA-F0-9]{32}", message = "新密码格式不正确") String newPassword) { + AuthUser authUser = UserContext.getCurrentUser(); + //校验当前用户是否存在 + Member member = memberService.getById(authUser.getId()); + if (member == null) { + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + MemberWallet memberWallet = this.memberWalletService.getOne(new QueryWrapper().eq("member_id", member.getId())); + //校验新旧密码是否一致 + if (memberWallet != null) { + if (!new BCryptPasswordEncoder().matches(oldPassword, memberWallet.getWalletPassword())) { + throw new ServiceException(ResultCode.USER_OLD_PASSWORD_ERROR); + } + this.memberWalletService.setMemberWalletPassword(member, newPassword); + return ResultUtil.data("修改成功"); + } + throw new ServiceException(ResultCode.ERROR); + } + + + @GetMapping(value = "/check") + @ApiOperation(value = "检测会员是否设置过支付密码,会员中心设置或者修改密码时使用") + public Boolean checkPassword() { + return memberWalletService.checkPassword(); + } + + + @PostMapping(value = "/withdrawal") + @ApiOperation(value = "会员中心余额提现") + @ApiImplicitParams({ + @ApiImplicitParam(name = "price", value = "提现金额", required = true, dataType = "double", paramType = "query") + }) + public ResultMessage withdrawal(@Max(value = 1000, message = "充值金额单次最多允许提现1000元") @Min(value = 1, message = "充值金额单次最少提现金额为1元") Double price) { + return ResultUtil.data(memberWalletService.applyWithdrawal(price)); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/MemberWithdrawApplyBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/MemberWithdrawApplyBuyerController.java new file mode 100644 index 00000000..6ed9adec --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/MemberWithdrawApplyBuyerController.java @@ -0,0 +1,45 @@ +package cn.lili.controller.member; + + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.MemberWithdrawApply; +import cn.lili.modules.member.entity.vo.MemberWithdrawApplyQueryVO; +import cn.lili.modules.member.service.MemberWithdrawApplyService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + + +/** + * 买家端,余额提现记录接口 + * + * @author pikachu + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "买家端,余额提现记录接口") +@RequestMapping("/buyer/member/withdrawApply") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberWithdrawApplyBuyerController { + + private final MemberWithdrawApplyService memberWithdrawApplyService; + + + @ApiOperation(value = "分页获取提现记录") + @GetMapping + public ResultMessage> getByPage(PageVO page, MemberWithdrawApplyQueryVO memberWithdrawApplyQueryVO) { + memberWithdrawApplyQueryVO.setMemberId(UserContext.getCurrentUser().getId()); + //构建查询 返回数据 + IPage memberWithdrawApplyIPage = memberWithdrawApplyService.getMemberWithdrawPage(page, memberWithdrawApplyQueryVO); + return ResultUtil.data(memberWithdrawApplyIPage); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/PointsHistoryBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/PointsHistoryBuyerController.java new file mode 100644 index 00000000..505407b8 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/PointsHistoryBuyerController.java @@ -0,0 +1,51 @@ +package cn.lili.controller.member; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.MemberPointsHistory; +import cn.lili.modules.member.entity.vo.MemberPointsHistoryVO; +import cn.lili.modules.member.service.MemberPointsHistoryService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +/** + * 买家端,会员积分历史接口 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "买家端,会员积分历史接口") +@RequestMapping("/buyer/member/memberPointsHistory") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PointsHistoryBuyerController { + + private final MemberPointsHistoryService memberPointsHistoryService; + + @ApiOperation(value = "分页获取") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(PageVO page) { + + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(MemberPointsHistory::getMemberId, UserContext.getCurrentUser().getId()); + + return ResultUtil.data(memberPointsHistoryService.page(PageUtil.initPage(page), queryWrapper)); + } + + @ApiOperation(value = "获取会员积分VO") + @GetMapping(value = "/getMemberPointsHistoryVO") + public ResultMessage getMemberPointsHistoryVO() { + return ResultUtil.data(memberPointsHistoryService.getMemberPointsHistoryVO(UserContext.getCurrentUser().getId())); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/RechargeBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/RechargeBuyerController.java new file mode 100644 index 00000000..dd7987ae --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/RechargeBuyerController.java @@ -0,0 +1,46 @@ +package cn.lili.controller.member; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.trade.entity.dos.Recharge; +import cn.lili.modules.order.trade.entity.vo.RechargeQueryVO; +import cn.lili.modules.order.trade.service.RechargeService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 买家端,预存款充值记录接口 + * + * @author pikachu + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "买家端,预存款充值记录接口") +@RequestMapping("/buyer/member/recharge") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RechargeBuyerController { + + @Autowired + private RechargeService rechargeService; + + @ApiOperation(value = "分页获取预存款充值记录") + @GetMapping + public ResultMessage> getByPage(PageVO page) { + //构建查询参数 + RechargeQueryVO rechargeQueryVO = new RechargeQueryVO(); + rechargeQueryVO.setMemberId(UserContext.getCurrentUser().getId()); + //构建查询 返回数据 + IPage rechargePage = rechargeService.rechargePage(page, rechargeQueryVO); + return ResultUtil.data(rechargePage); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/member/ServiceNoticeBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/member/ServiceNoticeBuyerController.java new file mode 100644 index 00000000..66b7c54a --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/member/ServiceNoticeBuyerController.java @@ -0,0 +1,58 @@ +package cn.lili.controller.member; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.system.entity.dos.ServiceNotice; +import cn.lili.modules.system.service.ServiceNoticeService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * 买家端,会员站服务消息接口 + * + * @author Chopper + * @date: 2020/11/17 2:31 下午 + */ +@RestController +@RequestMapping("/service/notice") +@Api(tags = "买家端,会员站服务消息接口") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ServiceNoticeBuyerController { + + /** + * 服务消息 + */ + private final ServiceNoticeService serviceNoticeService; + + @ApiOperation(value = "获取消息详情") + @ApiImplicitParam(name = "id", value = "商品ID", required = true, dataType = "Long", paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage get(@PathVariable String id) { + ServiceNotice serviceNotice = serviceNoticeService.getById(id); + return ResultUtil.data(serviceNotice); + } + + @ApiOperation(value = "分页获取服务消息") + @GetMapping + @ApiImplicitParam(name = "storeId", value = "商家id,默认为-1,代表平台消息,如果查询某商家发布的消息,传递商家id即可", dataType = "int", paramType = "query") + public ResultMessage> getByPage(PageVO page, String storeId) { + ServiceNotice serviceNotice = new ServiceNotice(); + if (storeId == null) { + storeId = "-1"; + } + serviceNotice.setStoreId(storeId); + IPage data = serviceNoticeService.page(PageUtil.initPage(page)); + return ResultUtil.data(data); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/other/ArticleBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/other/ArticleBuyerController.java new file mode 100644 index 00000000..d3591e59 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/other/ArticleBuyerController.java @@ -0,0 +1,72 @@ +package cn.lili.controller.other; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dos.Article; +import cn.lili.modules.page.entity.dto.ArticleSearchParams; +import cn.lili.modules.page.entity.vos.ArticleCategoryVO; +import cn.lili.modules.page.entity.vos.ArticleVO; +import cn.lili.modules.page.service.ArticleCategoryService; +import cn.lili.modules.page.service.ArticleService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 买家端,文章接口 + * + * @author Chopper + * @date: 2020/11/16 10:02 下午 + */ +@RestController +@Api(tags = "买家端,文章接口") +@RequestMapping("/buyer/article") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ArticleBuyerController { + + /** + * 文章 + */ + private final ArticleService articleService; + + /** + * 文章分类 + */ + private final ArticleCategoryService articleCategoryService; + + @ApiOperation(value = "获取文章分类列表") + @GetMapping(value = "/articleCategory/list") + public ResultMessage> getArticleCategoryList() { + return ResultUtil.data(articleCategoryService.allChildren()); + } + + @ApiOperation(value = "分页获取") + @GetMapping + public ResultMessage> getByPage(ArticleSearchParams articleSearchParams) { + return ResultUtil.data(articleService.articlePage(articleSearchParams)); + } + + @ApiOperation(value = "通过id获取文章") + @ApiImplicitParam(name = "id", value = "文章ID", required = true, paramType = "path") + @GetMapping(value = "/get/{id}") + public ResultMessage
get(@NotNull(message = "文章ID") @PathVariable("id") String id) { + return ResultUtil.data(articleService.customGet(id)); + } + + @ApiOperation(value = "通过类型获取文章") + @ApiImplicitParam(name = "type", value = "文章类型", required = true, paramType = "path") + @GetMapping(value = "/type/{type}") + public ResultMessage
getByType(@NotNull(message = "文章类型") @PathVariable("type") String type) { + return ResultUtil.data(articleService.customGetByType(type)); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/other/FeedbackBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/other/FeedbackBuyerController.java new file mode 100644 index 00000000..9d1243ba --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/other/FeedbackBuyerController.java @@ -0,0 +1,44 @@ +package cn.lili.controller.other; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dos.Feedback; +import cn.lili.modules.page.service.FeedbackService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 买家端,意见反馈接口 + * + * @author Bulbasaur + * @date 2020-05-5 15:10:16 + */ +@RestController +@Api(tags = "买家端,意见反馈接口") +@RequestMapping("/buyer/feedback") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FeedbackBuyerController { + + /** + * 意见反馈 + */ + private final FeedbackService feedbackService; + + @ApiOperation(value = "添加意见反馈") + @PostMapping() + public ResultMessage save(Feedback feedback) { + feedback.setUserName(UserContext.getCurrentUser().getNickName()); + if (feedbackService.save(feedback)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/other/LogisticsBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/other/LogisticsBuyerController.java new file mode 100644 index 00000000..2e1c3010 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/other/LogisticsBuyerController.java @@ -0,0 +1,38 @@ +package cn.lili.controller.other; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.service.LogisticsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 买家端,物流公司接口 + * + * @author Bulbasaur + * @date 2020-05-5 15:10:16 + */ +@RestController +@Api(tags = "买家端,物流公司接口") +@RequestMapping("/buyer/logistics") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class LogisticsBuyerController { + + private final LogisticsService logisticsService; + + + @ApiOperation(value = "分页获取物流公司") + @GetMapping + public ResultMessage> getByPage() { + return ResultUtil.data(logisticsService.getOpenLogistics()); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/other/PageBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/other/PageBuyerController.java new file mode 100644 index 00000000..0e424d96 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/other/PageBuyerController.java @@ -0,0 +1,48 @@ +package cn.lili.controller.other; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dto.PageDataDTO; +import cn.lili.modules.page.entity.enums.PageEnum; +import cn.lili.modules.page.entity.vos.PageDataVO; +import cn.lili.modules.page.service.PageDataService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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; + +/** + * 买家端,页面接口 + * + * @author Chopper + * @date 2020/11/16 10:08 下午 + */ +@RestController +@Api(tags = "买家端,页面接口") +@RequestMapping("/buyer/pageData") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PageBuyerController { + + /** + * 页面管理 + */ + private final PageDataService pageService; + + @ApiOperation(value = "获取首页数据") + @GetMapping("/getIndex") + public ResultMessage getIndex(@RequestParam String clientType) { + PageDataDTO pageDataDTO = new PageDataDTO(PageEnum.INDEX.name()); + pageDataDTO.setPageClientType(clientType); + return ResultUtil.data(pageService.getPageData(pageDataDTO)); + } + + @ApiOperation(value = "获取页面数据") + @GetMapping + public ResultMessage get(PageDataDTO pageDataDTO) { + return ResultUtil.data(pageService.getPageData(pageDataDTO)); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/passport/MemberBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/passport/MemberBuyerController.java new file mode 100644 index 00000000..ecee45fa --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/passport/MemberBuyerController.java @@ -0,0 +1,160 @@ +package cn.lili.controller.passport; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.sms.SmsUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.verification.enums.VerificationEnums; +import cn.lili.common.verification.service.VerificationService; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dto.MemberEditDTO; +import cn.lili.modules.member.service.MemberService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 买家端,会员接口 + * + * @author Chopper + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "买家端,会员接口") +@RequestMapping("/buyer/members") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberBuyerController { + + /** + * 会员 + */ + private final MemberService memberService; + + private final SmsUtil smsUtil; + + private final VerificationService verificationService; + + + @ApiOperation(value = "登录接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query"), + @ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query") + }) + @PostMapping("/userLogin") + public ResultMessage userLogin(@NotNull(message = "用户名不能为空") @RequestParam String username, + @NotNull(message = "密码不能为空") @RequestParam String password, + @RequestHeader String uuid) { + if (verificationService.check(uuid, VerificationEnums.LOGIN)) { + return ResultUtil.data(this.memberService.usernameLogin(username, password)); + } else { + return ResultUtil.error(ResultCode.VERIFICATION_ERROR); + } + } + + @ApiOperation(value = "短信登录接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "mobile", value = "手机号", required = true, paramType = "query"), + @ApiImplicitParam(name = "code", value = "验证码", required = true, paramType = "query") + }) + @PostMapping("/smsLogin") + public ResultMessage smsLogin(@NotNull(message = "手机号为空") @RequestParam String mobile, + @NotNull(message = "验证码为空") @RequestParam String code, + @RequestHeader String uuid) { +// if(smsUtil.verifyCode(mobile,VerificationEnums.LOGIN,uuid,code)){ + return ResultUtil.data(memberService.mobilePhoneLogin(mobile)); +// }else { +// return ResultUtil.error("验证码错误"); +// } + } + + @ApiOperation(value = "注册用户") + @ApiImplicitParams({ + @ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query"), + @ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query"), + @ApiImplicitParam(name = "mobilePhone", value = "手机号", required = true, paramType = "query"), + @ApiImplicitParam(name = "code", value = "验证码", required = true, paramType = "query") + }) + @PostMapping("/register") + public ResultMessage register(@NotNull(message = "用户名不能为空") @RequestParam String username, + @NotNull(message = "密码不能为空") @RequestParam String password, + @NotNull(message = "手机号为空") @RequestParam String mobilePhone, + @RequestHeader String uuid, + @NotNull(message = "验证码不能为空") @RequestParam String code) { + + boolean result = smsUtil.verifyCode(mobilePhone, VerificationEnums.REGISTER, uuid, code); + if (result) { + return ResultUtil.data(memberService.register(username, password, mobilePhone)); + } else { + return ResultUtil.error(ResultCode.VERIFICATION_SMS_ERROR); + } + } + + @ApiOperation(value = "获取当前登录用户接口") + @GetMapping + public ResultMessage getUserInfo() { + + return ResultUtil.data(memberService.getUserInfo()); + } + + @ApiOperation(value = "通过短信重置密码") + @ApiImplicitParams({ + @ApiImplicitParam(name = "mobile", value = "手机号", required = true, paramType = "query"), + @ApiImplicitParam(name = "password", value = "是否保存登录", required = true, paramType = "query") + }) + @PostMapping("/resetByMobile") + public ResultMessage resetByMobile(@NotNull(message = "手机号为空") @RequestParam String mobile, + @NotNull(message = "验证码为空") @RequestParam String code, + @RequestHeader String uuid) { + //校验短信验证码是否正确 + if (smsUtil.verifyCode(mobile, VerificationEnums.FIND_USER, uuid, code)) { + //校验是否通过手机号可获取会员,存在则将会员信息存入缓存,有效时间3分钟 + if (memberService.findByMobile(uuid, mobile)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + } + return ResultUtil.error(ResultCode.VERIFICATION_ERROR); + } + + @ApiOperation(value = "修改密码") + @ApiImplicitParams({ + @ApiImplicitParam(name = "mobile", value = "手机号", required = true, paramType = "query"), + @ApiImplicitParam(name = "password", value = "是否保存登录", required = true, paramType = "query") + }) + @PostMapping("/resetPassword") + public ResultMessage resetByMobile(@NotNull(message = "密码为空") @RequestParam String password, @RequestHeader String uuid) { + + return ResultUtil.data(memberService.resetByMobile(uuid, password)); + } + + @ApiOperation(value = "修改用户自己资料") + @PutMapping("/editOwn") + public ResultMessage editOwn(MemberEditDTO memberEditDTO) { + + return ResultUtil.data(memberService.editOwn(memberEditDTO)); + } + + @ApiOperation(value = "修改密码") + @ApiImplicitParams({ + @ApiImplicitParam(name = "password", value = "旧密码", required = true, paramType = "query"), + @ApiImplicitParam(name = "newPassword", value = "新密码", required = true, paramType = "query") + }) + @PutMapping("/modifyPass") + public ResultMessage modifyPass(@NotNull(message = "旧密码不能为空") @RequestParam String password, + @NotNull(message = "新密码不能为空") @RequestParam String newPassword) { + return ResultUtil.data(memberService.modifyPass(password, newPassword)); + } + + + @ApiOperation(value = "刷新token") + @GetMapping("/refresh/{refreshToken}") + public ResultMessage refreshToken(@NotNull(message = "刷新token不能为空") @PathVariable String refreshToken) { + return ResultUtil.data(this.memberService.refreshToken(refreshToken)); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/passport/connect/ConnectBuyerBindController.java b/buyer-api/src/main/java/cn/lili/controller/passport/connect/ConnectBuyerBindController.java new file mode 100644 index 00000000..4e357676 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/passport/connect/ConnectBuyerBindController.java @@ -0,0 +1,56 @@ +package cn.lili.controller.passport.connect; + + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.connect.service.ConnectService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 买家端,app/小程序 联合登录 + * + * @author Chopper + * @date 2020-11-25 19:29 + */ +@RestController +@Api(tags = "买家端,app/小程序 联合登录") +@RequestMapping("/buyer/connect/bind") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ConnectBuyerBindController { + + private final ConnectService connectService; + + @ApiOperation(value = "unionID 绑定") + @ApiImplicitParams({ + @ApiImplicitParam(name = "unionID", value = "unionID", required = true, paramType = "query"), + @ApiImplicitParam(name = "type", value = "type", required = true, paramType = "query") + }) + @PostMapping + public void unionIDBind(@RequestParam String unionID, @RequestParam String type) { + connectService.bind(unionID, type); + } + + @ApiOperation(value = "unionID 解绑") + @ApiImplicitParam(name = "type", value = "type", required = true, paramType = "query") + @PostMapping("/unbind") + public void unionIDBind(@RequestParam String type) { + connectService.unbind(type); + } + + + @GetMapping("/list") + @ApiOperation(value = "绑定列表") + public ResultMessage> bindList() { + return ResultUtil.data(connectService.bindList()); + } + + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/passport/connect/ConnectBuyerWebController.java b/buyer-api/src/main/java/cn/lili/controller/passport/connect/ConnectBuyerWebController.java new file mode 100644 index 00000000..99588b3b --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/passport/connect/ConnectBuyerWebController.java @@ -0,0 +1,98 @@ +package cn.lili.controller.passport.connect; + + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.token.Token; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.request.AuthRequest; +import cn.lili.modules.connect.service.ConnectService; +import cn.lili.modules.connect.util.ConnectUtil; +import cn.lili.modules.connect.util.UuidUtils; +import cn.lili.modules.member.service.MemberService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 买家端,web联合登录 + * + * @author Chopper + * @date 2020-11-25 19:29 + */ +@RestController +@Api(tags = "买家端,web联合登录") +@RequestMapping("/buyer/connect") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ConnectBuyerWebController { + + private final ConnectService connectService; + + private final MemberService memberService; + + private final ConnectUtil connectUtil; + + + @GetMapping("/login/web/{type}") + @ApiOperation(value = "WEB信任登录授权") + @ApiImplicitParams({ + @ApiImplicitParam(name = "type", value = "登录方式:QQ,微信,微信_PC", + allowableValues = "QQ,WECHAT,WECHAT_PC", paramType = "path") + }) + public ResultMessage webAuthorize(@PathVariable String type, HttpServletResponse response) throws IOException { + AuthRequest authRequest = connectUtil.getAuthRequest(type); + String authorizeUrl = authRequest.authorize(UuidUtils.getUUID()); + response.sendRedirect(authorizeUrl); + return ResultUtil.data(authorizeUrl); + } + + + @ApiOperation(value = "信任登录统一回调地址", hidden = true) + @GetMapping("/callback/{type}") + public void callBack(@PathVariable String type, AuthCallback callback, HttpServletResponse httpServletResponse) throws IOException { + connectUtil.callback(type, callback, httpServletResponse); + } + + @ApiOperation(value = "信任登录响应结果获取") + @GetMapping("/result") + public ResultMessage callBackResult(String state) { + if (state == null) { + return ResultUtil.error(ResultCode.USER_CONNECT_LOGIN_ERROR); + } + return connectUtil.getResult(state); + } + + @GetMapping("/register/auto") + @ApiOperation(value = "WEB信任登录授权") + public ResultMessage webAuthorize() { + Token token = memberService.autoRegister(); + return ResultUtil.data(token); + } + + @ApiOperation(value = "unionID登录") + @ApiImplicitParams({ + @ApiImplicitParam(name = "openId", value = "openid", required = true, paramType = "query"), + @ApiImplicitParam(name = "type", value = "联合类型", required = true, + allowableValues = "WECHAT,QQ,ALIPAY,WEIBO,APPLE", paramType = "query"), + @ApiImplicitParam(name = "uniAccessToken", value = "联合登陆返回的accessToken", required = true, paramType = "query") + }) + @GetMapping("/app/login") + public ResultMessage unionIDLogin(ConnectAuthUser authUser, @RequestHeader("uuid") String uuid) { + try { + return ResultUtil.data(connectService.appLoginCallback(authUser, uuid)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/passport/connect/MiniProgramBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/passport/connect/MiniProgramBuyerController.java new file mode 100644 index 00000000..2feadb82 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/passport/connect/MiniProgramBuyerController.java @@ -0,0 +1,72 @@ +package cn.lili.controller.passport.connect; + +import cn.lili.common.token.Token; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.connect.entity.dto.WechatMPLoginParams; +import cn.lili.modules.connect.service.ConnectService; +import cn.lili.modules.message.entity.dos.WechatMPMessage; +import cn.lili.modules.message.service.ShortLinkService; +import cn.lili.modules.message.service.WechatMPMessageService; +import cn.lili.modules.message.util.WechatMpCodeUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 买家端,小程序登录接口 + * + * @author Chopper + * @date 2021/2/19 09:28 + */ +@RestController +@RequestMapping("/buyer/mini-program") +@Api(tags = "买家端,小程序登录接口") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MiniProgramBuyerController { + + public final ConnectService connectService; + public final WechatMpCodeUtil wechatMpCodeUtil; + + public final WechatMPMessageService wechatMPMessageService; + public final ShortLinkService shortLinkService; + + @GetMapping("/auto-login") + @ApiOperation(value = "小程序自动登录") + public ResultMessage autoLogin(@RequestHeader String uuid, WechatMPLoginParams params) { + params.setUuid(uuid); + return ResultUtil.data(this.connectService.miniProgramAutoLogin(params)); + } + + @GetMapping("/subscribeMessage") + @ApiOperation(value = "消息订阅") + public ResultMessage> autoLogin() { + return ResultUtil.data(wechatMPMessageService.list()); + } + + @GetMapping("/mp/unlimited") + @ApiOperation(value = "小程序二维码生成:不限制数量,但是限制长度,只能存放32为长度") + public ResultMessage unlimited(String page, String scene) { + return ResultUtil.data(wechatMpCodeUtil.createCode(page, scene)); + } + + @GetMapping("/mp/qrcode") + @ApiOperation(value = "小程序二维码生成:只适用于少量场景,多数场景需要unlimited接口实现") + public ResultMessage qrcode(String page) { + return ResultUtil.data(wechatMpCodeUtil.createQrCode(page)); + } + + @GetMapping("/mp/unlimited/scene") + @ApiOperation(value = "根据shortlink 获取页面参数") + public ResultMessage getScene(String id) { + return ResultUtil.data(shortLinkService.getById(id).getOriginalParams()); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/payment/CashierController.java b/buyer-api/src/main/java/cn/lili/controller/payment/CashierController.java new file mode 100644 index 00000000..683b2c82 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/payment/CashierController.java @@ -0,0 +1,104 @@ +package cn.lili.controller.payment; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.payment.kit.CashierSupport; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.enums.PaymentClientEnum; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.payment.service.PaymentService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 买家端,收银台接口 + * + * @author Chopper + * @date 2020-12-18 16:59 + */ +@RestController +@Api(tags = "买家端,收银台接口") +@RequestMapping("/buyer/cashier") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CashierController { + + + private final CashierSupport cashierSupport; + + private final PaymentService paymentService; + + + @ApiImplicitParams({ + @ApiImplicitParam(name = "client", value = "客户端类型", paramType = "path", allowableValues = "PC,H5,WECHAT_MP,APP") + }) + @GetMapping(value = "/tradeDetail") + @ApiOperation(value = "获取支付详情") + public ResultMessage paymentParams(@Validated PayParam payParam) { + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + return ResultUtil.data(cashierParam); + } + + + @ApiImplicitParams({ + @ApiImplicitParam(name = "paymentMethod", value = "支付方式", paramType = "path", allowableValues = "WECHAT,ALIPAY"), + @ApiImplicitParam(name = "paymentClient", value = "调起方式", paramType = "path", allowableValues = "APP,NATIVE,JSAPI,H5,MP") + }) + @GetMapping(value = "/pay/{paymentMethod}/{paymentClient}") + @ApiOperation(value = "支付") + public ResultMessage payment( + HttpServletRequest request, + HttpServletResponse response, + @PathVariable String paymentMethod, + @PathVariable String paymentClient, + @Validated PayParam payParam) { + PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(paymentMethod); + PaymentClientEnum paymentClientEnum = PaymentClientEnum.valueOf(paymentClient); + + try { + return cashierSupport.payment(paymentMethodEnum, paymentClientEnum, request, response, payParam); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + + + } + + @ApiOperation(value = "支付回调") + @RequestMapping(value = "/callback/{paymentMethod}", method = {RequestMethod.GET, RequestMethod.POST}) + public ResultMessage callback(HttpServletRequest request, @PathVariable String paymentMethod) { + + PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(paymentMethod); + + cashierSupport.callback(paymentMethodEnum, request); + + return ResultUtil.success(ResultCode.PAY_SUCCESS); + } + + @ApiOperation(value = "支付异步通知") + @RequestMapping(value = "/notify/{paymentMethod}", method = {RequestMethod.GET, RequestMethod.POST}) + public void notify(HttpServletRequest request, @PathVariable String paymentMethod) { + + PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(paymentMethod); + + cashierSupport.notify(paymentMethodEnum, request); + + } + + @ApiOperation(value = "查询支付结果") + @GetMapping(value = "/result") + public ResultMessage paymentResult(PayParam payParam) { + return ResultUtil.data(cashierSupport.paymentResult(payParam)); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/payment/CashierRefundController.java b/buyer-api/src/main/java/cn/lili/controller/payment/CashierRefundController.java new file mode 100644 index 00000000..3dbb41ff --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/payment/CashierRefundController.java @@ -0,0 +1,38 @@ +package cn.lili.controller.payment; + +import cn.lili.modules.payment.kit.RefundSupport; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + * 买家端,退款回调 + * + * @author Chopper + * @date 2020-12-18 16:59 + */ +@Api(tags = "买家端,退款回调") +@RestController +@RequestMapping("/buyer/cashier/refund") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CashierRefundController { + + + private final RefundSupport refundSupport; + + + @ApiOperation(value = "退款通知") + @RequestMapping(value = "/notify/{paymentMethod}", method = {RequestMethod.GET, RequestMethod.POST}) + public void notify(HttpServletRequest request, @PathVariable String paymentMethod) { + PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(paymentMethod); + refundSupport.notify(paymentMethodEnum, request); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/promotion/PintuanBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/promotion/PintuanBuyerController.java new file mode 100644 index 00000000..9d8fb447 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/promotion/PintuanBuyerController.java @@ -0,0 +1,68 @@ +package cn.lili.controller.promotion; + +import cn.hutool.core.date.DateUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dto.PromotionGoodsDTO; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.PintuanMemberVO; +import cn.lili.modules.promotion.entity.vos.PintuanShareVO; +import cn.lili.modules.promotion.entity.vos.PromotionGoodsSearchParams; +import cn.lili.modules.promotion.service.PintuanService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 买家端,拼团接口 + * + * @author paulG + * @date 2021/2/20 + **/ +@Api(tags = "买家端,拼团接口") +@RestController +@RequestMapping("/buyer/promotion/pintuan") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PintuanBuyerController { + + private final PromotionGoodsService promotionGoodsService; + + private final PintuanService pintuanService; + + @ApiOperation(value = "获取拼团商品") + @GetMapping + public ResultMessage> getPintuanCategory(String goodsName, String categoryPath, PageVO pageVo) { + PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams(); + searchParams.setGoodsName(goodsName); + searchParams.setPromotionType(PromotionTypeEnum.PINTUAN.name()); + searchParams.setPromotionStatus(PromotionStatusEnum.START.name()); + searchParams.setCategoryPath(categoryPath); + searchParams.setEndTime(DateUtil.date().getTime()); + return ResultUtil.data(promotionGoodsService.getPromotionGoods(searchParams, pageVo)); + } + + + @ApiOperation(value = "获取当前拼团活动的未成团的会员") + @GetMapping("/{pintuanId}/members") + public ResultMessage> getPintuanMember(@PathVariable String pintuanId) { + return ResultUtil.data(pintuanService.getPintuanMember(pintuanId)); + } + + @ApiOperation(value = "获取当前拼团订单的拼团分享信息") + @GetMapping("/share") + public ResultMessage share(String parentOrderSn, String skuId) { + return ResultUtil.data(pintuanService.getPintuanShareInfo(parentOrderSn, skuId)); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/promotion/PointsGoodsBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/promotion/PointsGoodsBuyerController.java new file mode 100644 index 00000000..f2ee1753 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/promotion/PointsGoodsBuyerController.java @@ -0,0 +1,49 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dos.PointsGoodsCategory; +import cn.lili.modules.promotion.entity.vos.PointsGoodsSearchParams; +import cn.lili.modules.promotion.entity.vos.PointsGoodsVO; +import cn.lili.modules.promotion.service.PointsGoodsCategoryService; +import cn.lili.modules.promotion.service.PointsGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +/** + * 买家端,积分商品接口 + * + * @author paulG + * @date 2021/1/19 + **/ +@RestController +@Api(tags = "买家端,积分商品接口") +@RequestMapping("/buyer/promotion/pointsGoods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PointsGoodsBuyerController { + + private final PointsGoodsService pointsGoodsService; + + private final PointsGoodsCategoryService pointsGoodsCategoryService; + + @GetMapping + @ApiOperation(value = "分页获取积分商品") + public ResultMessage> getPointsGoodsPage(PointsGoodsSearchParams searchParams, PageVO page) { + IPage pointsGoodsByPage = pointsGoodsService.getPointsGoodsByPage(searchParams, page); + return ResultUtil.data(pointsGoodsByPage); + } + + @GetMapping("/category") + @ApiOperation(value = "获取积分商品分类分页") + public ResultMessage> page(String name, PageVO page) { + return ResultUtil.data(pointsGoodsCategoryService.getCategoryByPage(name, page)); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/promotion/SeckillBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/promotion/SeckillBuyerController.java new file mode 100644 index 00000000..059c3514 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/promotion/SeckillBuyerController.java @@ -0,0 +1,49 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.vos.SeckillGoodsVO; +import cn.lili.modules.promotion.entity.vos.SeckillTimelineVO; +import cn.lili.modules.promotion.service.SeckillApplyService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + + +/** + * 买家端,限时抢购接口 + * + * @author paulG + * @date 2020/11/17 2:30 下午 + */ +@Api(tags = "买家端,限时抢购接口") +@RestController +@RequestMapping("/buyer/promotion/seckill") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SeckillBuyerController { + + /** + * 限时抢购 + */ + private final SeckillApplyService seckillApplyService; + + @ApiOperation(value = "获取当天限时抢购信息") + @GetMapping + public ResultMessage> getSeckillTime() { + return ResultUtil.data(seckillApplyService.getSeckillTimeline()); + } + + @ApiOperation(value = "获取某个时刻的限时抢购商品信息") + @GetMapping("/{timeline}") + public ResultMessage> getSeckillGoods(@PathVariable Integer timeline) { + return ResultUtil.data(seckillApplyService.getSeckillGoods(timeline)); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/purchase/PurchaseBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/purchase/PurchaseBuyerController.java new file mode 100644 index 00000000..05bea31a --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/purchase/PurchaseBuyerController.java @@ -0,0 +1,75 @@ +package cn.lili.controller.purchase; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.purchase.entity.dos.PurchaseOrder; +import cn.lili.modules.purchase.entity.params.PurchaseOrderSearchParams; +import cn.lili.modules.purchase.entity.vos.PurchaseOrderVO; +import cn.lili.modules.purchase.service.PurchaseOrderService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 买家端,采购接口 + * + * @author Chopper + * @date: 2020/11/16 10:06 下午 + */ +@Api(tags = "买家端,采购接口") +@RestController +@RequestMapping("/buyer/purchase") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PurchaseBuyerController { + + /** + * 采购单 + */ + private final PurchaseOrderService purchaseOrderService; + + @ApiOperation(value = "添加采购单") + @PostMapping + public ResultMessage addPurchaseOrderVO(PurchaseOrderVO purchaseOrderVO) { + return ResultUtil.data(purchaseOrderService.addPurchaseOrder(purchaseOrderVO)); + } + + @ApiOperation(value = "采购单分页") + @GetMapping + public ResultMessage> get(PurchaseOrderSearchParams purchaseOrderSearchParams) { + return ResultUtil.data(purchaseOrderService.page(purchaseOrderSearchParams)); + } + + @ApiOperation(value = "采购单详情") + @ApiImplicitParam(name = "id", value = "采购单ID", required = true, dataType = "Long", paramType = "path") + @GetMapping("/{id}") + public ResultMessage getPurchaseOrder(@NotNull @PathVariable String id) { + return ResultUtil.data(purchaseOrderService.getPurchaseOrder(id)); + } + + @ApiOperation(value = "会员采购单分页") + @GetMapping("/getByMember") + public ResultMessage> getByMember(PurchaseOrderSearchParams purchaseOrderSearchParams) { + purchaseOrderSearchParams.setMemberId(UserContext.getCurrentUser().getId()); + return ResultUtil.data(purchaseOrderService.page(purchaseOrderSearchParams)); + } + + @ApiOperation(value = "关闭采购单") + @ApiImplicitParam(name = "id", value = "采购单ID", required = true, dataType = "Long", paramType = "path") + @PutMapping("/{id}") + public ResultMessage close(@NotNull @PathVariable String id) { + + if (purchaseOrderService.close(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/purchase/PurchaseQuotedController.java b/buyer-api/src/main/java/cn/lili/controller/purchase/PurchaseQuotedController.java new file mode 100644 index 00000000..3642a66f --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/purchase/PurchaseQuotedController.java @@ -0,0 +1,56 @@ +package cn.lili.controller.purchase; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.purchase.entity.dos.PurchaseQuoted; +import cn.lili.modules.purchase.entity.vos.PurchaseQuotedVO; +import cn.lili.modules.purchase.service.PurchaseQuotedService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 买家端,采购报价接口 + * + * @author Bulbasaur + * @date: 2020/11/16 10:06 下午 + */ +@Api(tags = "买家端,采购报价接口") +@RestController +@RequestMapping("/buyer/purchaseQuoted") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PurchaseQuotedController { + + /** + * 采购单报价 + */ + private final PurchaseQuotedService purchaseQuotedService; + + @ApiOperation(value = "添加采购单报价") + @PostMapping + public ResultMessage addPurchaseOrderVO(PurchaseQuotedVO purchaseQuotedVO) { + return ResultUtil.data(purchaseQuotedService.addPurchaseQuoted(purchaseQuotedVO)); + } + + @ApiOperation(value = "报价列表") + @ApiImplicitParam(name = "purchaseOrderId", value = "报价单ID", required = true, dataType = "String", paramType = "path") + @GetMapping("/purchaseOrder/{purchaseOrderId}") + public ResultMessage> get(@NotNull @PathVariable String purchaseOrderId) { + return ResultUtil.data(purchaseQuotedService.getByPurchaseOrderId(purchaseOrderId)); + } + + @ApiOperation(value = "报价单详情") + @ApiImplicitParam(name = "id", value = "报价单ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "purchaseQuoted/{id}") + public ResultMessage getPurchaseQuoted(@NotNull @PathVariable String id) { + return ResultUtil.data(purchaseQuotedService.getById(id)); + } + + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/store/StoreBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/store/StoreBuyerController.java new file mode 100644 index 00000000..5289e4d5 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/store/StoreBuyerController.java @@ -0,0 +1,110 @@ +package cn.lili.controller.store; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.statistics.aop.PageViewPoint; +import cn.lili.modules.statistics.aop.enums.PageViewEnum; +import cn.lili.modules.store.entity.dto.StoreBankDTO; +import cn.lili.modules.store.entity.dto.StoreCompanyDTO; +import cn.lili.modules.store.entity.dto.StoreOtherInfoDTO; +import cn.lili.modules.store.entity.vos.*; +import cn.lili.modules.store.service.StoreDetailService; +import cn.lili.modules.store.service.StoreGoodsLabelService; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; +import java.util.List; + + +/** + * 买家端,店铺接口 + * + * @author Bulbasaur + * @date: 2020/11/17 2:32 下午 + */ +@RestController +@RequestMapping("/buyer/store") +@Api(tags = "买家端,店铺接口") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreBuyerController { + + /** + * 店铺 + */ + private final StoreService storeService; + /** + * 店铺商品分类 + */ + private final StoreGoodsLabelService storeGoodsLabelService; + /** + * 店铺详情 + */ + private final StoreDetailService storeDetailService; + + @ApiOperation(value = "获取店铺列表分页") + @GetMapping + public ResultMessage> getByPage(StoreSearchParams entity, PageVO page) { + return ResultUtil.data(storeService.findByConditionPage(entity, page)); + } + + @ApiOperation(value = "通过id获取店铺信息") + @ApiImplicitParam(name = "id", value = "店铺ID", required = true, paramType = "path") + @GetMapping(value = "/get/detail/{id}") + @PageViewPoint(type = PageViewEnum.STORE, id = "#id") + public ResultMessage detail(@NotNull @PathVariable String id) { + return ResultUtil.data(storeDetailService.getStoreBasicInfoDTO(id)); + } + + @ApiOperation(value = "通过id获取店铺商品分类") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "店铺ID", required = true, paramType = "path") + }) + @GetMapping(value = "/label/get/{id}") + public ResultMessage> storeGoodsLabel(@NotNull @PathVariable String id) { + return ResultUtil.data(storeGoodsLabelService.listByStoreId(id)); + } + + @ApiOperation(value = "申请店铺第一步-填写企业信息") + @PutMapping(value = "/apply/first") + public ResultMessage applyFirstStep(StoreCompanyDTO storeCompanyDTO) { + if (storeService.applyFirstStep(storeCompanyDTO)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "申请店铺第二步-填写银行信息") + @PutMapping(value = "/apply/second") + public ResultMessage applyFirstStep(StoreBankDTO storeBankDTO) { + if (storeService.applySecondStep(storeBankDTO)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "申请店铺第三步-填写其他信息") + @PutMapping(value = "/apply/third") + public ResultMessage applyFirstStep(StoreOtherInfoDTO storeOtherInfoDTO) { + if (storeService.applyThirdStep(storeOtherInfoDTO)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "获取当前登录会员的店铺信息-入驻店铺") + @GetMapping(value = "/apply") + public ResultMessage apply() { + return ResultUtil.data(storeDetailService.getStoreDetailVOByMemberId(UserContext.getCurrentUser().getId())); + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/trade/AfterSaleBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/trade/AfterSaleBuyerController.java new file mode 100644 index 00000000..6f913497 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/trade/AfterSaleBuyerController.java @@ -0,0 +1,133 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.dos.AfterSaleReason; +import cn.lili.modules.order.order.entity.dto.AfterSaleDTO; +import cn.lili.modules.order.order.entity.vo.AfterSaleApplyVO; +import cn.lili.modules.order.order.entity.vo.AfterSaleSearchParams; +import cn.lili.modules.order.order.entity.vo.AfterSaleVO; +import cn.lili.modules.order.order.service.AfterSaleReasonService; +import cn.lili.modules.order.order.service.AfterSaleService; +import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO; +import cn.lili.modules.order.trade.entity.dos.AfterSaleLog; +import cn.lili.modules.order.order.service.AfterSaleLogService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.List; + +/** + * 买家端,售后管理接口 + * + * @author Chopper + * @date 2020/11/16 10:02 下午 + */ +@RestController +@Api(tags = "买家端,售后管理接口") +@RequestMapping("/buyer/afterSale") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AfterSaleBuyerController { + + /** + * 售后 + */ + private final AfterSaleService afterSaleService; + + /** + * 售后原因 + */ + private final AfterSaleReasonService afterSaleReasonService; + + /** + * 售后日志 + */ + private final AfterSaleLogService afterSaleLogService; + + @ApiOperation(value = "查看售后服务详情") + @ApiImplicitParam(name = "sn", value = "售后单号", required = true, paramType = "path") + @GetMapping(value = "/get/{sn}") + public ResultMessage get(@NotNull(message = "售后单号") @PathVariable("sn") String sn) { + return ResultUtil.data(afterSaleService.getAfterSale(sn)); + } + + @ApiOperation(value = "分页获取售后服务") + @GetMapping(value = "/page") + public ResultMessage> getByPage(AfterSaleSearchParams searchParams) { + return ResultUtil.data(afterSaleService.getAfterSalePages(searchParams)); + } + + @ApiOperation(value = "获取申请售后页面信息") + @ApiImplicitParams({ + @ApiImplicitParam(name = "sn", value = "订单货物编号", required = true, dataType = "String", paramType = "path") + }) + @GetMapping(value = "/applyAfterSaleInfo/{sn}") + public ResultMessage applyAfterSaleInfo(@PathVariable String sn) { + return ResultUtil.data(afterSaleService.getAfterSaleDTO(sn)); + } + + @PostMapping(value = "/save/{orderItemSn}") + @ApiImplicitParam(name = "orderItemSn", value = "订单货物编号", required = true, paramType = "query") + @ApiOperation(value = "申请售后") + public ResultMessage save(AfterSaleDTO afterSaleDTO) { + return ResultUtil.data(afterSaleService.saveAfterSale(afterSaleDTO)); + + } + + @ApiOperation(value = "买家 退回 物流信息") + @ApiImplicitParams({ + @ApiImplicitParam(name = "afterSaleSn", value = "售后sn", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "logisticsNo", value = "发货单号", required = true, dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "logisticsId", value = "物流公司id", required = true, dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "mDeliverTime", value = "买家发货时间", required = true, dataType = "date", paramType = "query") + + }) + @PostMapping(value = "/delivery/{afterSaleSn}") + public ResultMessage delivery(@NotNull(message = "售后编号不能为空") @PathVariable("afterSaleSn") String afterSaleSn, + @NotNull(message = "发货单号不能为空") @RequestParam String logisticsNo, + @NotNull(message = "请选择物流公司") @RequestParam String logisticsId, + @NotNull(message = "请选择发货时间") @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date mDeliverTime) { + return ResultUtil.data(afterSaleService.buyerDelivery(afterSaleSn, logisticsNo, logisticsId, mDeliverTime)); + } + + @ApiOperation(value = "售后,取消售后") + @ApiImplicitParams({ + @ApiImplicitParam(name = "afterSaleSn", value = "售后sn", required = true, dataType = "String", paramType = "path") + }) + @PostMapping(value = "/cancel/{afterSaleSn}") + public ResultMessage cancel(@NotNull(message = "参数非法") @PathVariable("afterSaleSn") String afterSaleSn) { + return ResultUtil.data(afterSaleService.cancel(afterSaleSn)); + } + + @ApiOperation(value = "获取商家售后收件地址") + @ApiImplicitParam(name = "sn", value = "售后单号", required = true, paramType = "path") + @GetMapping(value = "/getStoreAfterSaleAddress/{sn}") + public ResultMessage getStoreAfterSaleAddress(@NotNull(message = "售后单号") @PathVariable("sn") String sn) { + return ResultUtil.data(afterSaleService.getStoreAfterSaleAddressDTO(sn)); + } + + @ApiOperation(value = "获取售后原因") + @ApiImplicitParam(name = "serviceType", value = "售后类型", required = true, paramType = "path", allowableValues = "CANCEL,RETURN_GOODS,RETURN_MONEY,COMPLAIN") + @GetMapping(value = "/get/afterSaleReason/{serviceType}") + public ResultMessage> getAfterSaleReason(@PathVariable String serviceType) { + return ResultUtil.data(afterSaleReasonService.afterSaleReasonList(serviceType)); + } + + @ApiOperation(value = "获取售后日志") + @ApiImplicitParam(name = "sn", value = "售后编号", required = true, paramType = "path") + @GetMapping(value = "/get/getAfterSaleLog/{sn}") + public ResultMessage> getAfterSaleLog(@PathVariable String sn) { + return ResultUtil.data(afterSaleLogService.getAfterSaleLog(sn)); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/trade/CartController.java b/buyer-api/src/main/java/cn/lili/controller/trade/CartController.java new file mode 100644 index 00000000..22d2ea00 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/trade/CartController.java @@ -0,0 +1,240 @@ +package cn.lili.controller.trade; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; +import cn.lili.modules.order.cart.entity.vo.TradeParams; +import cn.lili.modules.order.cart.service.CartService; +import cn.lili.modules.order.order.entity.vo.ReceiptVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +/** + * 买家端,购物车接口 + * + * @author Chopper + * @date 2020/11/16 10:04 下午 + */ +@Slf4j +@RestController +@Api(tags = "买家端,购物车接口") +@RequestMapping("/buyer/trade/carts") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CartController { + + /** + * 购物车 + */ + private final CartService cartService; + + + @ApiOperation(value = "向购物车中添加一个产品") + @PostMapping + @ApiImplicitParams({ + @ApiImplicitParam(name = "skuId", value = "产品ID", required = true, dataType = "Long", paramType = "query"), + @ApiImplicitParam(name = "num", value = "此产品的购买数量", required = true, dataType = "int", paramType = "query"), + @ApiImplicitParam(name = "cartType", value = "购物车类型,默认加入购物车", paramType = "query") + }) + public ResultMessage add(@NotNull(message = "产品id不能为空") String skuId, + @NotNull(message = "购买数量不能为空") @Min(value = 1, message = "加入购物车数量必须大于0") Integer num, + String cartType) { + cartService.add(skuId, num, cartType); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "获取购物车页面购物车详情") + @GetMapping("/all") + public ResultMessage cartAll() { + return ResultUtil.data(this.cartService.getAllTradeDTO()); + } + + @ApiOperation(value = "获取购物车数量") + @GetMapping("/count") + public ResultMessage cartCount(@RequestParam(required = false) Boolean checked) { + return ResultUtil.data(this.cartService.getCartNum(checked)); + } + + @ApiOperation(value = "获取购物车可用优惠券数量") + @GetMapping("/coupon/num") + @ApiImplicitParams({ + @ApiImplicitParam(name = "way", value = "购物车购买:CART/立即购买:BUY_NOW/拼团购买:PINTUAN / 积分购买:POINT ", required = true, paramType = "query") + }) + public ResultMessage cartCouponNum(String way) { + return ResultUtil.data(this.cartService.getCanUseCoupon(CartTypeEnum.valueOf(way))); + } + + @ApiOperation(value = "更新购物车中单个产品数量", notes = "更新购物车中的多个产品的数量或选中状态") + @ApiImplicitParams({ + @ApiImplicitParam(name = "skuId", value = "产品id数组", required = true, dataType = "Long", paramType = "path"), + @ApiImplicitParam(name = "num", value = "产品数量", dataType = "int", paramType = "query"), + }) + @PostMapping(value = "/sku/num/{skuId}") + public ResultMessage update(@NotNull(message = "产品id不能为空") @PathVariable(name = "skuId") String skuId, + Integer num) { + cartService.updateNum(skuId, num); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "更新购物车中单个产品", notes = "更新购物车中的多个产品的数量或选中状态") + @ApiImplicitParams({ + @ApiImplicitParam(name = "skuId", value = "产品id数组", required = true, dataType = "Long", paramType = "path") + }) + @PostMapping(value = "/sku/checked/{skuId}") + public ResultMessage updateChecked(@NotNull(message = "产品id不能为空") @PathVariable(name = "skuId") String skuId, + boolean checked) { + cartService.checked(skuId, checked); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "购物车选中设置") + @PostMapping(value = "/sku/checked", produces = MediaType.APPLICATION_JSON_VALUE) + public ResultMessage updateAll(boolean checked) { + cartService.checkedAll(checked); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "批量设置某商家的商品为选中或不选中") + @ApiImplicitParams({ + @ApiImplicitParam(name = "storeId", value = "卖家id", required = true, dataType = "Long", paramType = "path"), + @ApiImplicitParam(name = "checked", value = "是否选中", required = true, dataType = "int", paramType = "query", allowableValues = "0,1") + }) + @ResponseBody + @PostMapping(value = "/store/{storeId}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResultMessage updateStoreAll(@NotNull(message = "卖家id不能为空") @PathVariable(name = "storeId") String storeId, boolean checked) { + cartService.checkedStore(storeId, checked); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "清空购物车") + @DeleteMapping() + public ResultMessage clean() { + cartService.clean(); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "删除购物车中的一个或多个产品") + @ApiImplicitParams({ + @ApiImplicitParam(name = "skuIds", value = "产品id", required = true, dataType = "Long", paramType = "path", allowMultiple = true) + }) + @DeleteMapping(value = "/sku/remove") + public ResultMessage delete(String[] skuIds) { + cartService.delete(skuIds); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "获取结算页面购物车详情") + @ApiImplicitParams({ + @ApiImplicitParam(name = "way", value = "购物车购买:CART/立即购买:BUY_NOW/拼团购买:PINTUAN / 积分购买:POINT ", required = true, paramType = "query") + }) + @GetMapping("/checked") + public ResultMessage cartChecked(@NotNull(message = "读取选中列表") String way) { + try { + // 读取选中的列表 + return ResultUtil.data(this.cartService.getCheckedTradeDTO(CartTypeEnum.valueOf(way))); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + log.error(ResultCode.CART_ERROR.message(), e); + return ResultUtil.error(ResultCode.CART_ERROR); + } + } + + @ApiOperation(value = "选择收货地址") + @ApiImplicitParams({ + @ApiImplicitParam(name = "shippingAddressId", value = "收货地址id ", required = true, paramType = "query"), + @ApiImplicitParam(name = "way", value = "购物车类型 ", paramType = "query") + }) + @GetMapping("/shippingAddress") + public ResultMessage shippingAddress(@NotNull(message = "收货地址ID不能为空") String shippingAddressId, + String way) { + try { + cartService.shippingAddress(shippingAddressId, way); + return ResultUtil.success(ResultCode.SUCCESS); + } catch (ServiceException se) { + log.error(ResultCode.SHIPPING_NOT_APPLY.message(), se); + return ResultUtil.error(ResultCode.SHIPPING_NOT_APPLY); + } catch (Exception e) { + log.error(ResultCode.CART_ERROR.message(), e); + return ResultUtil.error(ResultCode.CART_ERROR); + } + } + + @ApiOperation(value = "选择配送方式") + @ApiImplicitParams({ + @ApiImplicitParam(name = "shippingMethod", value = "配送方式:SELF_PICK_UP(自提)," + + "LOCAL_TOWN_DELIVERY(同城配送)," + + "LOGISTICS(物流) ", required = true, paramType = "query"), + @ApiImplicitParam(name = "selleId", value = "店铺id", paramType = "query"), + @ApiImplicitParam(name = "way", value = "购物车类型 ", paramType = "query") + }) + @GetMapping("/shippingMethod") + public ResultMessage shippingMethod(@NotNull(message = "配送方式不能为空") String shippingMethod, + String selleId, + String way) { + try { + cartService.shippingMethod(selleId, shippingMethod, way); + return ResultUtil.success(ResultCode.SUCCESS); + } catch (Exception e) { + log.error(ResultCode.CART_ERROR.message(), e); + return ResultUtil.error(ResultCode.CART_ERROR); + } + } + + @ApiOperation(value = "选择发票") + @ApiImplicitParams({ + @ApiImplicitParam(name = "way", value = "购物车购买:CART/立即购买:BUY_NOW/拼团购买:PINTUAN / 积分购买:POINT ", required = true, paramType = "query"), + }) + @GetMapping("/select/receipt") + public ResultMessage selectReceipt(String way, ReceiptVO receiptVO) { + this.cartService.shippingReceipt(receiptVO, way); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "选择优惠券") + @ApiImplicitParams({ + @ApiImplicitParam(name = "way", value = "购物车购买:CART/立即购买:BUY_NOW/拼团购买:PINTUAN / 积分购买:POINT ", required = true, paramType = "query"), + @ApiImplicitParam(name = "memberCouponId", value = "优惠券id ", required = true, paramType = "query"), + @ApiImplicitParam(name = "used", value = "使用true 弃用false ", required = true, paramType = "query") + }) + @GetMapping("/select/coupon") + public ResultMessage selectCoupon(String way, @NotNull(message = "优惠券id不能为空") String memberCouponId, boolean used) { + this.cartService.selectCoupon(memberCouponId, way, used); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "创建交易") + @PostMapping(value = "/create/trade", consumes = "application/json", produces = "application/json") + public ResultMessage crateTrade(@RequestBody TradeParams tradeParams) { + try { + // 读取选中的列表 + return ResultUtil.data(this.cartService.createTrade(tradeParams)); + } catch (Exception e) { + log.error(ResultCode.ORDER_ERROR.message(), e); + if (e.getMessage().equals(ResultCode.SHIPPING_NOT_APPLY.message())) { + return ResultUtil.error(ResultCode.SHIPPING_NOT_APPLY); + } + return ResultUtil.error(ResultCode.ORDER_ERROR); + } + } +} diff --git a/buyer-api/src/main/java/cn/lili/controller/trade/OrderBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/trade/OrderBuyerController.java new file mode 100644 index 00000000..77c3958a --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/trade/OrderBuyerController.java @@ -0,0 +1,120 @@ +package cn.lili.controller.trade; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dto.OrderSearchParams; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import cn.lili.modules.order.order.service.OrderService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import springfox.documentation.annotations.ApiIgnore; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 买家端,订单接口 + * + * @author Chopper + * @date 2020/11/16 10:08 下午 + */ +@RestController +@Api(tags = "买家端,订单接口") +@RequestMapping("/buyer/orders") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderBuyerController { + + /** + * 订单 + */ + private final OrderService orderService; + + @ApiOperation(value = "查询会员订单列表") + @GetMapping + public ResultMessage> queryMineOrder(OrderSearchParams orderSearchParams) { + AuthUser currentUser = UserContext.getCurrentUser(); + orderSearchParams.setMemberId(currentUser.getId()); + return ResultUtil.data(orderService.queryByParams(orderSearchParams)); + } + + @ApiOperation(value = "订单明细") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, paramType = "path") + }) + @GetMapping(value = "/{orderSn}") + public ResultMessage detail(@NotNull(message = "订单编号不能为空") @PathVariable("orderSn") String orderSn) { + return ResultUtil.data(orderService.queryDetail(orderSn)); + } + + @ApiOperation(value = "确认收货") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, paramType = "path") + }) + @PostMapping(value = "/{orderSn}/receiving") + public ResultMessage receiving(@NotNull(message = "订单编号不能为空") @PathVariable("orderSn") String orderSn) { + Order order = orderService.getBySn(orderSn); + if (order == null) { + return ResultUtil.error(ResultCode.ORDER_NOT_EXIST); + } + //判定是否是待收货状态 + if (!order.getOrderStatus().equals(OrderStatusEnum.DELIVERED.name())) { + return ResultUtil.error(ResultCode.ORDER_DELIVERED_ERROR); + } + orderService.complete(orderSn); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "取消订单") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "reason", value = "取消原因", required = true, dataType = "String", paramType = "query") + }) + @PostMapping(value = "/{orderSn}/cancel") + public ResultMessage cancel(@ApiIgnore @PathVariable String orderSn, @RequestParam String reason) { + orderService.cancel(orderSn, reason); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "删除订单") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") + }) + @DeleteMapping(value = "/{orderSn}") + public ResultMessage deleteOrder(@PathVariable String orderSn) { + orderService.deleteOrder(orderSn); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "查询物流踪迹") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") + }) + @PostMapping(value = "/getTraces/{orderSn}") + public ResultMessage getTraces(@NotBlank(message = "订单编号不能为空") @PathVariable String orderSn) { + return ResultUtil.data(orderService.getTraces(orderSn)); + } + + + @ApiOperation(value = "开票") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") + }) + @PostMapping(value = "/receipt/{orderSn}") + public ResultMessage invoice(@NotBlank(message = "订单编号不能为空") @PathVariable String orderSn) { + return ResultUtil.data(orderService.invoice(orderSn)); + } + + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/trade/OrderComplaintBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/trade/OrderComplaintBuyerController.java new file mode 100644 index 00000000..482e7c89 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/trade/OrderComplaintBuyerController.java @@ -0,0 +1,99 @@ +package cn.lili.controller.trade; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.OrderComplaint; +import cn.lili.modules.order.order.entity.dto.OrderComplaintDTO; +import cn.lili.modules.order.order.entity.enums.CommunicationOwnerEnum; +import cn.lili.modules.order.order.entity.vo.OrderComplaintCommunicationVO; +import cn.lili.modules.order.order.entity.vo.OrderComplaintSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderComplaintVO; +import cn.lili.modules.order.order.service.OrderComplaintCommunicationService; +import cn.lili.modules.order.order.service.OrderComplaintService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 买家端,交易投诉接口 + * + * @author paulG + * @since 2020/12/7 + **/ +@RestController +@Api(tags = "买家端,交易投诉接口") +@RequestMapping("/buyer/complain") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderComplaintBuyerController { + + /** + * 交易投诉 + */ + private final OrderComplaintService orderComplaintService; + + /** + * 交易投诉沟通 + */ + private final OrderComplaintCommunicationService orderComplaintCommunicationService; + + + @ApiOperation(value = "通过id获取") + @ApiImplicitParam(name = "id", value = "投诉单ID", required = true, paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(orderComplaintService.getOrderComplainById(id)); + } + + @ApiOperation(value = "分页获取") + @GetMapping + public ResultMessage> get(OrderComplaintSearchParams searchParams, PageVO pageVO) { + searchParams.setMemberId(UserContext.getCurrentUser().getId()); + return ResultUtil.data(orderComplaintService.getOrderComplainByPage(searchParams, pageVO)); + + } + + @ApiOperation(value = "添加交易投诉") + @PostMapping + public ResultMessage add(@Valid OrderComplaintDTO orderComplaintDTO) { + return ResultUtil.data(orderComplaintService.addOrderComplain(orderComplaintDTO)); + } + + @ApiOperation(value = "添加交易投诉对话") + @ApiImplicitParams({ + @ApiImplicitParam(name = "complainId", value = "投诉单ID", required = true, paramType = "query"), + @ApiImplicitParam(name = "content", value = "内容", required = true, paramType = "query") + }) + @PostMapping("/communication") + public ResultMessage addCommunication(@RequestParam String complainId, @RequestParam String content) { + AuthUser currentUser = UserContext.getCurrentUser(); + OrderComplaintCommunicationVO communicationVO = new OrderComplaintCommunicationVO(complainId, content, CommunicationOwnerEnum.BUYER.name(), currentUser.getId(), currentUser.getNickName()); + if (orderComplaintCommunicationService.addCommunication(communicationVO)) { + return ResultUtil.data(communicationVO); + + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "取消售后") + @ApiImplicitParam(name = "id", value = "投诉单ID", required = true, paramType = "path") + @PutMapping(value = "/status/{id}") + public ResultMessage cancel(@PathVariable String id) { + if (orderComplaintService.cancel(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/trade/ReceiptBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/trade/ReceiptBuyerController.java new file mode 100644 index 00000000..000cf305 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/trade/ReceiptBuyerController.java @@ -0,0 +1,51 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.Receipt; +import cn.lili.modules.order.order.entity.dto.OrderReceiptDTO; +import cn.lili.modules.order.order.entity.dto.ReceiptSearchParams; +import cn.lili.modules.order.order.service.ReceiptService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 买家端,发票接口 + * + * @author paulG + * @since 2021/1/12 + **/ +@RestController +@Api(tags = "买家端,发票接口") +@RequestMapping("/buyer/trade/receipt") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ReceiptBuyerController { + + private final ReceiptService receiptService; + + @ApiOperation(value = "获取发票详情") + @GetMapping("/{id}") + public ResultMessage getDetail(@PathVariable String id) { + return ResultUtil.data(this.receiptService.getDetail(id)); + } + + @ApiOperation(value = "获取发票分页信息") + @GetMapping + public ResultMessage> getPage(ReceiptSearchParams searchParams, PageVO pageVO) { + return ResultUtil.data(this.receiptService.getReceiptData(searchParams, pageVO)); + } + + @ApiOperation(value = "保存发票信息") + @PostMapping + public ResultMessage save(@Valid Receipt receipt) { + return ResultUtil.data(receiptService.saveReceipt(receipt)); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/trade/RechargeTradeBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/trade/RechargeTradeBuyerController.java new file mode 100644 index 00000000..cc98621c --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/trade/RechargeTradeBuyerController.java @@ -0,0 +1,46 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.trade.entity.dos.Recharge; +import cn.lili.modules.order.trade.service.RechargeService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; + +/** + * 买家端,预存款充值记录接口 + * + * @author paulG + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "买家端,预存款充值记录接口") +@RequestMapping("/buyer/trade/recharge") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RechargeTradeBuyerController { + + private final RechargeService rechargeService; + + @PostMapping + @ApiOperation(value = "创建余额充值订单") + @ApiImplicitParams({ + @ApiImplicitParam(name = "price", value = "充值金额", required = true, dataType = "double", paramType = "query") + }) + public ResultMessage create(@Max(value = 10000, message = "充值金额单次最多允许充值10000元") @Min(value = 1, message = "充值金额单次最少充值金额为1元") Double price) { + Recharge recharge = this.rechargeService.recharge(price); + return ResultUtil.data(recharge); + } + +} diff --git a/buyer-api/src/main/java/cn/lili/controller/trade/WalletLogBuyerController.java b/buyer-api/src/main/java/cn/lili/controller/trade/WalletLogBuyerController.java new file mode 100644 index 00000000..0b7f7eca --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/controller/trade/WalletLogBuyerController.java @@ -0,0 +1,44 @@ +package cn.lili.controller.trade; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.trade.entity.dos.WalletLog; +import cn.lili.modules.order.trade.service.WalletLogService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +/** + * 买家端,预存款变动日志记录接口 + * + * @author pikachu + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "买家端,预存款变动日志记录接口") +@RequestMapping("/buyer/wallet/log") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WalletLogBuyerController { + + private final WalletLogService walletLogService; + + @ApiOperation(value = "分页获取预存款变动日志") + @GetMapping + public ResultMessage> getByPage(PageVO page) { + //获取当前登录用户 + AuthUser authUser = UserContext.getCurrentUser(); + //构建查询 返回数据 + IPage depositLogPage = walletLogService.page(PageUtil.initPage(page), new QueryWrapper().eq("member_id", authUser.getId())); + return ResultUtil.data(depositLogPage); + } +} diff --git a/buyer-api/src/main/java/cn/lili/security/BuyerAuthenticationFilter.java b/buyer-api/src/main/java/cn/lili/security/BuyerAuthenticationFilter.java new file mode 100755 index 00000000..6aa98c77 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/security/BuyerAuthenticationFilter.java @@ -0,0 +1,120 @@ +package cn.lili.security; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.enums.SecurityEnum; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.token.SecretKeyUtil; +import cn.lili.common.utils.ResponseUtil; +import com.google.gson.Gson; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + + +/** + * 认证结果过滤器 + * + * @author Chopper + * @version v4.1 + * @date 2020/11/17 3:37 下午 + * @Description: + * @since + */ +@Slf4j +public class BuyerAuthenticationFilter extends BasicAuthenticationFilter { + + + /** + * 缓存 + */ + private final Cache cache; + + /** + * 自定义构造器 + * + * @param authenticationManager + * @param cache + */ + public BuyerAuthenticationFilter(AuthenticationManager authenticationManager, + Cache cache) { + super(authenticationManager); + this.cache = cache; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + + //从header中获取jwt + String jwt = request.getHeader(SecurityEnum.HEADER_TOKEN.getValue()); + try { + // 如果没有token 则return + if (StrUtil.isBlank(jwt)) { + chain.doFilter(request, response); + return; + } + //获取用户信息,存入context + UsernamePasswordAuthenticationToken authentication = getAuthentication(jwt, response); + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (Exception e) { + log.error("BuyerAuthenticationFilter-> member authentication exception:", e); + } + chain.doFilter(request, response); + } + + /** + * 解析用户 + * + * @param jwt + * @param response + * @return + */ + private UsernamePasswordAuthenticationToken getAuthentication(String jwt, HttpServletResponse response) { + + try { + Claims claims + = Jwts.parser() + .setSigningKey(SecretKeyUtil.generalKeyByDecoders()) + .parseClaimsJws(jwt).getBody(); + //获取存储在claims中的用户信息 + String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString(); + AuthUser authUser = new Gson().fromJson(json, AuthUser.class); + + // 校验redis中是否有权限 + if (cache.hasKey(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.MEMBER) + jwt)) { + //构造返回信息 + List auths = new ArrayList<>(); + auths.add(new SimpleGrantedAuthority("ROLE_" + authUser.getRole().name())); + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authUser.getUsername(), null, auths); + authentication.setDetails(authUser); + return authentication; + } + ResponseUtil.output(response, 403, ResponseUtil.resultMap(false, 403, "登录已失效,请重新登录")); + return null; + } catch (ExpiredJwtException e) { + log.debug("user analysis exception:", e); + } catch (Exception e) { + log.error("user analysis exception:", e); + } + return null; + } + +} + diff --git a/buyer-api/src/main/java/cn/lili/security/BuyerSecurityConfig.java b/buyer-api/src/main/java/cn/lili/security/BuyerSecurityConfig.java new file mode 100644 index 00000000..eda49b97 --- /dev/null +++ b/buyer-api/src/main/java/cn/lili/security/BuyerSecurityConfig.java @@ -0,0 +1,86 @@ +package cn.lili.security; + +import cn.lili.common.cache.Cache; +import cn.lili.common.security.CustomAccessDeniedHandler; +import cn.lili.common.utils.SpringContextUtil; +import cn.lili.config.properties.IgnoredUrlsProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.web.cors.CorsConfigurationSource; + +/** + * spring Security 核心配置类 Buyer安全配置中心 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/14 16:20 + */ + +@Slf4j +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BuyerSecurityConfig extends WebSecurityConfigurerAdapter { + + /** + * 忽略验权配置 + */ + private final IgnoredUrlsProperties ignoredUrlsProperties; + + + + /** + * spring security -》 权限不足处理 + */ + private final CustomAccessDeniedHandler accessDeniedHandler; + + + private final Cache cache; + + @Override + protected void configure(HttpSecurity http) throws Exception { + + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = http + .authorizeRequests(); + // 配置的url 不需要授权 + for (String url : ignoredUrlsProperties.getUrls()) { + registry.antMatchers(url).permitAll(); + } + registry + .and() + // 禁止网页iframe + .headers().frameOptions().disable() + .and() + .logout() + .permitAll() + .and() + .authorizeRequests() + // 任何请求 + .anyRequest() + // 需要身份认证 + .authenticated() + .and() + // 允许跨域 + .cors().configurationSource((CorsConfigurationSource) SpringContextUtil.getBean("corsConfigurationSource")).and() + // 关闭跨站请求防护 + .csrf().disable() + // 前后端分离采用JWT 不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + // 自定义权限拒绝处理类 + .exceptionHandling().accessDeniedHandler(accessDeniedHandler) + .and() + // 添加JWT认证过滤器 + .addFilter(new BuyerAuthenticationFilter(authenticationManager(), cache)); + } + + +} diff --git a/buyer-api/src/main/resources/application.yml b/buyer-api/src/main/resources/application.yml new file mode 100644 index 00000000..2350e434 --- /dev/null +++ b/buyer-api/src/main/resources/application.yml @@ -0,0 +1,303 @@ +server: + port: 8888 + + servlet: + context-path: / + + # 正式部署时候,解开此处配置,防止文件夹被清除导致的文件上传失败问题 + # multipart: + # location: /Users/lifenlong/Desktop/ceshi + tomcat: + uri-encoding: UTF-8 + threads: + min-spare: 50 + max: 1000 + +# 与Spring Boot 2一样,默认情况下,大多数端点都不通过http公开,我们公开了所有端点。对于生产,您应该仔细选择要公开的端点。 +management: + # health: + # elasticsearch: + # enabled: false + # datasource: + # enabled: false + endpoints: + web: + exposure: + include: '*' +spring: + # 要在其中注册的Spring Boot Admin Server的URL。 + boot: + admin: + client: + url: http://127.0.0.1:8000 + # 文件大小上传配置 + servlet: + multipart: + max-file-size: 20MB + max-request-size: 20MB + cache: + type: redis + #JPA + jpa: + # 自动生成表结构 + generate-ddl: true + open-in-view: false + #jackson json解析 + jackson: + time-zone: GMT+8 + serialization: + #关闭jackson 对json做解析 + fail-on-empty-beans: false + + # mongodb + data: + mongodb: + host: 127.0.0.1 + port: 27017 + database: lilishop + username: root + password: lilishop + authentication-database: admin + # replica-set-name: mongoreplset + + # Redis + redis: + host: 127.0.0.1 + port: 6379 + password: lilishop + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: 20 + # 连接池中的最大空闲连接 默认 8 + max-idle: 10 + # 连接池中的最小空闲连接 默认 8 + min-idle: 8 + #mysql + shardingsphere: + datasource: + # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 + names: default-datasource + default-datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: lilishop + maxActive: 20 + initialSize: 5 + maxWait: 60000 + minIdle: 5 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 + poolPreparedStatements: false + #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 + maxOpenPreparedStatements: -1 + #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,log4j2 + #通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + #合并多个DruidDataSource的监控数据 + useGlobalDataSourceStat: true + loginUsername: druid + loginPassword: druid + # sharding: + # default-data-source-name: default-datasource + # #需要拆分的表,可以设置多个 在 li_order 级别即可 + # tables: + # #需要进行分表的逻辑表名 + # li_order: + # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 + # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} + # table-strategy: + # # 分表策略,根据创建日期 + # standard: + # sharding-column: create_time + # #分表策略 + # precise-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + # #范围查询实现 + # range-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + props: + #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 + sql: + show: true + +# 忽略TOKEN 鉴权 的url +ignored: + urls: + - /editor-app/** + - /actuator** + - /actuator/** + - /MP_verify_qSyvBPhDsPdxvOhC.txt + - /weixin/** + - /source/** + - /buyer/store/** + - /buyer/mini-program/** + - /buyer/cashier/** + - /buyer/pageData/** + - /buyer/article/** + - /buyer/goods/** + - /buyer/category/** + - /buyer/shop/** + - /buyer/connect/** + - /buyer/members/** + - /buyer/promotion/pintuan + - /buyer/promotion/seckill + - /buyer/memberEvaluation/**/goodsEvaluation + - /buyer/memberEvaluation/**/evaluationNumber + - /store/login/** + - /manager/user/login + - /manager/user/refresh/** + - /druid/** + - /swagger-ui.html + - /doc.html + - /swagger-resources/** + - /swagger/** + - /**/**.js + - /**/**.png + - /**/**.css + - /webjars/** + - /v2/api-docs + - /configuration/ui + - /boot-admin + statics: + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + +# Swagger界面内容配置 +swagger: + title: lili API接口文档 + description: lili Api Documentation + version: 1.0.0 + termsOfServiceUrl: https://pickmall.cn + contact: + name: lili + url: https://pickmall.cn + email: admin@pickmall.com + +# Mybatis-plus +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + #缓存开启 + cache-enabled: true + #日志 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 日志 +logging: + # 输出级别 + level: + cn.lili: debug + org.hibernate: debug +# org.springframework: debug + # org.springframework.data.mongodb.core: debug + file: + # 指定路径 + path: lili-logs + # 最大保存天数 + max-history: 7 + # 每个文件最大大小 + max-size: 5MB +#加密参数 +jasypt: + encryptor: + password: lili + +lili: + system: + isDemoSite: true + statistics: + # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 + onlineMember: 48 + # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 + currentOnlineUpdate: 600 + #qq lbs 申请 + lbs: + key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT + sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 + #域名 + domain: + pc: https://pc-b2b2c.pickmall.cn + wap: https://m-b2b2c.pickmall.cn + store: https://store-b2b2c.pickmall.cn + admin: https://admin-b2b2c.pickmall.cn + #api地址 + api: + buyer: https://buyer-api.pickmall.cn + common: https://common-api.pickmall.cn + manager: https://admin-api.pickmall.cn + store: https://store-api.pickmall.cn + + # jwt 细节设定 + jwt-setting: + # token过期时间(分钟) + tokenExpireTime: 60 + + # 使用Spring @Cacheable注解失效时间 + cache: + # 过期时间 单位秒 永久不过期设为-1 + timeout: 1500 + #多线程配置 + thread: + corePoolSize: 5 + maxPoolSize: 50 + queueCapacity: 50 + data: + elasticsearch: + cluster-name: elasticsearch + cluster-nodes: 127.0.0.1:9200 + index: + number-of-replicas: 0 + number-of-shards: 3 + index-prefix: lili + schema: http + # account: + # username: elastic + # password: LiLiShopES + + rocketmq: + promotion-topic: lili_promotion_topic + promotion-group: lili_promotion_group + msg-ext-topic: lili_msg_topic + msg-ext-group: lili_msg_group + goods-topic: lili_goods_topic + goods-group: lili_goods_group + order-topic: lili_order_topic + order-group: lili_order_group + member-topic: lili_member_topic + member-group: lili_member_group + other-topic: lili_other_topic + other-group: lili_other_group + notice-topic: lili_notice_topic + notice-group: lili_notice_group + notice-send-topic: lili_send_notice_topic + notice-send-group: lili_send_notice_group + after-sale-topic: lili_after_sale_topic + after-sale-group: lili_after_sale_group +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: lili_group + send-message-timeout: 30000 + +xxl: + job: + admin: + addresses: http://127.0.0.1:9001/xxl-job-admin + executor: + appname: xxl-job-executor-lilishop + address: + ip: + port: 8891 + logpath: ./xxl-job/executor + logretentiondays: 7 diff --git a/buyer-api/src/main/resources/banner.txt b/buyer-api/src/main/resources/banner.txt new file mode 100644 index 00000000..be6731c5 --- /dev/null +++ b/buyer-api/src/main/resources/banner.txt @@ -0,0 +1,19 @@ + ___ ___ ___ ___ ________ ________ _______ ________ _____ ______ ___ __ ________ ________ ___ __ +|\ \ |\ \|\ \ |\ \ |\ _____\\ __ \|\ ___ \ |\ __ \|\ _ \ _ \|\ \ |\ \|\ __ \|\ __ \|\ \|\ \ +\ \ \ \ \ \ \ \ \ \ \ ____________\ \ \__/\ \ \|\ \ \ __/|\ \ \|\ \ \ \\\__\ \ \ \ \ \ \ \ \ \|\ \ \ \|\ \ \ \/ /|_ + \ \ \ \ \ \ \ \ \ \ \|\____________\ \ __\\ \ _ _\ \ \_|/_\ \ __ \ \ \\|__| \ \ \ \ __\ \ \ \ \\\ \ \ _ _\ \ ___ \ + \ \ \____\ \ \ \ \____\ \ \|____________|\ \ \_| \ \ \\ \\ \ \_|\ \ \ \ \ \ \ \ \ \ \ \ \|\__\_\ \ \ \\\ \ \ \\ \\ \ \\ \ \ + \ \_______\ \__\ \_______\ \__\ \ \__\ \ \__\\ _\\ \_______\ \__\ \__\ \__\ \ \__\ \____________\ \_______\ \__\\ _\\ \__\\ \__\ + \|_______|\|__|\|_______|\|__| \|__| \|__|\|__|\|_______|\|__|\|__|\|__| \|__|\|____________|\|_______|\|__|\|__|\|__| \|__| + + + + ___ ___ ___ ___ ________ ___ ___ ________ ________ + |\ \ |\ \|\ \ |\ \ |\ ____\|\ \|\ \|\ __ \|\ __ \ + \ \ \ \ \ \ \ \ \ \ \ ____________\ \ \___|\ \ \\\ \ \ \|\ \ \ \|\ \ + \ \ \ \ \ \ \ \ \ \ \|\____________\ \_____ \ \ __ \ \ \\\ \ \ ____\ + \ \ \____\ \ \ \ \____\ \ \|____________|\|____|\ \ \ \ \ \ \ \\\ \ \ \___| + \ \_______\ \__\ \_______\ \__\ ____\_\ \ \__\ \__\ \_______\ \__\ + \|_______|\|__|\|_______|\|__| |\_________\|__|\|__|\|_______|\|__| + \|_________| + diff --git a/buyer-api/src/main/resources/static/MP_verify_qSyvBPhDsPdxvOhC.txt b/buyer-api/src/main/resources/static/MP_verify_qSyvBPhDsPdxvOhC.txt new file mode 100644 index 00000000..767ab3e8 --- /dev/null +++ b/buyer-api/src/main/resources/static/MP_verify_qSyvBPhDsPdxvOhC.txt @@ -0,0 +1 @@ +qSyvBPhDsPdxvOhC \ No newline at end of file diff --git a/buyer-api/src/test/java/cn/lili/buyer/test/cart/CartTest.java b/buyer-api/src/test/java/cn/lili/buyer/test/cart/CartTest.java new file mode 100644 index 00000000..1cc333be --- /dev/null +++ b/buyer-api/src/test/java/cn/lili/buyer/test/cart/CartTest.java @@ -0,0 +1,75 @@ +package cn.lili.buyer.test.cart; + +import cn.hutool.json.JSONUtil; +import cn.lili.modules.goods.entity.vos.CategoryVO; +import cn.lili.modules.goods.service.CategoryService; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; +import cn.lili.modules.order.cart.entity.vo.TradeParams; +import cn.lili.modules.order.cart.service.CartService; +import cn.lili.modules.payment.service.PaymentService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.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; + +import java.util.List; + +/** + * @author paulG + * @since 2020/11/14 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class CartTest { + + @Autowired + private CartService cartService; + + @Autowired + private CategoryService categoryService; + + @Autowired + private PaymentService paymentService; + + + @Test + void getAll() { + TradeDTO allTradeDTO = cartService.getAllTradeDTO(); + Assertions.assertNotNull(allTradeDTO); + System.out.println(JSONUtil.toJsonStr(allTradeDTO)); + } + + @Test + void deleteAll() { + cartService.delete(new String[]{"1344220459059404800"}); + Assertions.assertTrue(true); + } + + @Test + void createTrade() { +// TradeDTO allTradeDTO = cartService.getAllTradeDTO(); +// Assert.assertNotNull(allTradeDTO); +// System.out.println(JsonUtil.objectToJson(allTradeDTO)); + cartService.createTrade(new TradeParams()); + } + + @Test + void getAllCategory() { + List allCategory = categoryService.categoryTree(); + for (CategoryVO categoryVO : allCategory) { + System.out.println(categoryVO); + } + Assertions.assertTrue(true); + } + + + @Test + void storeCoupon() { + cartService.selectCoupon("1333318596239843328", CartTypeEnum.CART.name(), true); + Assertions.assertTrue(true); + } + +} diff --git a/buyer-api/src/test/java/cn/lili/buyer/test/cart/FileTest.java b/buyer-api/src/test/java/cn/lili/buyer/test/cart/FileTest.java new file mode 100644 index 00000000..9fedbd63 --- /dev/null +++ b/buyer-api/src/test/java/cn/lili/buyer/test/cart/FileTest.java @@ -0,0 +1,52 @@ +package cn.lili.buyer.test.cart; + + +import cn.lili.modules.file.plugin.FileManagerPlugin; +import cn.lili.modules.goods.entity.dos.Brand; +import cn.lili.modules.goods.service.BrandService; +import com.xkcoding.http.util.StringUtil; +import org.junit.jupiter.api.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; + +import java.io.*; +import java.net.URL; +import java.util.List; + +/** + * @author paulG + * @since 2020/11/14 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class FileTest { + + + @Autowired + private FileManagerPlugin fileManagerPlugin; + + @Autowired + private BrandService brandService; + + @Test + void test() throws Exception { + List categoryList = brandService.list(); + for (Brand brand : categoryList) { + try { + if (StringUtil.isEmpty(brand.getLogo()) || brand.getLogo().indexOf("lilishop") > 1) { + continue; + } + URL url = new URL(brand.getLogo()); + InputStream inputStream = url.openStream(); + // 上传至第三方云服务或服务器 + brand.setLogo(fileManagerPlugin.inputStreamUpload(inputStream, brand.getId() + ".png")); + } catch (IOException e) { + e.printStackTrace(); + } + } + brandService.updateBatchById(categoryList); + } + +} diff --git a/buyer-api/src/test/java/cn/lili/buyer/test/cart/MemberCouponTest.java b/buyer-api/src/test/java/cn/lili/buyer/test/cart/MemberCouponTest.java new file mode 100644 index 00000000..4aba260e --- /dev/null +++ b/buyer-api/src/test/java/cn/lili/buyer/test/cart/MemberCouponTest.java @@ -0,0 +1,29 @@ +package cn.lili.buyer.test.cart; + +import cn.lili.modules.promotion.service.MemberCouponService; +import org.junit.Assert; +import org.junit.jupiter.api.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; + +/** + * @author paulG + * @since 2020/11/27 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class MemberCouponTest { + + @Autowired + private MemberCouponService memberCouponService; + + @Test + void receiveCoupon() { + memberCouponService.receiveCoupon("1333318596239843328", "1326834797335306240", "1"); + Assert.assertTrue(true); + } + + +} diff --git a/buyer-api/src/test/resources/application.yml b/buyer-api/src/test/resources/application.yml new file mode 100644 index 00000000..dd015575 --- /dev/null +++ b/buyer-api/src/test/resources/application.yml @@ -0,0 +1,298 @@ +server: + servlet: + context-path: / + + # 正式部署时候,解开此处配置,防止文件夹被清除导致的文件上传失败问题 + # multipart: + # location: /Users/lifenlong/Desktop/ceshi + tomcat: + uri-encoding: UTF-8 + threads: + min-spare: 50 + max: 1000 + +# 与Spring Boot 2一样,默认情况下,大多数端点都不通过http公开,我们公开了所有端点。对于生产,您应该仔细选择要公开的端点。 +management: + # health: + # elasticsearch: + # enabled: false + # datasource: + # enabled: false + endpoints: + web: + exposure: + include: '*' +spring: + # 要在其中注册的Spring Boot Admin Server的URL。 + boot: + admin: + client: + url: http://192.168.0.116:8000 + # mongodb + data: + mongodb: + host: 192.168.0.116 + port: 27017 + database: lilishop + username: root + password: lilishop + authentication-database: admin + # replica-set-name: mongoreplset + cache: + type: redis + #amqp + # rabbitmq: + # host: 192.168.0.116 + jpa: + # 自动生成表结构 + generate-ddl: true + open-in-view: false + # Redis + redis: + host: 192.168.0.116 + port: 6379 + password: lilishop + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: 20 + # 连接池中的最大空闲连接 默认 8 + max-idle: 10 + # 连接池中的最小空闲连接 默认 8 + min-idle: 8 + # 文件大小上传配置 + servlet: + multipart: + max-file-size: 20MB + max-request-size: 20MB + jackson: + time-zone: GMT+8 + serialization: + #关闭jackson 对json做解析 + fail-on-empty-beans: false + + shardingsphere: + datasource: + # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 + names: default-datasource + default-datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.0.116:3306/new-lili?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: lilishop + maxActive: 20 + initialSize: 5 + maxWait: 60000 + minIdle: 5 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 + poolPreparedStatements: false + #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 + maxOpenPreparedStatements: -1 + #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,log4j2 + #通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + #合并多个DruidDataSource的监控数据 + useGlobalDataSourceStat: true + loginUsername: druid + loginPassword: druid + # sharding: + # default-data-source-name: default-datasource + # #需要拆分的表,可以设置多个 在 li_order 级别即可 + # tables: + # #需要进行分表的逻辑表名 + # li_order: + # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 + # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} + # table-strategy: + # # 分表策略,根据创建日期 + # standard: + # sharding-column: create_time + # #分表策略 + # precise-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + # #范围查询实现 + # range-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + props: + #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 + sql: + show: true + +# 忽略鉴权url +ignored: + urls: + - /editor-app/** + - /actuator** + - /actuator/** + - /MP_verify_qSyvBPhDsPdxvOhC.txt + - /weixin/** + - /source/** + - /buyer/mini-program/** + - /buyer/cashier/** + - /buyer/pageData/** + - /buyer/article/** + - /buyer/goods/** + - /buyer/category/** + - /buyer/store/** + - /buyer/connect/** + - /buyer/members/** + - /buyer/promotion/pintuan/** + - /buyer/promotion/seckill/** + - /buyer/promotion/pointsGoods/** + - /buyer/memberEvaluation/**/goodsEvaluation + - /buyer/memberEvaluation/**/evaluationNumber + - /store/login/** + - /manager/user/login + - /manager/user/refresh/** + - /druid/** + - /swagger-ui.html + - /doc.html + - /swagger-resources/** + - /swagger/** + - /**/**.js + - /**/**.png + - /**/**.css + - /webjars/** + - /v2/api-docs + - /configuration/ui + - /boot-admin + statics: + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + +# Swagger界面内容配置 +swagger: + title: lili API接口文档 + description: lili Api Documentation + version: 1.0.0 + termsOfServiceUrl: https://pickmall.cn + contact: + name: lili + url: https://pickmall.cn + email: admin@pickmall.com + +# Mybatis-plus +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + #缓存开启 + cache-enabled: true + #日志 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 日志 +logging: + # 输出级别 + level: + cn.lili: info + # org.hibernate: debug + # org.springframework: debug + # org.springframework.data.mongodb.core: debug + file: + # 指定路径 + path: lili-logs + # 最大保存天数 + max-history: 7 + # 每个文件最大大小 + max-size: 5MB +#加密参数 +jasypt: + encryptor: + password: lili + +lili: + system: + isDemoSite: true + statistics: + # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 + onlineMember: 48 + # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 + currentOnlineUpdate: 600 + #qq lbs 申请 + lbs: + key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT + sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 + #域名 + domain: + pc: http://192.168.0.116:8888 + wap: http://192.168.0.116:8888 + seller: http://192.168.0.116:8888 + admin: http://192.168.0.116:8888 + #api地址 + api: + buyer: https://z171l91606.51mypc.cn + base: http://192.168.0.116:8888 + manager: http://192.168.0.116:8888 + seller: http://192.168.0.116:8888 + + # jwt 细节设定 + jwt-setting: + # token过期时间(分钟) + tokenExpireTime: 60 + + # 使用Spring @Cacheable注解失效时间 + cache: + # 过期时间 单位秒 永久不过期设为-1 + timeout: 1500 + #多线程配置 + thread: + corePoolSize: 5 + maxPoolSize: 50 + queueCapacity: 50 + data: + elasticsearch: + cluster-name: elasticsearch + cluster-nodes: 192.168.0.116:9200 + index: + number-of-replicas: 0 + number-of-shards: 3 + index-prefix: lili + schema: http + # account: + # username: elastic + # password: LiLiShopES + + rocketmq: + promotion-topic: lili_promotion_topic + promotion-group: lili_promotion_group + msg-ext-topic: lili_msg_topic + msg-ext-group: lili_msg_group + goods-topic: lili_goods_topic + goods-group: lili_goods_group + order-topic: lili_order_topic + order-group: lili_order_group + member-topic: lili_member_topic + member-group: lili_member_group + other-topic: lili_other_topic + other-group: lili_other_group + notice-topic: lili_notice_topic + notice-group: lili_notice_group + notice-send-topic: lili_send_notice_topic + notice-send-group: lili_send_notice_group +rocketmq: + name-server: 192.168.0.116:9876 + producer: + group: lili_group + send-message-timeout: 30000 + +xxl: + job: + admin: + addresses: http://192.168.0.116:9001/xxl-job-admin + executor: + appname: xxl-job-executor-lilishop + address: + ip: + port: 8891 + logpath: ./xxl-job/executor + logretentiondays: 7 \ No newline at end of file diff --git a/common-api/pom.xml b/common-api/pom.xml new file mode 100644 index 00000000..4e5c7d8d --- /dev/null +++ b/common-api/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + + cn.lili + lili-shop-parent + 1.0.1 + + + cn.lili + common-api + + + + cn.lili + framework + 1.0.1 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/common-api/src/main/java/cn/lili/CommonApiApplication.java b/common-api/src/main/java/cn/lili/CommonApiApplication.java new file mode 100644 index 00000000..c97858ec --- /dev/null +++ b/common-api/src/main/java/cn/lili/CommonApiApplication.java @@ -0,0 +1,21 @@ +package cn.lili; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; + +/** + * 基础API + * + * @author Chopper + * @date 2020/11/17 3:38 下午 + */ +@EnableCaching +@SpringBootApplication +public class CommonApiApplication { + + public static void main(String[] args) { + SpringApplication.run(CommonApiApplication.class, args); + } + +} diff --git a/common-api/src/main/java/cn/lili/controller/common/FileController.java b/common-api/src/main/java/cn/lili/controller/common/FileController.java new file mode 100644 index 00000000..b2c5f162 --- /dev/null +++ b/common-api/src/main/java/cn/lili/controller/common/FileController.java @@ -0,0 +1,99 @@ +package cn.lili.controller.common; + +import cn.lili.common.cache.Cache; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.file.entity.File; +import cn.lili.modules.file.entity.dto.FileOwnerDTO; +import cn.lili.modules.file.service.FileService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 文件管理管理接口 + * + * @author Chopper + * @date 2020/11/26 15:41 + */ +@RestController +@Api(tags = "文件管理管理接口") +@RequestMapping("/common/file") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FileController { + + private final FileService fileService; + + private final Cache cache; + + @ApiOperation(value = "获取自己的图片资源") + @GetMapping + @ApiImplicitParam(name = "title", value = "名称模糊匹配") + public ResultMessage> getFileList(@RequestHeader String accessToken, File file, SearchVO searchVO, PageVO pageVo) { + + AuthUser authUser = UserContext.getAuthUser(cache, accessToken); + FileOwnerDTO fileOwnerDTO = new FileOwnerDTO(); + //只有买家才写入自己id + if (authUser.getRole().equals(UserEnums.MEMBER)) { + fileOwnerDTO.setOwnerId(authUser.getId()); + }//如果是店铺,则写入店铺id + else if (authUser.getRole().equals(UserEnums.STORE)) { + fileOwnerDTO.setOwnerId(authUser.getStoreId()); + } + fileOwnerDTO.setUserEnums(authUser.getRole().name()); + return ResultUtil.data(fileService.customerPageOwner(fileOwnerDTO, file, searchVO, pageVo)); + } + + @ApiOperation(value = "文件重命名") + @PostMapping(value = "/rename") + public ResultMessage upload(@RequestHeader String accessToken, String id, String newName) { + + AuthUser authUser = UserContext.getAuthUser(cache, accessToken); + File file = fileService.getById(id); + file.setName(newName); + //操作图片属性判定 + switch (authUser.getRole()) { + case MEMBER: + if (file.getOwnerId().equals(authUser.getId()) && file.getUserEnums().equals(authUser.getRole().name())) { + break; + } + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + case STORE: + if (file.getOwnerId().equals(authUser.getStoreId()) && file.getUserEnums().equals(authUser.getRole().name())) { + break; + } + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + case MANAGER: + if (file.getUserEnums().equals(authUser.getRole().name())) { + break; + } + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + fileService.updateById(file); + return ResultUtil.data(file); + } + + @ApiOperation(value = "文件删除") + @DeleteMapping(value = "/delete/{ids}") + public ResultMessage delete(@RequestHeader String accessToken, @PathVariable List ids) { + + AuthUser authUser = UserContext.getAuthUser(cache, accessToken); + fileService.batchDelete(ids, authUser); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/common-api/src/main/java/cn/lili/controller/common/LogoController.java b/common-api/src/main/java/cn/lili/controller/common/LogoController.java new file mode 100644 index 00000000..824bd48a --- /dev/null +++ b/common-api/src/main/java/cn/lili/controller/common/LogoController.java @@ -0,0 +1,38 @@ +package cn.lili.controller.common; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + + +/** + * 文件管理管理接口 + * + * @author Chopper + * @date 2020/11/26 15:41 + */ +@RestController +@Api(tags = "文件管理管理接口") +@RequestMapping("/common/logo") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class LogoController { + + @Autowired + private SettingService settingService; + + @ApiOperation(value = "获取logo") + @GetMapping + public ResultMessage getFileList() { + return ResultUtil.data(settingService.get(SettingEnum.BASE_SETTING.name())); + } + + +} diff --git a/common-api/src/main/java/cn/lili/controller/common/RegionController.java b/common-api/src/main/java/cn/lili/controller/common/RegionController.java new file mode 100644 index 00000000..a8d2d40d --- /dev/null +++ b/common-api/src/main/java/cn/lili/controller/common/RegionController.java @@ -0,0 +1,59 @@ +package cn.lili.controller.common; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.base.service.RegionService; +import cn.lili.modules.system.entity.dos.Region; +import cn.lili.modules.system.entity.vo.RegionVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 地址信息接口 + * + * @author Chopper + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "地址信息接口") +@RequestMapping("/common/region") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RegionController { + + + private final RegionService regionService; + + @ApiOperation(value = "点地图获取地址信息") + @ApiImplicitParams({ + @ApiImplicitParam(name = "cityCode", value = "城市code", dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "townName", value = "镇名称", dataType = "Long", paramType = "query") + }) + @GetMapping(value = "/region") + public ResultMessage getRegion(@RequestParam String cityCode,@RequestParam String townName) { + return ResultUtil.data(regionService.getRegion(cityCode,townName)); + } + + + @GetMapping(value = "/item/{id}") + @ApiImplicitParam(name = "id", value = "地区ID", required = true, dataType = "String", paramType = "path") + @ApiOperation(value = "通过id获取子地区") + public ResultMessage> getItem(@PathVariable String id) { + return ResultUtil.data(regionService.getItem(id)); + } + + @GetMapping(value = "/allCity") + @ApiOperation(value = "获取所有的省-市") + public ResultMessage> getAllCity() { + return ResultUtil.data(regionService.getAllCity()); + } + + +} diff --git a/common-api/src/main/java/cn/lili/controller/common/SliderImageController.java b/common-api/src/main/java/cn/lili/controller/common/SliderImageController.java new file mode 100644 index 00000000..a5bcfe3a --- /dev/null +++ b/common-api/src/main/java/cn/lili/controller/common/SliderImageController.java @@ -0,0 +1,51 @@ +package cn.lili.controller.common; + +import cn.lili.common.aop.limiter.annotation.LimitPoint; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.verification.enums.VerificationEnums; +import cn.lili.common.verification.service.VerificationService; +import cn.lili.common.vo.ResultMessage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 滑块验证码接口 + * + * @author Chopper + * @date 2020/11/26 15:41 + */ +@RequestMapping("/common/slider") +@RestController +@Api(tags = "滑块验证码接口") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SliderImageController { + + + private final VerificationService verificationService; + + //一分钟同一个ip请求10次 + @LimitPoint(name = "slider_image", key = "verification") + @GetMapping("/{verificationEnums}") + @ApiOperation(value = "获取校验接口") + public ResultMessage getSliderImage(@RequestHeader String uuid, @PathVariable VerificationEnums verificationEnums) { + try { + return ResultUtil.data(verificationService.createVerification(verificationEnums, uuid)); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @LimitPoint(name = "slider_image", key = "verification_pre_check", limit = 600) + @PostMapping("/{verificationEnums}") + @ApiOperation(value = "验证码预校验") + public ResultMessage verificationImage(Integer xPos, @RequestHeader String uuid, @PathVariable VerificationEnums verificationEnums) { + return ResultUtil.data(verificationService.preCheck(xPos, uuid, verificationEnums)); + } +} diff --git a/common-api/src/main/java/cn/lili/controller/common/SmsController.java b/common-api/src/main/java/cn/lili/controller/common/SmsController.java new file mode 100644 index 00000000..64e7e677 --- /dev/null +++ b/common-api/src/main/java/cn/lili/controller/common/SmsController.java @@ -0,0 +1,53 @@ +package cn.lili.controller.common; + +import cn.lili.common.aop.limiter.annotation.LimitPoint; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.sms.SmsUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.verification.enums.VerificationEnums; +import cn.lili.common.verification.service.VerificationService; +import cn.lili.common.vo.ResultMessage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 短信验证码接口 + * + * @author Chopper + * @date 2020/11/26 15:41 + */ +@RestController +@Api(tags = "短信验证码接口") +@RequestMapping("/common/sms") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SmsController { + + private final SmsUtil smsUtil; + + private final VerificationService verificationService; + + //一分钟同一个ip请求1次 + @LimitPoint(name = "sms_send", key = "sms") + @ApiImplicitParams({ + @ApiImplicitParam(paramType = "path", dataType = "String", name = "mobile", value = "手机号"), + @ApiImplicitParam(paramType = "header", dataType = "String", name = "uuid", value = "uuid"), + }) + @GetMapping("/{verificationEnums}/{mobile}") + @ApiOperation(value = "发送短信验证码") + public ResultMessage getSmsCode( + @RequestHeader String uuid, + @PathVariable String mobile, + @PathVariable VerificationEnums verificationEnums) { + if (verificationService.check(uuid, verificationEnums)) { + smsUtil.sendSmsCode(mobile, verificationEnums, uuid); + return ResultUtil.success(ResultCode.VERIFICATION_SEND_SUCCESS); + } else { + return ResultUtil.error(ResultCode.VERIFICATION_SMS_EXPIRED_ERROR); + } + } +} diff --git a/common-api/src/main/java/cn/lili/controller/common/UploadController.java b/common-api/src/main/java/cn/lili/controller/common/UploadController.java new file mode 100644 index 00000000..3f6d5cfd --- /dev/null +++ b/common-api/src/main/java/cn/lili/controller/common/UploadController.java @@ -0,0 +1,106 @@ +package cn.lili.controller.common; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.Base64DecodeMultipartFile; +import cn.lili.common.utils.CommonUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.file.entity.File; +import cn.lili.modules.file.plugin.FileManagerPlugin; +import cn.lili.modules.file.service.FileService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; + +/** + * 文件上传接口 + * + * @author Chopper + * @date 2020/11/26 15:41 + */ +@Slf4j +@RestController +@Api(tags = "文件上传接口") +@RequestMapping("/common/upload") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class UploadController { + + private final FileService fileService; + + private final SettingService settingService; + + private final FileManagerPlugin fileManagerPlugin; + + private final Cache cache; + + @ApiOperation(value = "文件上传") + @PostMapping(value = "/file") + public ResultMessage upload(MultipartFile file, + String base64, + @RequestHeader String accessToken) { + + + AuthUser authUser = UserContext.getAuthUser(cache, accessToken); + //如果用户未登录,则无法上传图片 + if (authUser == null) { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + Setting setting = settingService.getById(SettingEnum.OSS_SETTING.name()); + if (setting == null || StrUtil.isBlank(setting.getSettingValue())) { + throw new ServiceException(ResultCode.OSS_NOT_EXIST); + } + + if (StringUtils.isNotBlank(base64)) { + // base64上传 + file = Base64DecodeMultipartFile.base64Convert(base64); + } + String result = ""; + String fileKey = CommonUtil.rename(file.getOriginalFilename()); + File newFile = new File(); + try { + InputStream inputStream = file.getInputStream(); + // 上传至第三方云服务或服务器 + result = fileManagerPlugin.inputStreamUpload(inputStream, fileKey); + // 保存数据信息至数据库 + newFile.setName(file.getOriginalFilename()); + newFile.setFileSize(file.getSize()); + newFile.setFileType(file.getContentType()); + newFile.setFileKey(fileKey); + newFile.setUrl(result); + newFile.setCreateBy(authUser.getUsername()); + newFile.setUserEnums(authUser.getRole().name()); + //如果是店铺,则记录店铺id + if (authUser.getRole().equals(UserEnums.STORE.name())) { + newFile.setOwnerId(authUser.getStoreId()); + } else { + newFile.setOwnerId(authUser.getId()); + } + fileService.save(newFile); + } catch (Exception e) { + log.error("文件上传失败", e); + return ResultUtil.error(400, e.toString()); + } + return ResultUtil.data(result); + } + + +} diff --git a/common-api/src/main/java/cn/lili/controller/security/CommonSecurityConfig.java b/common-api/src/main/java/cn/lili/controller/security/CommonSecurityConfig.java new file mode 100644 index 00000000..c86b787f --- /dev/null +++ b/common-api/src/main/java/cn/lili/controller/security/CommonSecurityConfig.java @@ -0,0 +1,68 @@ +package cn.lili.controller.security; + +import cn.lili.common.cache.Cache; +import cn.lili.common.security.CustomAccessDeniedHandler; +import cn.lili.config.properties.IgnoredUrlsProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.web.cors.CorsConfigurationSource; + +/** + * spring Security 核心配置类 通用安全 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/14 16:20 + */ +@Slf4j +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CommonSecurityConfig extends WebSecurityConfigurerAdapter { + + + /** + * 忽略验权配置 + */ + private final IgnoredUrlsProperties ignoredUrlsProperties; + + /** + * spring security -》 权限不足处理 + */ + private final CustomAccessDeniedHandler accessDeniedHandler; + + + private final Cache cache; + + private final CorsConfigurationSource corsConfigurationSource; + + @Override + protected void configure(HttpSecurity http) throws Exception { + + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = http + .authorizeRequests(); + registry + .and() + // 禁止网页iframe + .headers().frameOptions().disable() + .and() + .authorizeRequests() + // 任何请求 + .anyRequest() + // 需要身份认证 + .permitAll() + .and() + // 允许跨域 + .cors().configurationSource(corsConfigurationSource).and() + // 关闭跨站请求防护 + .csrf().disable(); + } + +} diff --git a/common-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/common-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 00000000..62b698e0 --- /dev/null +++ b/common-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,8 @@ +{ + "properties": [ + { + "name": "spring.http.multipart.location", + "type": "java.lang.String", + "description": "Description for spring.http.multipart.location." + } +] } \ No newline at end of file diff --git a/common-api/src/main/resources/application.yml b/common-api/src/main/resources/application.yml new file mode 100644 index 00000000..50b9a813 --- /dev/null +++ b/common-api/src/main/resources/application.yml @@ -0,0 +1,299 @@ +server: + port: 8890 + + servlet: + context-path: / + + # 正式部署时候,解开此处配置,防止文件夹被清除导致的文件上传失败问题 + # multipart: + # location: /Users/lifenlong/Desktop/ceshi + tomcat: + uri-encoding: UTF-8 + threads: + min-spare: 50 + max: 1000 + +# 与Spring Boot 2一样,默认情况下,大多数端点都不通过http公开,我们公开了所有端点。对于生产,您应该仔细选择要公开的端点。 +management: + # health: + # elasticsearch: + # enabled: false + # datasource: + # enabled: false + endpoints: + web: + exposure: + include: '*' +spring: + # 要在其中注册的Spring Boot Admin Server的URL。 + boot: + admin: + client: + url: http://127.0.0.1:8000 + # mongodb + data: + mongodb: + host: 127.0.0.1 + port: 27017 + database: lilishop + username: root + password: lilishop + authentication-database: admin + # replica-set-name: mongoreplset + cache: + type: redis + + jpa: + # 自动生成表结构 + generate-ddl: true + open-in-view: false + # Redis + redis: + host: 127.0.0.1 + port: 6379 + password: lilishop + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: 20 + # 连接池中的最大空闲连接 默认 8 + max-idle: 10 + # 连接池中的最小空闲连接 默认 8 + min-idle: 8 + # 文件大小上传配置 + servlet: + multipart: + max-file-size: 20MB + max-request-size: 20MB + jackson: + time-zone: GMT+8 + serialization: + #关闭jackson 对json做解析 + fail-on-empty-beans: false + + shardingsphere: + datasource: + # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 + names: default-datasource + default-datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: lilishop + maxActive: 20 + initialSize: 5 + maxWait: 60000 + minIdle: 5 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 + poolPreparedStatements: false + #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 + maxOpenPreparedStatements: -1 + #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,log4j2 + #通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + #合并多个DruidDataSource的监控数据 + useGlobalDataSourceStat: true + loginUsername: druid + loginPassword: druid + # sharding: + # default-data-source-name: default-datasource + # #需要拆分的表,可以设置多个 在 li_order 级别即可 + # tables: + # #需要进行分表的逻辑表名 + # li_order: + # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 + # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} + # table-strategy: + # # 分表策略,根据创建日期 + # standard: + # sharding-column: create_time + # #分表策略 + # precise-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + # #范围查询实现 + # range-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + props: + #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 + sql: + show: true + +# 忽略鉴权url +ignored: + urls: + - /editor-app/** + - /actuator** + - /actuator/** + - /MP_verify_qSyvBPhDsPdxvOhC.txt + - /weixin/** + - /source/** + - /buyer/mini-program/** + - /buyer/cashier/** + - /buyer/pageData/** + - /buyer/article/** + - /buyer/goods/** + - /buyer/category/** + - /buyer/shop/** + - /buyer/connect/** + - /buyer/members/smsLogin + - /buyer/members/refresh/* + - /buyer/members/refresh** + - /buyer/promotion/pintuan + - /buyer/promotion/seckill + - /buyer/memberEvaluation/**/goodsEvaluation + - /buyer/memberEvaluation/**/evaluationNumber + - /store/login/** + - /manager/user/login + - /manager/user/refresh/** + - /druid/** + - /swagger-ui.html + - /doc.html + - /swagger-resources/** + - /swagger/** + - /**/**.js + - /**/**.png + - /**/**.css + - /webjars/** + - /v2/api-docs + - /configuration/ui + - /boot-admin + statics: + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + +# Swagger界面内容配置 +swagger: + title: lili API接口文档 + description: lili Api Documentation + version: 1.0.0 + termsOfServiceUrl: https://pickmall.cn + contact: + name: lili + url: https://pickmall.cn + email: admin@pickmall.com + +# Mybatis-plus +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + #缓存开启 + cache-enabled: true + #日志 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 日志 +logging: + # 输出级别 + level: + cn.lili: info + # org.hibernate: debug + # org.springframework: debug + # org.springframework.data.mongodb.core: debug + file: + # 指定路径 + path: lili-logs + # 最大保存天数 + max-history: 7 + # 每个文件最大大小 + max-size: 5MB +#加密参数 +jasypt: + encryptor: + password: lili + +lili: + system: + isDemoSite: true + statistics: + # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 + onlineMember: 48 + # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 + currentOnlineUpdate: 600 + #qq lbs 申请 + lbs: + key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT + sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 + #域名 + domain: + pc: https://pc.b2b2c.pickmall.cn + wap: https://m.b2b2c.pickmall.cn + store: https://store.b2b2c.pickmall.cn + admin: https://admin.b2b2c.pickmall.cn + #api地址 + api: + buyer: https://buyer-api.pickmall.cn + common: https://common-api.pickmall.cn + manager: https://admin-api.pickmall.cn + store: https://store-api.pickmall.cn + + # jwt 细节设定 + jwt-setting: + # token过期时间(分钟) + tokenExpireTime: 60 + + # 使用Spring @Cacheable注解失效时间 + cache: + # 过期时间 单位秒 永久不过期设为-1 + timeout: 1500 + #多线程配置 + thread: + corePoolSize: 5 + maxPoolSize: 50 + queueCapacity: 50 + data: + elasticsearch: + cluster-name: elasticsearch + cluster-nodes: 127.0.0.1:9200 + index: + number-of-replicas: 0 + number-of-shards: 3 + index-prefix: lili + schema: http + # account: + # username: elastic + # password: LiLiShopES + + rocketmq: + promotion-topic: lili_promotion_topic + promotion-group: lili_promotion_group + msg-ext-topic: lili_msg_topic + msg-ext-group: lili_msg_group + goods-topic: lili_goods_topic + goods-group: lili_goods_group + order-topic: lili_order_topic + order-group: lili_order_group + member-topic: lili_member_topic + member-group: lili_member_group + other-topic: lili_other_topic + other-group: lili_other_group + notice-topic: lili_notice_topic + notice-group: lili_notice_group + notice-send-topic: lili_send_notice_topic + notice-send-group: lili_send_notice_group +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: lili_group + send-message-timeout: 30000 + +xxl: + job: + admin: + addresses: http://127.0.0.1:9001/xxl-job-admin + executor: + appname: xxl-job-executor-lilishop + address: + ip: + port: 8891 + logpath: ./xxl-job/executor + logretentiondays: 7 \ No newline at end of file diff --git a/common-api/src/main/resources/slider/images/1.jpg b/common-api/src/main/resources/slider/images/1.jpg new file mode 100644 index 00000000..e44fcf87 Binary files /dev/null and b/common-api/src/main/resources/slider/images/1.jpg differ diff --git a/common-api/src/main/resources/slider/images/10.jpg b/common-api/src/main/resources/slider/images/10.jpg new file mode 100644 index 00000000..219b84b6 Binary files /dev/null and b/common-api/src/main/resources/slider/images/10.jpg differ diff --git a/common-api/src/main/resources/slider/images/11.jpg b/common-api/src/main/resources/slider/images/11.jpg new file mode 100644 index 00000000..e9819e8a Binary files /dev/null and b/common-api/src/main/resources/slider/images/11.jpg differ diff --git a/common-api/src/main/resources/slider/images/12.jpg b/common-api/src/main/resources/slider/images/12.jpg new file mode 100644 index 00000000..d09ddeff Binary files /dev/null and b/common-api/src/main/resources/slider/images/12.jpg differ diff --git a/common-api/src/main/resources/slider/images/13.jpg b/common-api/src/main/resources/slider/images/13.jpg new file mode 100644 index 00000000..9d14f132 Binary files /dev/null and b/common-api/src/main/resources/slider/images/13.jpg differ diff --git a/common-api/src/main/resources/slider/images/14.jpg b/common-api/src/main/resources/slider/images/14.jpg new file mode 100644 index 00000000..8854e630 Binary files /dev/null and b/common-api/src/main/resources/slider/images/14.jpg differ diff --git a/common-api/src/main/resources/slider/images/15.jpg b/common-api/src/main/resources/slider/images/15.jpg new file mode 100644 index 00000000..f2b0c8eb Binary files /dev/null and b/common-api/src/main/resources/slider/images/15.jpg differ diff --git a/common-api/src/main/resources/slider/images/16.jpg b/common-api/src/main/resources/slider/images/16.jpg new file mode 100644 index 00000000..e804854f Binary files /dev/null and b/common-api/src/main/resources/slider/images/16.jpg differ diff --git a/common-api/src/main/resources/slider/images/17.jpg b/common-api/src/main/resources/slider/images/17.jpg new file mode 100644 index 00000000..33f9fdb2 Binary files /dev/null and b/common-api/src/main/resources/slider/images/17.jpg differ diff --git a/common-api/src/main/resources/slider/images/18.jpg b/common-api/src/main/resources/slider/images/18.jpg new file mode 100644 index 00000000..8ce0fbd3 Binary files /dev/null and b/common-api/src/main/resources/slider/images/18.jpg differ diff --git a/common-api/src/main/resources/slider/images/19.jpg b/common-api/src/main/resources/slider/images/19.jpg new file mode 100644 index 00000000..e7ba06c4 Binary files /dev/null and b/common-api/src/main/resources/slider/images/19.jpg differ diff --git a/common-api/src/main/resources/slider/images/2.jpg b/common-api/src/main/resources/slider/images/2.jpg new file mode 100644 index 00000000..a5d2af1d Binary files /dev/null and b/common-api/src/main/resources/slider/images/2.jpg differ diff --git a/common-api/src/main/resources/slider/images/20.jpg b/common-api/src/main/resources/slider/images/20.jpg new file mode 100644 index 00000000..e21cc15f Binary files /dev/null and b/common-api/src/main/resources/slider/images/20.jpg differ diff --git a/common-api/src/main/resources/slider/images/21.jpg b/common-api/src/main/resources/slider/images/21.jpg new file mode 100644 index 00000000..75db3e7f Binary files /dev/null and b/common-api/src/main/resources/slider/images/21.jpg differ diff --git a/common-api/src/main/resources/slider/images/22.jpg b/common-api/src/main/resources/slider/images/22.jpg new file mode 100644 index 00000000..c1d12128 Binary files /dev/null and b/common-api/src/main/resources/slider/images/22.jpg differ diff --git a/common-api/src/main/resources/slider/images/23.jpg b/common-api/src/main/resources/slider/images/23.jpg new file mode 100644 index 00000000..1af98fe8 Binary files /dev/null and b/common-api/src/main/resources/slider/images/23.jpg differ diff --git a/common-api/src/main/resources/slider/images/3.jpg b/common-api/src/main/resources/slider/images/3.jpg new file mode 100644 index 00000000..7f597bca Binary files /dev/null and b/common-api/src/main/resources/slider/images/3.jpg differ diff --git a/common-api/src/main/resources/slider/images/4.jpg b/common-api/src/main/resources/slider/images/4.jpg new file mode 100644 index 00000000..e1516645 Binary files /dev/null and b/common-api/src/main/resources/slider/images/4.jpg differ diff --git a/common-api/src/main/resources/slider/images/5.jpg b/common-api/src/main/resources/slider/images/5.jpg new file mode 100644 index 00000000..731dd315 Binary files /dev/null and b/common-api/src/main/resources/slider/images/5.jpg differ diff --git a/common-api/src/main/resources/slider/images/6.jpg b/common-api/src/main/resources/slider/images/6.jpg new file mode 100644 index 00000000..74859339 Binary files /dev/null and b/common-api/src/main/resources/slider/images/6.jpg differ diff --git a/common-api/src/main/resources/slider/images/7.jpg b/common-api/src/main/resources/slider/images/7.jpg new file mode 100644 index 00000000..5c37b9e5 Binary files /dev/null and b/common-api/src/main/resources/slider/images/7.jpg differ diff --git a/common-api/src/main/resources/slider/images/8.jpg b/common-api/src/main/resources/slider/images/8.jpg new file mode 100644 index 00000000..e307d067 Binary files /dev/null and b/common-api/src/main/resources/slider/images/8.jpg differ diff --git a/common-api/src/main/resources/slider/images/9.jpg b/common-api/src/main/resources/slider/images/9.jpg new file mode 100644 index 00000000..b2b484c2 Binary files /dev/null and b/common-api/src/main/resources/slider/images/9.jpg differ diff --git a/common-api/src/main/resources/slider/slider/1-w.png b/common-api/src/main/resources/slider/slider/1-w.png new file mode 100644 index 00000000..5ebede09 Binary files /dev/null and b/common-api/src/main/resources/slider/slider/1-w.png differ diff --git a/common-api/src/main/resources/slider/slider/2-w.png b/common-api/src/main/resources/slider/slider/2-w.png new file mode 100644 index 00000000..a67b04aa Binary files /dev/null and b/common-api/src/main/resources/slider/slider/2-w.png differ diff --git a/common-api/src/main/resources/slider/slider/3-w.png b/common-api/src/main/resources/slider/slider/3-w.png new file mode 100644 index 00000000..0dab8d3e Binary files /dev/null and b/common-api/src/main/resources/slider/slider/3-w.png differ diff --git a/common-api/src/main/resources/slider/slider/4-w.png b/common-api/src/main/resources/slider/slider/4-w.png new file mode 100644 index 00000000..086b2952 Binary files /dev/null and b/common-api/src/main/resources/slider/slider/4-w.png differ diff --git a/common-api/src/main/resources/slider/slider/5-w.png b/common-api/src/main/resources/slider/slider/5-w.png new file mode 100644 index 00000000..18d80382 Binary files /dev/null and b/common-api/src/main/resources/slider/slider/5-w.png differ diff --git a/common-api/src/main/resources/slider/slider/6-w.png b/common-api/src/main/resources/slider/slider/6-w.png new file mode 100644 index 00000000..80167a30 Binary files /dev/null and b/common-api/src/main/resources/slider/slider/6-w.png differ diff --git a/config/application.yml b/config/application.yml new file mode 100644 index 00000000..d11ce90c --- /dev/null +++ b/config/application.yml @@ -0,0 +1,298 @@ +server: + servlet: + context-path: / + + # 正式部署时候,解开此处配置,防止文件夹被清除导致的文件上传失败问题 + # multipart: + # location: /Users/lifenlong/Desktop/ceshi + tomcat: + uri-encoding: UTF-8 + threads: + min-spare: 50 + max: 1000 + +# 与Spring Boot 2一样,默认情况下,大多数端点都不通过http公开,我们公开了所有端点。对于生产,您应该仔细选择要公开的端点。 +management: + # health: + # elasticsearch: + # enabled: false + # datasource: + # enabled: false + endpoints: + web: + exposure: + include: '*' +spring: + # 要在其中注册的Spring Boot Admin Server的URL。 + boot: + admin: + client: + url: http://192.168.0.116:8000 + # mongodb + data: + mongodb: + host: 192.168.0.116 + port: 27017 + database: lilishop + username: root + password: lilishop + authentication-database: admin + # replica-set-name: mongoreplset + cache: + type: redis + #amqp + # rabbitmq: + # host: 192.168.0.116 + jpa: + # 自动生成表结构 + generate-ddl: true + open-in-view: false + # Redis + redis: + host: 192.168.0.116 + port: 6379 + password: lilishop + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: 20 + # 连接池中的最大空闲连接 默认 8 + max-idle: 10 + # 连接池中的最小空闲连接 默认 8 + min-idle: 8 + # 文件大小上传配置 + servlet: + multipart: + max-file-size: 20MB + max-request-size: 20MB + jackson: + time-zone: GMT+8 + serialization: + #关闭jackson 对json做解析 + fail-on-empty-beans: false + + shardingsphere: + datasource: + # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 + names: default-datasource + default-datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.0.116:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: lilishop + maxActive: 20 + initialSize: 5 + maxWait: 60000 + minIdle: 5 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 + poolPreparedStatements: false + #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 + maxOpenPreparedStatements: -1 + #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,log4j2 + #通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + #合并多个DruidDataSource的监控数据 + useGlobalDataSourceStat: true + loginUsername: druid + loginPassword: druid + # sharding: + # default-data-source-name: default-datasource + # #需要拆分的表,可以设置多个 在 li_order 级别即可 + # tables: + # #需要进行分表的逻辑表名 + # li_order: + # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 + # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} + # table-strategy: + # # 分表策略,根据创建日期 + # standard: + # sharding-column: create_time + # #分表策略 + # precise-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + # #范围查询实现 + # range-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + props: + #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 + sql: + show: true + +# 忽略鉴权url +ignored: + urls: + - /editor-app/** + - /actuator** + - /actuator/** + - /MP_verify_qSyvBPhDsPdxvOhC.txt + - /weixin/** + - /source/** + - /buyer/mini-program/** + - /buyer/cashier/** + - /buyer/pageData/** + - /buyer/article/** + - /buyer/goods/** + - /buyer/category/** + - /buyer/store/** + - /buyer/connect/** + - /buyer/members/** + - /buyer/promotion/pintuan/** + - /buyer/promotion/seckill/** + - /buyer/promotion/pointsGoods/** + - /buyer/memberEvaluation/**/goodsEvaluation + - /buyer/memberEvaluation/**/evaluationNumber + - /store/login/** + - /manager/user/login + - /manager/user/refresh/** + - /druid/** + - /swagger-ui.html + - /doc.html + - /swagger-resources/** + - /swagger/** + - /**/**.js + - /**/**.png + - /**/**.css + - /webjars/** + - /v2/api-docs + - /configuration/ui + - /boot-admin + statics: + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + +# Swagger界面内容配置 +swagger: + title: lili API接口文档 + description: lili Api Documentation + version: 1.0.0 + termsOfServiceUrl: https://pickmall.cn + contact: + name: lili + url: https://pickmall.cn + email: admin@pickmall.com + +# Mybatis-plus +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + #缓存开启 + cache-enabled: true + #日志 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 日志 +logging: + # 输出级别 + level: + cn.lili: info + # org.hibernate: debug + # org.springframework: debug + # org.springframework.data.mongodb.core: debug + file: + # 指定路径 + path: lili-logs + # 最大保存天数 + max-history: 7 + # 每个文件最大大小 + max-size: 5MB +#加密参数 +jasypt: + encryptor: + password: lili + +lili: + system: + isDemoSite: true + statistics: + # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 + onlineMember: 48 + # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 + currentOnlineUpdate: 600 + #qq lbs 申请 + lbs: + key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT + sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 + #域名 + domain: + pc: http://192.168.0.116:8888 + wap: http://192.168.0.116:8888 + seller: http://192.168.0.116:8888 + admin: http://192.168.0.116:8888 + #api地址 + api: + buyer: https://z171l91606.51mypc.cn + base: http://192.168.0.116:8888 + manager: http://192.168.0.116:8888 + seller: http://192.168.0.116:8888 + + # jwt 细节设定 + jwt-setting: + # token过期时间(分钟) + tokenExpireTime: 60 + + # 使用Spring @Cacheable注解失效时间 + cache: + # 过期时间 单位秒 永久不过期设为-1 + timeout: 1500 + #多线程配置 + thread: + corePoolSize: 5 + maxPoolSize: 50 + queueCapacity: 50 + data: + elasticsearch: + cluster-name: elasticsearch + cluster-nodes: 192.168.0.116:9200 + index: + number-of-replicas: 0 + number-of-shards: 3 + index-prefix: lili + schema: http + # account: + # username: elastic + # password: LiLiShopES + + rocketmq: + promotion-topic: lili_promotion_topic + promotion-group: lili_promotion_group + msg-ext-topic: lili_msg_topic + msg-ext-group: lili_msg_group + goods-topic: lili_goods_topic + goods-group: lili_goods_group + order-topic: lili_order_topic + order-group: lili_order_group + member-topic: lili_member_topic + member-group: lili_member_group + other-topic: lili_other_topic + other-group: lili_other_group + notice-topic: lili_notice_topic + notice-group: lili_notice_group + notice-send-topic: lili_send_notice_topic + notice-send-group: lili_send_notice_group +rocketmq: + name-server: 192.168.0.116:9876 + producer: + group: lili_group + send-message-timeout: 30000 + +xxl: + job: + admin: + addresses: http://192.168.0.116:9001/xxl-job-admin + executor: + appname: xxl-job-executor-lilishop + address: + ip: + port: 8891 + logpath: ./xxl-job/executor + logretentiondays: 7 \ No newline at end of file diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/_remote.repositories b/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/_remote.repositories new file mode 100644 index 00000000..945656cd --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/_remote.repositories @@ -0,0 +1,4 @@ +#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. +#Thu Dec 24 11:13:34 CST 2020 +xxl-job-admin-2.3.0-SNAPSHOT.pom>= +xxl-job-admin-2.3.0-SNAPSHOT.jar>= diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/maven-metadata-local.xml b/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/maven-metadata-local.xml new file mode 100644 index 00000000..e4a32a10 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/maven-metadata-local.xml @@ -0,0 +1,24 @@ + + + com.xuxueli + xxl-job-admin + 2.3.0-SNAPSHOT + + + true + + 20201224031334 + + + jar + 2.3.0-SNAPSHOT + 20201224031334 + + + pom + 2.3.0-SNAPSHOT + 20201224031334 + + + + diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/xxl-job-admin-2.3.0-SNAPSHOT.jar b/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/xxl-job-admin-2.3.0-SNAPSHOT.jar new file mode 100644 index 00000000..e6002908 Binary files /dev/null and b/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/xxl-job-admin-2.3.0-SNAPSHOT.jar differ diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/xxl-job-admin-2.3.0-SNAPSHOT.pom b/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/xxl-job-admin-2.3.0-SNAPSHOT.pom new file mode 100644 index 00000000..d8b2f9f9 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job-admin/2.3.0-SNAPSHOT/xxl-job-admin-2.3.0-SNAPSHOT.pom @@ -0,0 +1,113 @@ + + 4.0.0 + + com.xuxueli + xxl-job + 2.3.0-SNAPSHOT + + xxl-job-admin + jar + + + + + org.springframework.boot + spring-boot-starter-parent + ${spring-boot.version} + pom + import + + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + + org.springframework.boot + spring-boot-starter-mail + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot-starter.version} + + + + mysql + mysql-connector-java + ${mysql-connector-java.version} + + + + + com.xuxueli + xxl-job-core + ${project.parent.version} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + com.spotify + docker-maven-plugin + 0.4.13 + + + ${project.artifactId}:${project.version} + ${project.basedir} + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + + + + + + diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-admin/maven-metadata-local.xml b/consumer/maven-repository/com/xuxueli/xxl-job-admin/maven-metadata-local.xml new file mode 100644 index 00000000..6bdd1f8b --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job-admin/maven-metadata-local.xml @@ -0,0 +1,11 @@ + + + com.xuxueli + xxl-job-admin + + + 2.3.0-SNAPSHOT + + 20201224031334 + + diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/_remote.repositories b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/_remote.repositories new file mode 100644 index 00000000..cd11d002 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/_remote.repositories @@ -0,0 +1,4 @@ +#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. +#Thu Dec 24 11:13:30 CST 2020 +xxl-job-core-2.3.0-SNAPSHOT.pom>= +xxl-job-core-2.3.0-SNAPSHOT.jar>= diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/maven-metadata-local.xml b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/maven-metadata-local.xml new file mode 100644 index 00000000..e48ba2f7 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/maven-metadata-local.xml @@ -0,0 +1,24 @@ + + + com.xuxueli + xxl-job-core + 2.3.0-SNAPSHOT + + + true + + 20201224031330 + + + jar + 2.3.0-SNAPSHOT + 20201224031330 + + + pom + 2.3.0-SNAPSHOT + 20201224031330 + + + + diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/resolver-status.properties b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/resolver-status.properties new file mode 100644 index 00000000..7033eb85 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/resolver-status.properties @@ -0,0 +1,6 @@ +#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. +#Thu Dec 24 20:01:24 CST 2020 +maven-metadata-maven-central.xml/@default-maven-central-http\://central.maven.org/maven2/.lastUpdated=1608811283852 +maven-metadata-aliyun.xml.error= +maven-metadata-maven-central.xml.error=Could not transfer metadata com.xuxueli\:xxl-job-core\:2.3.0-SNAPSHOT/maven-metadata.xml from/to maven-central (http\://central.maven.org/maven2/)\: Transfer failed for http\://central.maven.org/maven2/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/maven-metadata.xml +maven-metadata-aliyun.xml.lastUpdated=1608811284126 diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/xxl-job-core-2.3.0-SNAPSHOT.jar b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/xxl-job-core-2.3.0-SNAPSHOT.jar new file mode 100644 index 00000000..68c8afce Binary files /dev/null and b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/xxl-job-core-2.3.0-SNAPSHOT.jar differ diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/xxl-job-core-2.3.0-SNAPSHOT.pom b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/xxl-job-core-2.3.0-SNAPSHOT.pom new file mode 100644 index 00000000..80b0a194 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job-core/2.3.0-SNAPSHOT/xxl-job-core-2.3.0-SNAPSHOT.pom @@ -0,0 +1,64 @@ + + 4.0.0 + + com.xuxueli + xxl-job + 2.3.0-SNAPSHOT + + xxl-job-core + jar + + ${project.artifactId} + A distributed task scheduling framework. + https://www.xuxueli.com/ + + + + + + io.netty + netty-all + ${netty-all.version} + + + com.google.code.gson + gson + ${gson.version} + + + + + + org.codehaus.groovy + groovy + ${groovy.version} + + + + + org.springframework + spring-context + ${spring.version} + provided + + + + + + org.slf4j + slf4j-api + ${slf4j-api.version} + + + + + javax.annotation + javax.annotation-api + ${javax.annotation-api.version} + provided + + + + + \ No newline at end of file diff --git a/consumer/maven-repository/com/xuxueli/xxl-job-core/maven-metadata-local.xml b/consumer/maven-repository/com/xuxueli/xxl-job-core/maven-metadata-local.xml new file mode 100644 index 00000000..23867780 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job-core/maven-metadata-local.xml @@ -0,0 +1,11 @@ + + + com.xuxueli + xxl-job-core + + + 2.3.0-SNAPSHOT + + 20201224031330 + + diff --git a/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/_remote.repositories b/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/_remote.repositories new file mode 100644 index 00000000..0e354116 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/_remote.repositories @@ -0,0 +1,3 @@ +#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. +#Thu Dec 24 11:13:27 CST 2020 +xxl-job-2.3.0-SNAPSHOT.pom>= diff --git a/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/maven-metadata-local.xml b/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/maven-metadata-local.xml new file mode 100644 index 00000000..c5768c05 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/maven-metadata-local.xml @@ -0,0 +1,19 @@ + + + com.xuxueli + xxl-job + 2.3.0-SNAPSHOT + + + true + + 20201224031327 + + + pom + 2.3.0-SNAPSHOT + 20201224031327 + + + + diff --git a/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/resolver-status.properties b/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/resolver-status.properties new file mode 100644 index 00000000..d30bfcd2 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/resolver-status.properties @@ -0,0 +1,6 @@ +#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. +#Thu Dec 24 20:01:24 CST 2020 +maven-metadata-maven-central.xml/@default-maven-central-http\://central.maven.org/maven2/.lastUpdated=1608811284134 +maven-metadata-aliyun.xml.error= +maven-metadata-maven-central.xml.error=Could not transfer metadata com.xuxueli\:xxl-job\:2.3.0-SNAPSHOT/maven-metadata.xml from/to maven-central (http\://central.maven.org/maven2/)\: Transfer failed for http\://central.maven.org/maven2/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/maven-metadata.xml +maven-metadata-aliyun.xml.lastUpdated=1608811284273 diff --git a/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/xxl-job-2.3.0-SNAPSHOT.pom b/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/xxl-job-2.3.0-SNAPSHOT.pom new file mode 100644 index 00000000..bb84f677 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job/2.3.0-SNAPSHOT/xxl-job-2.3.0-SNAPSHOT.pom @@ -0,0 +1,145 @@ + + 4.0.0 + com.xuxueli + xxl-job + 2.3.0-SNAPSHOT + pom + + ${project.artifactId} + A distributed task scheduling framework. + https://www.xuxueli.com/ + + + xxl-job-core + xxl-job-admin + xxl-job-executor-samples + + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + true + + 4.1.54.Final + 2.8.6 + + 5.3.1 + 2.4.0 + + 2.1.4 + 8.0.22 + + 1.7.30 + 5.7.0 + 1.3.2 + + 3.0.7 + + 3.2.1 + 3.2.0 + 1.6 + 3.3.1 + + + + + + + + + + + GNU General Public License version 3 + https://opensource.org/licenses/GPL-3.0 + + + + + master + https://github.com/xuxueli/xxl-job.git + scm:git:https://github.com/xuxueli/xxl-job.git + scm:git:git@github.com:xuxueli/xxl-job.git + + + + XXL + xuxueli + 931591021@qq.com + https://github.com/xuxueli + + + + + + + release + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + package + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + package + + jar + + + none + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + false + + + + verify + + sign + + + + + + + + + oss + https://oss.sonatype.org/content/repositories/snapshots/ + + + oss + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + \ No newline at end of file diff --git a/consumer/maven-repository/com/xuxueli/xxl-job/maven-metadata-local.xml b/consumer/maven-repository/com/xuxueli/xxl-job/maven-metadata-local.xml new file mode 100644 index 00000000..dc6069c6 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-job/maven-metadata-local.xml @@ -0,0 +1,11 @@ + + + com.xuxueli + xxl-job + + + 2.3.0-SNAPSHOT + + 20201224031327 + + diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/_remote.repositories b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/_remote.repositories new file mode 100644 index 00000000..d1e6937e --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/_remote.repositories @@ -0,0 +1,5 @@ +#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. +#Sat Apr 04 19:56:05 CST 2020 +xxl-rpc-core-1.2.1.pom>aliyun= +xxl-rpc-core-1.2.1-sources.jar>aliyun= +xxl-rpc-core-1.2.1.jar>aliyun= diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1-sources.jar b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1-sources.jar new file mode 100644 index 00000000..f233be1e Binary files /dev/null and b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1-sources.jar differ diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1-sources.jar.lastUpdated b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1-sources.jar.lastUpdated new file mode 100644 index 00000000..0b1e7d6f --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1-sources.jar.lastUpdated @@ -0,0 +1,5 @@ +#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. +#Sat Apr 04 19:56:05 CST 2020 +http\://central.maven.org/maven2/.error=Could not transfer artifact com.xuxueli\:xxl-rpc-core\:jar\:sources\:1.2.1 from/to maven-central (http\://central.maven.org/maven2/)\: central.maven.org +@default-maven-central-http\://central.maven.org/maven2/.lastUpdated=1586001364931 +http\://maven.aliyun.com/nexus/content/groups/public/.lastUpdated=1586001365245 diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1-sources.jar.sha1 b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1-sources.jar.sha1 new file mode 100644 index 00000000..804426ca --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1-sources.jar.sha1 @@ -0,0 +1,2 @@ + +33b53883ecf6e77b8b4eafe3506e213e83b2c80a \ No newline at end of file diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.jar b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.jar new file mode 100644 index 00000000..8190b5c0 Binary files /dev/null and b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.jar differ diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.jar.sha1 b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.jar.sha1 new file mode 100644 index 00000000..b02eee7a --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.jar.sha1 @@ -0,0 +1 @@ +27f8d667b1ede36b219f36af75b493b0e6143288 \ No newline at end of file diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.pom b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.pom new file mode 100644 index 00000000..5c5d1bdb --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.pom @@ -0,0 +1,143 @@ + + 4.0.0 + + com.xuxueli + xxl-rpc + 1.2.1 + + xxl-rpc-core + jar + + ${project.artifactId} + A high performance, distributed RPC framework. + http://www.xuxueli.com/ + + + + + + + + org.slf4j + slf4j-api + ${slf4j-api.version} + + + org.slf4j + slf4j-log4j12 + ${slf4j-api.version} + test + + + + + + + + com.caucho + hessian + ${hessian.version} + + + + + io.protostuff + protostuff-core + ${protostuff.version} + provided + + + io.protostuff + protostuff-runtime + ${protostuff.version} + provided + + + org.objenesis + objenesis + ${objenesis.version} + provided + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + provided + + + + + + + + org.apache.zookeeper + zookeeper + ${zookeeper.version} + provided + + + + + + + + org.eclipse.jetty + jetty-server + ${jetty-server.version} + + + org.eclipse.jetty + jetty-client + ${jetty-server.version} + + + + + io.netty + netty-all + ${netty.version} + provided + + + + org.apache.mina + mina-core + 2.0.19 + provided + + + + org.apache.commons + commons-pool2 + ${commons-pool2.version} + provided + + + + + + + org.springframework + spring-beans + ${spring.version} + provided + + + org.springframework + spring-core + ${spring.version} + provided + + + org.springframework + spring-context + ${spring.version} + provided + + + + + \ No newline at end of file diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.pom.sha1 b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.pom.sha1 new file mode 100644 index 00000000..18eb35dc --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-rpc-core/1.2.1/xxl-rpc-core-1.2.1.pom.sha1 @@ -0,0 +1,2 @@ + +bed5b85e402f8fccf7109a3d5944f45dd5cae342 \ No newline at end of file diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc/1.2.1/_remote.repositories b/consumer/maven-repository/com/xuxueli/xxl-rpc/1.2.1/_remote.repositories new file mode 100644 index 00000000..71739fc4 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-rpc/1.2.1/_remote.repositories @@ -0,0 +1,3 @@ +#NOTE: This is a Maven Resolver internal implementation file, its format can be changed without prior notice. +#Mon Mar 02 16:06:40 CST 2020 +xxl-rpc-1.2.1.pom>aliyun= diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc/1.2.1/xxl-rpc-1.2.1.pom b/consumer/maven-repository/com/xuxueli/xxl-rpc/1.2.1/xxl-rpc-1.2.1.pom new file mode 100644 index 00000000..5f8afc32 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-rpc/1.2.1/xxl-rpc-1.2.1.pom @@ -0,0 +1,151 @@ + + 4.0.0 + com.xuxueli + xxl-rpc + 1.2.1 + pom + + ${project.artifactId} + A high performance, distributed RPC framework. + http://www.xuxueli.com/ + + + xxl-rpc-core + + + + + UTF-8 + UTF-8 + UTF-8 + 1.7 + 1.7 + + true + + + 3.1.0 + 2.3.3 + + 1.7.25 + 4.12 + + + 4.1.29.Final + 2.0.19 + 9.2.26.v20180806 + + 2.6.0 + + + 4.0.51 + + 1.6.0 + 2.6 + + 2.9.6 + + + 3.4.13 + + + 4.3.19.RELEASE + 1.5.16.RELEASE + + + + + + + + + + + GNU General Public License version 3 + https://opensource.org/licenses/GPL-3.0 + + + + + master + https://github.com/xuxueli/xxl-rpc.git + scm:git:https://github.com/xuxueli/xxl-rpc.git + scm:git:git@github.com:xuxueli/xxl-rpc.git + + + + XXL + xuxueli + 931591021@qq.com + https://github.com/xuxueli + + + + + + + release + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + package + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.0 + + + package + + jar + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + false + + + + verify + + sign + + + + + + + + + oss + https://oss.sonatype.org/content/repositories/snapshots/ + + + oss + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + \ No newline at end of file diff --git a/consumer/maven-repository/com/xuxueli/xxl-rpc/1.2.1/xxl-rpc-1.2.1.pom.sha1 b/consumer/maven-repository/com/xuxueli/xxl-rpc/1.2.1/xxl-rpc-1.2.1.pom.sha1 new file mode 100644 index 00000000..2ebdd727 --- /dev/null +++ b/consumer/maven-repository/com/xuxueli/xxl-rpc/1.2.1/xxl-rpc-1.2.1.pom.sha1 @@ -0,0 +1 @@ +370f4c4f20b218942fba34d939cbc625025e1e30 \ No newline at end of file diff --git a/consumer/pom.xml b/consumer/pom.xml new file mode 100644 index 00000000..dd25a81b --- /dev/null +++ b/consumer/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + consumer + + cn.lili + lili-shop-parent + 1.0.1 + + + + + cn.lili + framework + 1.0.1 + + + + com.xuxueli + xxl-job-core + 2.2.0 + + + + + + maven-repository + file:///${project.basedir}/maven-repository + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/consumer/src/main/java/cn/lili/ConsumerApplication.java b/consumer/src/main/java/cn/lili/ConsumerApplication.java new file mode 100644 index 00000000..afa05d8e --- /dev/null +++ b/consumer/src/main/java/cn/lili/ConsumerApplication.java @@ -0,0 +1,20 @@ +package cn.lili; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 消费者 + * + * @author Chopper + * @date 2020/11/16 10:03 下午 + */ +@SpringBootApplication +public class ConsumerApplication { + + public static void main(String[] args) { + System.setProperty("es.set.netty.runtime.available.processors", "false"); + SpringApplication.run(ConsumerApplication.class, args); + } + +} \ No newline at end of file diff --git a/consumer/src/main/java/cn/lili/event/AfterSaleStatusChangeEvent.java b/consumer/src/main/java/cn/lili/event/AfterSaleStatusChangeEvent.java new file mode 100644 index 00000000..9524e265 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/AfterSaleStatusChangeEvent.java @@ -0,0 +1,20 @@ +package cn.lili.event; + + +import cn.lili.modules.order.order.entity.dos.AfterSale; + +/** + * 售后单改变状态 + * + * @author Chopper + * @date 2020/11/17 7:13 下午 + */ +public interface AfterSaleStatusChangeEvent { + + /** + * 售后单改变 + * + * @param afterSale 售后 + */ + void afterSaleStatusChange(AfterSale afterSale); +} diff --git a/consumer/src/main/java/cn/lili/event/GoodsCommentCompleteEvent.java b/consumer/src/main/java/cn/lili/event/GoodsCommentCompleteEvent.java new file mode 100644 index 00000000..4ec0abfe --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/GoodsCommentCompleteEvent.java @@ -0,0 +1,18 @@ +package cn.lili.event; + +import cn.lili.modules.member.entity.dos.MemberEvaluation; + +/** + * 订单状态改变事件 + * + * @author Chopper + * @date 2020/11/17 7:13 下午 + */ +public interface GoodsCommentCompleteEvent { + + /** + * 商品评价 + * @param memberEvaluation 会员评价 + */ + void goodsComment(MemberEvaluation memberEvaluation); +} diff --git a/consumer/src/main/java/cn/lili/event/MemberPointChangeEvent.java b/consumer/src/main/java/cn/lili/event/MemberPointChangeEvent.java new file mode 100644 index 00000000..3a87ddfa --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/MemberPointChangeEvent.java @@ -0,0 +1,19 @@ +package cn.lili.event; + +import cn.lili.modules.member.entity.dto.MemberPointMessage; + +/** + * 会员积分改变消息 + * + * @author Chopper + * @date 2020/11/17 7:13 下午 + */ +public interface MemberPointChangeEvent { + + /** + * 会员积分改变消息 + * + * @param memberPointMessage 会员积分消息 + */ + void memberPointChange(MemberPointMessage memberPointMessage); +} diff --git a/consumer/src/main/java/cn/lili/event/MemberRegisterEvent.java b/consumer/src/main/java/cn/lili/event/MemberRegisterEvent.java new file mode 100644 index 00000000..e9a32947 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/MemberRegisterEvent.java @@ -0,0 +1,19 @@ +package cn.lili.event; + +import cn.lili.modules.member.entity.dos.Member; + +/** + * 会员注册消息 + * + * @author Chopper + * @date 2020/11/17 7:13 下午 + */ +public interface MemberRegisterEvent { + + /** + * 会员登录 + * + * @param member 会员 + */ + void memberRegister(Member member); +} diff --git a/consumer/src/main/java/cn/lili/event/MemberWithdrawalEvent.java b/consumer/src/main/java/cn/lili/event/MemberWithdrawalEvent.java new file mode 100644 index 00000000..2738692a --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/MemberWithdrawalEvent.java @@ -0,0 +1,19 @@ +package cn.lili.event; + +import cn.lili.modules.member.entity.dto.MemberWithdrawalMessage; + +/** + * 会员提现消息 + * + * @author Chopper + * @date 2020/11/17 7:13 下午 + */ +public interface MemberWithdrawalEvent { + + /** + * 会员提现 + * + * @param memberWithdrawalMessage 提现对象 + */ + void memberWithdrawal(MemberWithdrawalMessage memberWithdrawalMessage); +} diff --git a/consumer/src/main/java/cn/lili/event/OrderStatusChangeEvent.java b/consumer/src/main/java/cn/lili/event/OrderStatusChangeEvent.java new file mode 100644 index 00000000..f1598758 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/OrderStatusChangeEvent.java @@ -0,0 +1,18 @@ +package cn.lili.event; + +import cn.lili.modules.order.order.entity.dto.OrderMessage; + +/** + * 订单状态改变事件 + * + * @author Chopper + * @date 2020/11/17 7:13 下午 + */ +public interface OrderStatusChangeEvent { + + /** + * 订单改变 + * @param orderMessage 订单消息 + */ + void orderChange(OrderMessage orderMessage); +} diff --git a/consumer/src/main/java/cn/lili/event/TradeEvent.java b/consumer/src/main/java/cn/lili/event/TradeEvent.java new file mode 100644 index 00000000..87316758 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/TradeEvent.java @@ -0,0 +1,20 @@ +package cn.lili.event; + +import cn.lili.modules.order.cart.entity.dto.TradeDTO; + +/** + * 订单创建消息 + * + * @author Chopper + * @date 2021/2/2 15:15 + */ +public interface TradeEvent { + + /** + * 订单创建 + * + * @param tradeDTO 交易 + */ + void orderCreate(TradeDTO tradeDTO); + +} diff --git a/consumer/src/main/java/cn/lili/event/impl/DistributionOrderExecute.java b/consumer/src/main/java/cn/lili/event/impl/DistributionOrderExecute.java new file mode 100644 index 00000000..b4eb9341 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/DistributionOrderExecute.java @@ -0,0 +1,71 @@ +package cn.lili.event.impl; + +import cn.hutool.core.date.DateTime; +import cn.lili.event.AfterSaleStatusChangeEvent; +import cn.lili.event.OrderStatusChangeEvent; +import cn.lili.modules.distribution.entity.dos.DistributionOrder; +import cn.lili.modules.distribution.entity.enums.DistributionOrderStatusEnum; +import cn.lili.modules.distribution.mapper.DistributionOrderMapper; +import cn.lili.modules.distribution.service.DistributionOrderService; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import cn.lili.modules.order.trade.entity.enums.AfterSaleStatusEnum; +import cn.lili.timetask.handler.EveryDayExecute; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 分销订单入库 + * + * @author Chopper + * @date 2020-07-03 11:20 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionOrderExecute implements OrderStatusChangeEvent, EveryDayExecute, AfterSaleStatusChangeEvent { + + //分销订单 + private final DistributionOrderService distributionOrderService; + //分销订单持久层 + private final DistributionOrderMapper distributionOrderMapper; + + + @Override + public void orderChange(OrderMessage orderMessage) { + + switch (orderMessage.getNewStatus()) { + case PAID: { + //记录分销订单 + distributionOrderService.payOrder(orderMessage.getOrderSn()); + break; + } + case CANCELLED: { + //修改分销订单状态 + distributionOrderService.cancelOrder(orderMessage.getOrderSn()); + } + break; + } + } + + @Override + public void execute() { + //计算分销提佣 + distributionOrderMapper.rebate(DistributionOrderStatusEnum.WAIT_BILL.name(), new DateTime()); + + //修改分销订单状态 + distributionOrderService.update(new LambdaUpdateWrapper() + .eq(DistributionOrder::getDistributionOrderStatus, DistributionOrderStatusEnum.WAIT_BILL.name()) + .le(DistributionOrder::getSettleCycle, new DateTime()) + .set(DistributionOrder::getDistributionOrderStatus, DistributionOrderStatusEnum.WAIT_CASH.name())); + } + + @Override + public void afterSaleStatusChange(AfterSale afterSale) { + if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.COMPLETE.name())) { + distributionOrderService.refundOrder(afterSale.getSn()); + } + } + +} diff --git a/consumer/src/main/java/cn/lili/event/impl/GoodsSkuExecute.java b/consumer/src/main/java/cn/lili/event/impl/GoodsSkuExecute.java new file mode 100644 index 00000000..6d31a2d0 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/GoodsSkuExecute.java @@ -0,0 +1,30 @@ +package cn.lili.event.impl; + + +import cn.lili.event.GoodsCommentCompleteEvent; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.member.entity.dos.MemberEvaluation; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 商品SKU变化 + * + * @author Chopper + * @date 2020-07-03 11:20 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsSkuExecute implements GoodsCommentCompleteEvent { + + //商品 + private final GoodsSkuService goodsSkuService; + + + @Override + public void goodsComment(MemberEvaluation memberEvaluation) { + goodsSkuService.updateGoodsSkuCommentNum(memberEvaluation.getSkuId()); + } +} diff --git a/consumer/src/main/java/cn/lili/event/impl/MemberPointExecute.java b/consumer/src/main/java/cn/lili/event/impl/MemberPointExecute.java new file mode 100644 index 00000000..a563978c --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/MemberPointExecute.java @@ -0,0 +1,51 @@ +package cn.lili.event.impl; + + +import cn.lili.event.GoodsCommentCompleteEvent; +import cn.lili.event.MemberRegisterEvent; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.member.service.MemberWalletService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.PointSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.google.gson.Gson; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 会员积分 + * + * @author Chopper + * @date 2020-07-03 11:20 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberPointExecute implements MemberRegisterEvent, GoodsCommentCompleteEvent { + + //配置 + private final SettingService settingService; + //会员 + private final MemberService memberService; + + @Override + public void memberRegister(Member member) { + //获取签到积分赠送设置 + Setting setting = settingService.get(SettingEnum.POINT_SETTING.name()); + PointSetting pointSetting = new Gson().fromJson(setting.getSettingValue(), PointSetting.class); + //赠送会员积分 + memberService.updateMemberPoint(Long.valueOf(pointSetting.getRegister().longValue()), 1, member.getId(), "会员注册,赠送积分" + pointSetting.getRegister() + "分"); + } + + @Override + public void goodsComment(MemberEvaluation memberEvaluation) { + //获取签到积分赠送设置 + Setting setting = settingService.get(SettingEnum.POINT_SETTING.name()); + PointSetting pointSetting = new Gson().fromJson(setting.getSettingValue(), PointSetting.class); + //赠送会员积分 + memberService.updateMemberPoint(Long.valueOf(pointSetting.getComment().longValue()), 1, memberEvaluation.getMemberId(), "会员评价,赠送积分" + pointSetting.getComment() + "分"); + } +} diff --git a/consumer/src/main/java/cn/lili/event/impl/MemberWalletExecute.java b/consumer/src/main/java/cn/lili/event/impl/MemberWalletExecute.java new file mode 100644 index 00000000..e2499f9d --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/MemberWalletExecute.java @@ -0,0 +1,27 @@ +package cn.lili.event.impl; + + +import cn.lili.event.MemberRegisterEvent; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberWalletService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 会员钱包创建 + * + * @author Chopper + * @date 2020-07-03 11:20 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberWalletExecute implements MemberRegisterEvent { + + private final MemberWalletService memberWalletService; + + @Override + public void memberRegister(Member member) { + memberWalletService.save(member.getId(),member.getUsername()); + } +} diff --git a/consumer/src/main/java/cn/lili/event/impl/NoticeMessageExecute.java b/consumer/src/main/java/cn/lili/event/impl/NoticeMessageExecute.java new file mode 100644 index 00000000..e0ab8288 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/NoticeMessageExecute.java @@ -0,0 +1,196 @@ +package cn.lili.event.impl; + +import cn.lili.event.*; +import cn.lili.modules.member.entity.dto.MemberPointMessage; +import cn.lili.modules.member.entity.dto.MemberWithdrawalMessage; +import cn.lili.modules.member.entity.enums.MemberWithdrawalDestinationEnum; +import cn.lili.modules.message.entity.dto.NoticeMessageDTO; +import cn.lili.modules.message.entity.enums.NoticeMessageNodeEnum; +import cn.lili.modules.message.entity.enums.NoticeMessageParameterEnum; +import cn.lili.modules.message.service.NoticeMessageService; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import cn.lili.modules.order.order.entity.enums.OrderTypeEnum; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.order.trade.entity.enums.AfterSaleStatusEnum; +import cn.lili.modules.order.trade.entity.enums.AfterSaleTypeEnum; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + + +/** + * 通知类消息实现 + * + * @author Chopper + * @date 2020-07-03 11:20 + **/ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class NoticeMessageExecute implements TradeEvent, OrderStatusChangeEvent, AfterSaleStatusChangeEvent, MemberPointChangeEvent, MemberWithdrawalEvent { + + private final NoticeMessageService noticeMessageService; + + private final OrderService orderService; + + + @Override + public void orderCreate(TradeDTO tradeDTO) { + //订单创建发送订单创建站内信息 + NoticeMessageDTO noticeMessageDTO = new NoticeMessageDTO(); + noticeMessageDTO.setMemberId(tradeDTO.getMemberId()); + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.ORDER_CREATE_SUCCESS); + Map params = new HashMap<>(); + params.put("goods", tradeDTO.getSkuList().get(0).getGoodsSku().getGoodsName()); + noticeMessageDTO.setParameter(params); + //保存站内信 + noticeMessageService.noticeMessage(noticeMessageDTO); + } + + @Override + public void orderChange(OrderMessage orderMessage) { + //查询订单信息 + OrderDetailVO orderDetailVO = orderService.queryDetail(orderMessage.getOrderSn()); + NoticeMessageDTO noticeMessageDTO = new NoticeMessageDTO(); + //如果订单状态不为空 + if (orderDetailVO != null) { + Map params = new HashMap<>(); + switch (orderMessage.getNewStatus()){ + //如果订单新的状态为已取消 则发送取消订单站内信 + case CANCELLED: + params.put(NoticeMessageParameterEnum.CANCEL_REASON.getType(), orderDetailVO.getOrder().getCancelReason()); + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.ORDER_CANCEL_SUCCESS); + break; + //如果订单新的状态为已经支付,则发送支付成功站内信 + case PAID: + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.ORDER_PAY_SUCCESS); + break; + //如果订单新的状态为已发货,则发送已发货站内信 + case DELIVERED: + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.ORDER_DELIVER); + break; + //如果订单新的状态为已完成,则发送已完成站内信 + case COMPLETED: + //订单完成消息 + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.ORDER_COMPLETE); + //订单完成也可以进行评价,所以要有评价消息 + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.ORDER_EVALUATION); + break; + //如果是拼团订单,发送拼团成功消息 + case UNDELIVERED: + if(orderDetailVO.getOrder().getOrderType().equals(OrderTypeEnum.PINTUAN.name())){ + //拼团成功消息 + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.PINTUAN_SUCCESS); + } + break; + default: + break; + } + noticeMessageDTO.setMemberId(orderDetailVO.getOrder().getMemberId()); + //添加站内信参数 + params.put(NoticeMessageParameterEnum.GOODS.getType(), orderDetailVO.getOrderItems().get(0).getGoodsName()); + noticeMessageDTO.setParameter(params); + //保存站内信 + noticeMessageService.noticeMessage(noticeMessageDTO); + } + } + + @Override + public void afterSaleStatusChange(AfterSale afterSale) { + NoticeMessageDTO noticeMessageDTO = new NoticeMessageDTO(); + noticeMessageDTO.setMemberId(afterSale.getMemberId()); + Map params = new HashMap<>(); + params.put("goods", afterSale.getGoodsName()); + params.put("refuse", afterSale.getAuditRemark()); + noticeMessageDTO.setParameter(params); + //如果售后单是申请中 则发送申请中站内信 + if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.APPLY.name())) { + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.AFTER_SALE_CREATE_SUCCESS); + } + //售后审核同意切退货站内信通知 + else if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.PASS.name()) && afterSale.getServiceType().equals(AfterSaleTypeEnum.RETURN_GOODS.name())) { + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.RETURN_GOODS_PASS); + } + //售后审核拒绝且退货站内信通知 + else if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.REFUSE.name()) && afterSale.getServiceType().equals(AfterSaleTypeEnum.RETURN_GOODS.name())) { + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.RETURN_GOODS_REFUSE); + } + //售后审核同意切退款站内信通知 + else if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.PASS.name()) && afterSale.getServiceType().equals(AfterSaleTypeEnum.RETURN_MONEY.name())) { + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.RETURN_MONEY_PASS); + } + //售后审核拒绝且退款站内信通知 + else if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.REFUSE.name()) && afterSale.getServiceType().equals(AfterSaleTypeEnum.RETURN_MONEY.name())) { + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.RETURN_MONEY_REFUSE); + } + //售后商家确认收货站内信通知 + else if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.SELLER_CONFIRM.name())) { + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.AFTER_SALE_ROG_PASS); + } + //退货物品拒收站内信通知 + else if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.SELLER_TERMINATION.name())) { + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.AFTER_SALE_ROG_REFUSE); + } + //售后完成通知 + else if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.COMPLETE.name())) { + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.AFTER_SALE_COMPLETE); + } + //保存站内信 + if (noticeMessageDTO.getNoticeMessageNodeEnum() != null) { + noticeMessageService.noticeMessage(noticeMessageDTO); + } + + } + + @Override + public void memberPointChange(MemberPointMessage memberPointMessage) { + //组织站内信参数 + NoticeMessageDTO noticeMessageDTO = new NoticeMessageDTO(); + noticeMessageDTO.setMemberId(memberPointMessage.getMemberId()); + Map params = new HashMap<>(); + if (memberPointMessage.getType().equals(1)) { + params.put("expenditure_points", "0"); + params.put("income_points", memberPointMessage.getPoint().toString()); + } else { + params.put("expenditure_points", memberPointMessage.getPoint().toString()); + params.put("income_points", "0"); + } + noticeMessageDTO.setParameter(params); + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.POINT_CHANGE); + //发送站内通知信息 + noticeMessageService.noticeMessage(noticeMessageDTO); + } + + + @Override + public void memberWithdrawal(MemberWithdrawalMessage memberWithdrawalMessage) { + + //如果提现到余额 + if (memberWithdrawalMessage.getDestination().equals(MemberWithdrawalDestinationEnum.WALLET.name())) { + + //组织参数 + NoticeMessageDTO noticeMessageDTO = new NoticeMessageDTO(); + noticeMessageDTO.setMemberId(memberWithdrawalMessage.getMemberId()); + Map params = new HashMap<>(); + params.put("income", memberWithdrawalMessage.getPrice().toString()); + noticeMessageDTO.setParameter(params); + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.WALLET_WITHDRAWAL_SUCCESS); + //发送提现申请成功消息 + noticeMessageService.noticeMessage(noticeMessageDTO); + + params.put("income", memberWithdrawalMessage.getPrice().toString()); + params.put("expenditure", "0"); + noticeMessageDTO.setNoticeMessageNodeEnum(NoticeMessageNodeEnum.WALLET_CHANGE); + noticeMessageDTO.setParameter(params); + //发送余额变动消息 + noticeMessageService.noticeMessage(noticeMessageDTO); + } + + + } +} diff --git a/consumer/src/main/java/cn/lili/event/impl/OrderCreateReceiptExecute.java b/consumer/src/main/java/cn/lili/event/impl/OrderCreateReceiptExecute.java new file mode 100644 index 00000000..138c217a --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/OrderCreateReceiptExecute.java @@ -0,0 +1,58 @@ +package cn.lili.event.impl; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.utils.BeanUtil; +import cn.lili.event.TradeEvent; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.order.entity.dos.Receipt; +import cn.lili.modules.order.order.entity.vo.OrderVO; +import cn.lili.modules.order.order.entity.vo.ReceiptVO; +import cn.lili.modules.order.order.service.ReceiptService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * 订单创建发票相关处理 + * + * @author Chopper + * @date 2020-07-03 11:20 + **/ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderCreateReceiptExecute implements TradeEvent { + + private final ReceiptService receiptService; + + @Override + public void orderCreate(TradeDTO tradeDTO) { + //根据交易sn查询订单信息 + List orderList = tradeDTO.getOrderVO(); + //获取发票信息 + ReceiptVO receiptVO = tradeDTO.getReceiptVO(); + //如果需要获取发票则保存发票信息 + if (tradeDTO.getNeedReceipt()) { + if (orderList.size() > 0) { + List receipts = new ArrayList<>(); + for (OrderVO orderVO : orderList) { + Receipt receipt = new Receipt(); + BeanUtil.copyProperties(receiptVO, receipt); + receipt.setMemberId(orderVO.getMemberId()); + receipt.setMemberName(orderVO.getMemberName()); + receipt.setStoreId(orderVO.getStoreId()); + receipt.setStoreName(orderVO.getStoreName()); + receipt.setOrderSn(orderVO.getSn()); + receipt.setReceiptDetail(JSONUtil.toJsonStr(orderVO.getOrderItems())); + receipt.setReceiptPrice(orderVO.getFlowPrice()); + receipt.setReceiptStatus(0); + receipts.add(receipt); + } + //保存发票 + receiptService.saveBatch(receipts); + } + } + } +} diff --git a/consumer/src/main/java/cn/lili/event/impl/PaymentExecute.java b/consumer/src/main/java/cn/lili/event/impl/PaymentExecute.java new file mode 100644 index 00000000..2c4cd46d --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/PaymentExecute.java @@ -0,0 +1,80 @@ +package cn.lili.event.impl; + +import cn.lili.common.utils.SnowFlake; +import cn.lili.common.utils.SpringContextUtil; +import cn.lili.event.OrderStatusChangeEvent; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.kit.Payment; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.payment.service.PaymentService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 支付 + * + * @author Chopper + * @date 2021-03-13 16:58 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PaymentExecute implements OrderStatusChangeEvent { + + //支付日志 + private final PaymentService paymentService; + //订单 + private final OrderService orderService; + + @Override + public void orderChange(OrderMessage orderMessage) { + + switch (orderMessage.getNewStatus()) { + case CANCELLED: + Order order = orderService.getBySn(orderMessage.getOrderSn()); + //未付款不做处理 直接返回 + if (order.getPayStatus() == PayStatusEnum.UNPAID.name()) { + return; + } + + PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(order.getPaymentMethod()); + //进行退款操作 + switch (paymentMethodEnum) { + case WALLET: + case ALIPAY: + case WECHAT: + //获取支付方式 + Payment payment = + (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin()); + + RefundLog refundLog = RefundLog.builder() + .isRefund(false) + .totalAmount(order.getFlowPrice()) + .payPrice(order.getFlowPrice()) + .memberId(order.getMemberId()) + .paymentName(order.getPaymentMethod()) + .afterSaleNo("订单取消") + .orderSn(order.getSn()) + .paymentReceivableNo(order.getReceivableNo()) + .outOrderNo("AF" + SnowFlake.getIdStr()) + .outOrderNo("AF" + SnowFlake.getIdStr()) + .refundReason("订单取消") + .build(); + payment.cancel(refundLog); + break; + case BANK_TRANSFER: + break; + } + default: + break; + } + + + } + + +} diff --git a/consumer/src/main/java/cn/lili/event/impl/StockUpdateExecute.java b/consumer/src/main/java/cn/lili/event/impl/StockUpdateExecute.java new file mode 100644 index 00000000..0a1e7359 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/StockUpdateExecute.java @@ -0,0 +1,191 @@ +package cn.lili.event.impl; + +import cn.lili.common.cache.Cache; +import cn.lili.event.OrderStatusChangeEvent; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * 库存扣减,他表示了订单状态是否出库成功 + * + * @author Chopper + * @date 2020-07-03 11:20 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StockUpdateExecute implements OrderStatusChangeEvent { + + //Redis + private final StringRedisTemplate stringRedisTemplate; + private final DefaultRedisScript quantityScript; + //订单 + private final OrderService orderService; + //规格商品 + private final GoodsSkuService goodsSkuService; + //促销商品 + private final PromotionGoodsService promotionGoodsService; + //缓存 + private final Cache cache; + + @Override + public void orderChange(OrderMessage orderMessage) { + + switch (orderMessage.getNewStatus()) { + case PAID: { + + OrderDetailVO order = orderService.queryDetail(orderMessage.getOrderSn()); + //库存key 和 扣减数量 + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + for (OrderItem orderItem : order.getOrderItems()) { + keys.add(GoodsSkuService.getStockCacheKey(orderItem.getSkuId())); + int i = -orderItem.getNum(); + values.add(Integer.toString(i)); + setPromotionStock(keys, values, orderItem); + } + //库存扣除结果 + Boolean skuResult = stringRedisTemplate.execute(quantityScript, keys, values.toArray()); + //如果库存扣减都成功,则记录成交订单 + if (Boolean.TRUE.equals(skuResult)) { + //库存确认之后对结构处理 + orderService.afterOrderConfirm(orderMessage.getOrderSn()); + //成功之后,同步库存 + synchroDB(order); + } else { + //失败之后取消订单 + this.errorOrder(orderMessage.getOrderSn()); + } + break; + } + case CANCELLED: { + + OrderDetailVO order = orderService.queryDetail(orderMessage.getOrderSn()); + if (order.getOrder().getPayStatus().equals(PayStatusEnum.PAID.name())) { + for (OrderItem orderItem : order.getOrderItems()) { + if (PromotionTypeEnum.haveStock(orderItem.getPromotionType())) { + PromotionTypeEnum promotionTypeEnum = PromotionTypeEnum.valueOf(orderItem.getPromotionType()); + Integer goodsPromotionOriginStock = promotionGoodsService.getPromotionGoodsStock(promotionTypeEnum, orderItem.getPromotionId(), orderItem.getSkuId()); + int goodsPromotionStock = goodsPromotionOriginStock + orderItem.getNum(); + String promotionGoodsStockCacheKey = PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, orderItem.getPromotionId(), orderItem.getSkuId()); + stringRedisTemplate.opsForValue().set(promotionGoodsStockCacheKey, Integer.toString(goodsPromotionStock)); + } + String stockCacheKey = GoodsSkuService.getStockCacheKey(orderItem.getSkuId()); + Integer goodsOriginStock = goodsSkuService.getStock(orderItem.getSkuId()); + int goodsStock = goodsOriginStock + orderItem.getNum(); + stringRedisTemplate.opsForValue().set(stockCacheKey, Integer.toString(goodsStock)); + } + } + break; + } + default: + break; + } + } + + /** + * 订单出库失败 + * + * @param orderSn 失败入库订单信息 + */ + private void errorOrder(String orderSn) { + orderService.systemCancel(orderSn, "库存不足,出库失败"); + } + + + /** + * 写入需要更改促销库存的商品 + * + * @param keys 缓存key值 + * @param values 缓存value值 + * @param sku 购物车信息 + */ + private void setPromotionStock(List keys, List values, OrderItem sku) { + if (sku.getPromotionType() != null) { + //如果此促销有库存概念,则计入 + if (!PromotionTypeEnum.haveStock(sku.getPromotionType())) { + return; + } + PromotionTypeEnum promotionTypeEnum = PromotionTypeEnum.valueOf(sku.getPromotionType()); + keys.add(PromotionGoodsService.getPromotionGoodsStockCacheKey(promotionTypeEnum, sku.getPromotionId(), sku.getSkuId())); + int i = -sku.getNum(); + values.add(Integer.toString(i)); + } + } + + /** + * 写入需要更改促销库存的商品 + * + * @param order 订单 + */ + private void synchroDB(OrderDetailVO order) { + + //sku商品 + List goodsSkus = new ArrayList<>(); + //促销商品 + List promotionGoods = new ArrayList<>(); + //sku库存key 集合 + List skuKeys = new ArrayList<>(); + //促销库存key 集合 + List promotionKey = new ArrayList<>(); + + // 循环订单 + for (OrderItem orderItem : order.getOrderItems()) { + skuKeys.add(GoodsSkuService.getStockCacheKey(orderItem.getSkuId())); + GoodsSku goodsSku = new GoodsSku(); + goodsSku.setId(orderItem.getSkuId()); + //如果有促销信息 + if (null != orderItem.getPromotionType() && null != orderItem.getPromotionId()) { + //如果促销有库存信息 + if (PromotionTypeEnum.haveStock(orderItem.getPromotionType())) { + PromotionTypeEnum promotionTypeEnum = PromotionTypeEnum.valueOf(orderItem.getPromotionType()); + PromotionGoods pGoods = promotionGoodsService.getPromotionGoods(promotionTypeEnum, orderItem.getPromotionId(), orderItem.getSkuId()); + promotionKey.add( + PromotionGoodsService.getPromotionGoodsStockCacheKey( + promotionTypeEnum, + orderItem.getPromotionId(), orderItem.getSkuId()) + ); + promotionGoods.add(pGoods); + } + } + goodsSkus.add(goodsSku); + } + + List skuStocks = cache.multiGet(skuKeys); + //循环写入商品库存 + for (int i = 0; i < skuStocks.size(); i++) { + goodsSkus.get(i).setQuantity(Integer.parseInt(skuStocks.get(i).toString())); + } + //批量修改商品库存 + goodsSkuService.updateBatchById(goodsSkus); + + //促销库存处理 + if (!promotionKey.isEmpty()) { + List promotionStocks = cache.multiGet(promotionKey); + for (int i = 0; i < promotionKey.size(); i++) { + promotionGoods.get(i).setQuantity(Integer.parseInt(promotionStocks.get(i).toString())); + Integer num = promotionGoods.get(i).getNum(); + promotionGoods.get(i).setNum((num != null ? num : 0) + order.getOrder().getGoodsNum()); + } + promotionGoodsService.updateBatchById(promotionGoods); + } + goodsSkuService.updateGoodsStuck(goodsSkus); + + } + +} diff --git a/consumer/src/main/java/cn/lili/event/impl/WechatMessageExecute.java b/consumer/src/main/java/cn/lili/event/impl/WechatMessageExecute.java new file mode 100644 index 00000000..79e58f45 --- /dev/null +++ b/consumer/src/main/java/cn/lili/event/impl/WechatMessageExecute.java @@ -0,0 +1,58 @@ +package cn.lili.event.impl; + +import cn.lili.event.OrderStatusChangeEvent; +import cn.lili.event.TradeEvent; +import cn.lili.modules.message.util.WechatMessageUtil; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import cn.lili.modules.order.order.entity.vo.OrderVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 微信消息执行器 + * + * @author Chopper + * @version v1.0 + * 2021-04-19 14:25 + */ +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WechatMessageExecute implements OrderStatusChangeEvent, TradeEvent { + + @Autowired + private WechatMessageUtil wechatMessageUtil; + + @Override + public void orderCreate(TradeDTO tradeDTO) { + for (OrderVO orderVO : tradeDTO.getOrderVO()) { + try { + wechatMessageUtil.sendWechatMessage(orderVO.getSn()); + } catch (Exception e) { + log.error("微信消息发送失败:" + orderVO.getSn(), e); + } + } + } + + @Override + public void orderChange(OrderMessage orderMessage) { + + switch (orderMessage.getNewStatus()) { + case PAID: + case UNDELIVERED: + case DELIVERED: + case COMPLETED: + try { + wechatMessageUtil.sendWechatMessage(orderMessage.getOrderSn()); + } catch (Exception e) { + log.error("微信消息发送失败", e); + } + default: + break; + } + + } +} diff --git a/consumer/src/main/java/cn/lili/listener/AfterSaleMessageListener.java b/consumer/src/main/java/cn/lili/listener/AfterSaleMessageListener.java new file mode 100644 index 00000000..59d7f71c --- /dev/null +++ b/consumer/src/main/java/cn/lili/listener/AfterSaleMessageListener.java @@ -0,0 +1,51 @@ +package cn.lili.listener; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.rocketmq.tags.AfterSaleTagsEnum; +import cn.lili.event.AfterSaleStatusChangeEvent; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 售后通知 + * + * @author paulG + * @since 2020/12/9 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RocketMQMessageListener(topic = "${lili.data.rocketmq.after-sale-topic}", consumerGroup = "${lili.data.rocketmq.after-sale-group}") +public class AfterSaleMessageListener implements RocketMQListener { + + //售后订单状态 + private final List afterSaleStatusChangeEvents; + + @Override + public void onMessage(MessageExt messageExt) { + switch (AfterSaleTagsEnum.valueOf(messageExt.getTags())) { + case AFTER_SALE_STATUS_CHANGE: + for (AfterSaleStatusChangeEvent afterSaleStatusChangeEvent : afterSaleStatusChangeEvents) { + try { + AfterSale afterSale = JSONUtil.toBean(new String(messageExt.getBody()), AfterSale.class); + afterSaleStatusChangeEvent.afterSaleStatusChange(afterSale); + } catch (Exception e) { + log.error("售后{},在{}业务中,状态修改事件执行异常", + new String(messageExt.getBody()), + afterSaleStatusChangeEvent.getClass().getName(), + e); + } + } + break; + } + + } +} diff --git a/consumer/src/main/java/cn/lili/listener/GoodsMessageListener.java b/consumer/src/main/java/cn/lili/listener/GoodsMessageListener.java new file mode 100644 index 00000000..44f58e89 --- /dev/null +++ b/consumer/src/main/java/cn/lili/listener/GoodsMessageListener.java @@ -0,0 +1,141 @@ +package cn.lili.listener; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.rocketmq.tags.GoodsTagsEnum; +import cn.lili.event.GoodsCommentCompleteEvent; +import cn.lili.event.MemberRegisterEvent; +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.dto.GoodsCompleteMessage; +import cn.lili.modules.goods.service.GoodsService; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.member.entity.dos.FootPrint; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import cn.lili.modules.member.service.FootprintService; +import cn.lili.modules.member.service.GoodsCollectionService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.service.EsGoodsIndexService; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 商品消息 + * + * @author paulG + * @since 2020/12/9 + **/ +@Component +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RocketMQMessageListener(topic = "${lili.data.rocketmq.goods-topic}", consumerGroup = "${lili.data.rocketmq.goods-group}") +public class GoodsMessageListener implements RocketMQListener { + + //ES商品 + private final EsGoodsIndexService goodsIndexService; + //店铺 + private final StoreService storeService; + //商品 + private final GoodsService goodsService; + //商品 + private final GoodsSkuService goodsSkuService; + //用户足迹 + private final FootprintService footprintService; + //商品收藏 + private final GoodsCollectionService goodsCollectionService; + //商品评价 + private final List goodsCommentCompleteEvents; + @Override + public void onMessage(MessageExt messageExt) { + + switch (GoodsTagsEnum.valueOf(messageExt.getTags())) { + //查看商品 + case VIEW_GOODS: + FootPrint footPrint = JSONUtil.toBean(new String(messageExt.getBody()), FootPrint.class); + footprintService.saveFootprint(footPrint); + break; + //生成索引 + case GENERATOR_GOODS_INDEX: + String goodsIndexJsonStr = new String(messageExt.getBody()); + List goodsIndices = JSONUtil.toList(JSONUtil.parseArray(goodsIndexJsonStr), EsGoodsIndex.class); + for (EsGoodsIndex goodsIndex : goodsIndices) { + log.info("生成商品索引" + goodsIndex); + this.goodsIndexService.addIndex(goodsIndex); + } + break; + //审核商品 + case GOODS_AUDIT: + //删除商品 + case GOODS_DELETE: + storeService.updateStoreGoodsNum(new String(messageExt.getBody())); + break; + //规格删除 + case SKU_DELETE: + String message = new String(messageExt.getBody()); + List skuIds = JSONUtil.toList(message, String.class); + goodsCollectionService.deleteSkuCollection(skuIds); + break; + //收藏商品 + case GOODS_COLLECTION: + storeService.updateStoreCollectionNum(new String(messageExt.getBody())); + break; + //商品评价 + case GOODS_COMMENT_COMPLETE: + for (GoodsCommentCompleteEvent goodsCommentCompleteEvent : goodsCommentCompleteEvents) { + try { + MemberEvaluation memberEvaluation = JSONUtil.toBean(new String(messageExt.getBody()), MemberEvaluation.class); + goodsCommentCompleteEvent.goodsComment(memberEvaluation); + } catch (Exception e) { + log.error("评价{},在{}业务中,状态修改事件执行异常", + new String(messageExt.getBody()), + goodsCommentCompleteEvent.getClass().getName(), + e); + } + } + break; + //购买商品完成 + case BUY_GOODS_COMPLETE: + String goodsCompleteMessageStr = new String(messageExt.getBody()); + List goodsCompleteMessageList = JSONUtil.toList(JSONUtil.parseArray(goodsCompleteMessageStr), GoodsCompleteMessage.class); + for (GoodsCompleteMessage goodsCompleteMessage : goodsCompleteMessageList) { + Goods goods = goodsService.getById(goodsCompleteMessage.getGoodsId()); + if (goods != null) { + // 更新商品购买数量 + if (goods.getBuyCount() == null) { + goods.setBuyCount(0); + } + int buyCount = goods.getBuyCount() + goodsCompleteMessage.getBuyNum(); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Goods::getId, goodsCompleteMessage.getGoodsId()); + updateWrapper.set(Goods::getBuyCount, buyCount); + goodsService.update(updateWrapper); + } else { + log.error("商品Id为[" + goodsCompleteMessage.getGoodsId() + "的商品不存在,更新商品失败!"); + } + GoodsSku goodsSku = goodsSkuService.getById(goodsCompleteMessage.getSkuId()); + if (goodsSku != null) { + // 更新商品购买数量 + if (goodsSku.getBuyCount() == null) { + goodsSku.setBuyCount(0); + } + int buyCount = goodsSku.getBuyCount() + goodsCompleteMessage.getBuyNum(); + goodsSku.setBuyCount(buyCount); + goodsSkuService.update(goodsSku); + goodsIndexService.updateIndexBuyNum(goodsCompleteMessage.getSkuId(), buyCount); + } else { + log.error("商品SkuId为[" + goodsCompleteMessage.getGoodsId() + "的商品不存在,更新商品失败!"); + } + } + break; + } + } +} diff --git a/consumer/src/main/java/cn/lili/listener/MemberMessageListener.java b/consumer/src/main/java/cn/lili/listener/MemberMessageListener.java new file mode 100644 index 00000000..a32489d6 --- /dev/null +++ b/consumer/src/main/java/cn/lili/listener/MemberMessageListener.java @@ -0,0 +1,99 @@ +package cn.lili.listener; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.rocketmq.tags.MemberTagsEnum; +import cn.lili.event.MemberPointChangeEvent; +import cn.lili.event.MemberRegisterEvent; +import cn.lili.event.MemberWithdrawalEvent; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberSign; +import cn.lili.modules.member.entity.dto.MemberPointMessage; +import cn.lili.modules.member.entity.dto.MemberWithdrawalMessage; +import cn.lili.modules.member.service.MemberSignService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 会员消息 + * + * @author paulG + * @since 2020/12/9 + **/ +@Component +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RocketMQMessageListener(topic = "${lili.data.rocketmq.member-topic}", consumerGroup = "${lili.data.rocketmq.member-group}") +public class MemberMessageListener implements RocketMQListener { + + //会员签到 + private final MemberSignService memberSignService; + //会员积分变化 + private final List memberPointChangeEvents; + //会员提现 + private final List memberWithdrawalEvents; + //会员注册 + private final List memberSignEvents; + + + @Override + public void onMessage(MessageExt messageExt) { + switch (MemberTagsEnum.valueOf(messageExt.getTags())) { + //会员注册 + case MEMBER_REGISTER: + for (MemberRegisterEvent memberRegisterEvent : memberSignEvents) { + try { + Member member = JSONUtil.toBean(new String(messageExt.getBody()), Member.class); + memberRegisterEvent.memberRegister(member); + } catch (Exception e) { + log.error("会员{},在{}业务中,状态修改事件执行异常", + new String(messageExt.getBody()), + memberRegisterEvent.getClass().getName(), + e); + } + } + break; + //会员签到 + case MEMBER_SING: + MemberSign memberSign = JSONUtil.toBean(new String(messageExt.getBody()), MemberSign.class); + memberSignService.memberSignSendPoint(memberSign.getMemberId(), memberSign.getSignDay()); + break; + //会员积分变动 + case MEMBER_POINT_CHANGE: + for (MemberPointChangeEvent memberPointChangeEvent : memberPointChangeEvents) { + try { + MemberPointMessage memberPointMessage = JSONUtil.toBean(new String(messageExt.getBody()), MemberPointMessage.class); + memberPointChangeEvent.memberPointChange(memberPointMessage); + } catch (Exception e) { + log.error("会员{},在{}业务中,状态修改事件执行异常", + new String(messageExt.getBody()), + memberPointChangeEvent.getClass().getName(), + e); + } + } + break; + //会员提现 + case MEMBER_WITHDRAWAL: + for (MemberWithdrawalEvent memberWithdrawalEvent : memberWithdrawalEvents) { + try { + MemberWithdrawalMessage memberWithdrawalMessage = JSONUtil.toBean(new String(messageExt.getBody()), MemberWithdrawalMessage.class); + memberWithdrawalEvent.memberWithdrawal(memberWithdrawalMessage); + } catch (Exception e) { + log.error("会员{},在{}业务中,提现事件执行异常", + new String(messageExt.getBody()), + memberWithdrawalEvent.getClass().getName(), + e); + } + } + + default: + break; + } + } +} diff --git a/consumer/src/main/java/cn/lili/listener/NoticeMessageListener.java b/consumer/src/main/java/cn/lili/listener/NoticeMessageListener.java new file mode 100644 index 00000000..eb3499c9 --- /dev/null +++ b/consumer/src/main/java/cn/lili/listener/NoticeMessageListener.java @@ -0,0 +1,32 @@ +package cn.lili.listener; + +import cn.hutool.json.JSONUtil; +import cn.lili.modules.message.entity.dto.NoticeMessageDTO; +import cn.lili.modules.message.service.NoticeMessageService; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 站内信通知 + * + * @author paulG + * @since 2020/12/9 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RocketMQMessageListener(topic = "${lili.data.rocketmq.notice-topic}", consumerGroup = "${lili.data.rocketmq.notice-group}") +public class NoticeMessageListener implements RocketMQListener { + + //站内信 + private final NoticeMessageService noticeMessageService; + + @Override + public void onMessage(MessageExt messageExt) { + NoticeMessageDTO noticeMessageDTO = JSONUtil.toBean(new String(messageExt.getBody()), NoticeMessageDTO.class); + noticeMessageService.noticeMessage(noticeMessageDTO); + } +} diff --git a/consumer/src/main/java/cn/lili/listener/NoticeSendMessageListener.java b/consumer/src/main/java/cn/lili/listener/NoticeSendMessageListener.java new file mode 100644 index 00000000..d241ce7c --- /dev/null +++ b/consumer/src/main/java/cn/lili/listener/NoticeSendMessageListener.java @@ -0,0 +1,100 @@ +package cn.lili.listener; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.rocketmq.tags.OtherTagsEnum; +import cn.lili.common.sms.SmsUtil; +import cn.lili.modules.member.mapper.MemberMapper; +import cn.lili.modules.message.entity.dos.Message; +import cn.lili.modules.message.entity.dos.StoreMessage; +import cn.lili.modules.message.entity.dto.SmsReachDTO; +import cn.lili.modules.message.entity.enums.MessageStatusEnum; +import cn.lili.modules.message.entity.enums.RangeEnum; +import cn.lili.modules.message.service.StoreMessageService; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 消息发送 + * + * @author paulG + * @since 2020/12/9 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RocketMQMessageListener(topic = "${lili.data.rocketmq.notice-send-topic}", consumerGroup = "${lili.data.rocketmq.notice-send-group}") +public class NoticeSendMessageListener implements RocketMQListener { + + //会员 + private final MemberMapper memberMapper; + //短信 + private final SmsUtil smsUtil; + //店铺消息 + private final StoreMessageService storeMessageService; + //店铺 + private final StoreService storeService; + + @Override + public void onMessage(MessageExt messageExt) { + switch (OtherTagsEnum.valueOf(messageExt.getTags())) { + case SMS: + String smsJsonStr = new String(messageExt.getBody()); + SmsReachDTO smsReachDTO = JSONUtil.toBean(smsJsonStr, SmsReachDTO.class); + + //发送全部会员 + if (smsReachDTO.getSmsRange().equals(RangeEnum.ALL.name())) { + //获取所有会员的手机号 + List list = memberMapper.getAllMemberMobile(); + smsUtil.sendBatchSms(smsReachDTO.getSignName(), list, smsReachDTO.getMessageCode()); + //判断为发送部分用户 + } else if (smsReachDTO.getSmsRange().equals(RangeEnum.APPOINT.name())) { + smsUtil.sendBatchSms(smsReachDTO.getSignName(), smsReachDTO.getMobile(), smsReachDTO.getMessageCode()); + } + break; + //管理员发送站内信 + case MESSAGE: + Message message = JSONUtil.toBean(new String(messageExt.getBody()), Message.class); + List list = new ArrayList<>(); + //保存商家记录 + if (message.getMessageRange().equals("ALL")) { + List storeList = storeService.list(new QueryWrapper().eq("store_disable", "OPEN")); + storeList.forEach(item -> { + StoreMessage storeMessage = new StoreMessage(); + storeMessage.setMessageId(message.getId()); + storeMessage.setStoreName(item.getStoreName()); + storeMessage.setStoreId(item.getId()); + storeMessage.setStatus(MessageStatusEnum.UN_READY.name()); + storeMessage.setTitle(message.getTitle()); + storeMessage.setContent(message.getContent()); + list.add(storeMessage); + }); + } else { + int i = 0; + for (String str : message.getUserIds()) { + StoreMessage storeMessage = new StoreMessage(); + storeMessage.setMessageId(message.getId()); + storeMessage.setStoreId(str); + storeMessage.setStoreName(message.getUserNames()[i]); + storeMessage.setStatus(MessageStatusEnum.UN_READY.name()); + storeMessage.setTitle(message.getTitle()); + storeMessage.setContent(message.getContent()); + list.add(storeMessage); + i++; + } + } + storeMessageService.save(list); + break; + default: + break; + } + } +} diff --git a/consumer/src/main/java/cn/lili/listener/OrderMessageListener.java b/consumer/src/main/java/cn/lili/listener/OrderMessageListener.java new file mode 100644 index 00000000..8244554f --- /dev/null +++ b/consumer/src/main/java/cn/lili/listener/OrderMessageListener.java @@ -0,0 +1,76 @@ +package cn.lili.listener; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.rocketmq.tags.MqOrderTagsEnum; +import cn.lili.event.OrderStatusChangeEvent; +import cn.lili.event.TradeEvent; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 订单消息 + * + * @author paulG + * @since 2020/12/9 + **/ +@Component +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RocketMQMessageListener(topic = "${lili.data.rocketmq.order-topic}", consumerGroup = "${lili.data.rocketmq.order-group}") +public class OrderMessageListener implements RocketMQListener { + + //交易 + private final List tradeEvent; + //订单状态 + private final List orderStatusChangeEvents; + //缓存 + private final Cache cache; + + @Override + public void onMessage(MessageExt messageExt) { + switch (MqOrderTagsEnum.valueOf(messageExt.getTags())) { + //订单创建 + case ORDER_CREATE: + String key = new String(messageExt.getBody()); + TradeDTO tradeDTO = (TradeDTO) cache.get(key); + for (TradeEvent event : tradeEvent) { + try { + event.orderCreate(tradeDTO); + } catch (Exception e) { + log.error("交易{}入库,在{}业务中,状态修改事件执行异常", + tradeDTO.getSn(), + event.getClass().getName(), + e); + } + } + break; + //订单状态变更 + case STATUS_CHANGE: + for (OrderStatusChangeEvent orderStatusChangeEvent : orderStatusChangeEvents) { + try { + OrderMessage orderMessage = JSONUtil.toBean(new String(messageExt.getBody()), OrderMessage.class); + orderStatusChangeEvent.orderChange(orderMessage); + } catch (Exception e) { + log.error("订单{},在{}业务中,状态修改事件执行异常", + new String(messageExt.getBody()), + orderStatusChangeEvent.getClass().getName(), + e); + } + } + break; + default: + break; + } + } + +} diff --git a/consumer/src/main/java/cn/lili/service/NoticeSendMessageConsumer.java b/consumer/src/main/java/cn/lili/service/NoticeSendMessageConsumer.java new file mode 100644 index 00000000..ef07b637 --- /dev/null +++ b/consumer/src/main/java/cn/lili/service/NoticeSendMessageConsumer.java @@ -0,0 +1,22 @@ +package cn.lili.service; + +import cn.lili.event.OrderStatusChangeEvent; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import org.springframework.stereotype.Component; + +/** + * 消息发送 + * + * @author paulG + * @since 2020/12/9 + */ +@Component +public class NoticeSendMessageConsumer implements OrderStatusChangeEvent { + + + @Override + public void orderChange(OrderMessage orderMessage) { + + + } +} diff --git a/consumer/src/main/java/cn/lili/timetask/TimedTaskJobHandler.java b/consumer/src/main/java/cn/lili/timetask/TimedTaskJobHandler.java new file mode 100644 index 00000000..3bf9d017 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/TimedTaskJobHandler.java @@ -0,0 +1,104 @@ +package cn.lili.timetask; + +import cn.lili.timetask.handler.EveryDayExecute; +import cn.lili.timetask.handler.EveryHourExecute; +import cn.lili.timetask.handler.EveryMinuteExecute; +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 定时器任务 + * + * @author Chopper + * @version v1.0 + * 2020-12-24 11:51 + */ +@Slf4j +@Component +public class TimedTaskJobHandler { + + @Autowired(required = false) + private List everyMinuteExecutes; + + + @Autowired(required = false) + private List everyHourExecutes; + + + @Autowired(required = false) + private List everyDayExecutes; + + /** + * 每分钟任务 + * + * @throws Exception + */ + @XxlJob("everyMinuteExecute") + public ReturnT everyMinuteExecute(String param) { + log.info("每分钟任务执行"); + if (everyMinuteExecutes == null || everyMinuteExecutes.size() == 0) { + return ReturnT.SUCCESS; + } + + for (int i = 0; i < everyMinuteExecutes.size(); i++) { + try { + everyMinuteExecutes.get(i).execute(); + } catch (Exception e) { + log.error("每分钟任务异常", e); + } + } + return ReturnT.SUCCESS; + } + + /** + * 每小时任务 + * + * @throws Exception + */ + @XxlJob("everyHourExecuteJobHandler") + public ReturnT everyHourExecuteJobHandler(String param) { + log.info("每小时任务执行"); + if (everyHourExecutes == null || everyHourExecutes.size() == 0) { + return ReturnT.SUCCESS; + } + + for (int i = 0; i < everyHourExecutes.size(); i++) { + try { + everyHourExecutes.get(i).execute(); + } catch (Exception e) { + log.error("每分钟任务异常", e); + } + } + return ReturnT.SUCCESS; + } + + /** + * 每日任务 + * + * @throws Exception + */ + @XxlJob("everyDayExecuteJobHandler") + public ReturnT everyDayExecuteJobHandler(String param) { + + log.info("每日任务执行"); + if (everyDayExecutes == null || everyDayExecutes.size() == 0) { + return ReturnT.SUCCESS; + } + + for (int i = 0; i < everyDayExecutes.size(); i++) { + try { + everyDayExecutes.get(i).execute(); + } catch (Exception e) { + log.error("每分钟任务异常", e); + } + } + return ReturnT.SUCCESS; + } + + +} diff --git a/consumer/src/main/java/cn/lili/timetask/config/XxlJobConfig.java b/consumer/src/main/java/cn/lili/timetask/config/XxlJobConfig.java new file mode 100644 index 00000000..80116a4d --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/config/XxlJobConfig.java @@ -0,0 +1,78 @@ +package cn.lili.timetask.config; + +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * xxl-job config + * + * @author xuxueli 2017-04-28 + */ +@Configuration +public class XxlJobConfig { + private final Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); + + @Value("${xxl.job.admin.addresses:}") + private String adminAddresses; + + @Value("${xxl.job.accessToken:}") + private String accessToken; + + @Value("${xxl.job.executor.appname}") + private String appname; + + @Value("${xxl.job.executor.address}") + private String address; + + @Value("${xxl.job.executor.ip}") + private String ip; + + @Value("${xxl.job.executor.port}") + private int port; + + @Value("${xxl.job.executor.logpath}") + private String logPath; + + @Value("${xxl.job.executor.logretentiondays}") + private int logRetentionDays; + + + @Bean + public XxlJobSpringExecutor xxlJobExecutor() { + logger.info(">>>>>>>>>>> xxl-job config init."); + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(adminAddresses); + xxlJobSpringExecutor.setAppname(appname); + xxlJobSpringExecutor.setAddress(address); + xxlJobSpringExecutor.setIp(ip); + xxlJobSpringExecutor.setPort(port); + xxlJobSpringExecutor.setAccessToken(accessToken); + xxlJobSpringExecutor.setLogPath(logPath); + xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); + + return xxlJobSpringExecutor; + } + + /** + * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP; + * + * 1、引入依赖: + * + * org.springframework.cloud + * spring-cloud-commons + * ${version} + * + * + * 2、配置文件,或者容器启动变量 + * spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.' + * + * 3、获取IP + * String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); + */ + + +} \ No newline at end of file diff --git a/consumer/src/main/java/cn/lili/timetask/handler/EveryDayExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/EveryDayExecute.java new file mode 100644 index 00000000..dd927f90 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/EveryDayExecute.java @@ -0,0 +1,18 @@ +package cn.lili.timetask.handler; + +/** + * 每日任务 + * 每日凌晨1点执行 + * + * @author Chopper + * @date 2020/12/24 11:52 + */ +public interface EveryDayExecute { + + /** + * 执行每日任务 + */ + void execute(); + + +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/EveryHourExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/EveryHourExecute.java new file mode 100644 index 00000000..9ebc83d8 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/EveryHourExecute.java @@ -0,0 +1,17 @@ +package cn.lili.timetask.handler; + +/** + * 每小时任务 + * + * @author Chopper + * @date 2020/12/24 11:52 + */ +public interface EveryHourExecute { + + /** + * 执行 + */ + void execute(); + + +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/EveryMinuteExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/EveryMinuteExecute.java new file mode 100644 index 00000000..4d7e08c8 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/EveryMinuteExecute.java @@ -0,0 +1,17 @@ +package cn.lili.timetask.handler; + +/** + * 每分钟任务 + * + * @author Chopper + * @date 2020/12/24 11:52 + */ +public interface EveryMinuteExecute { + + /** + * 执行 + */ + void execute(); + + +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/impl/bill/BillExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/impl/bill/BillExecute.java new file mode 100644 index 00000000..14a98d78 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/impl/bill/BillExecute.java @@ -0,0 +1,54 @@ +package cn.lili.timetask.handler.impl.bill; + +import cn.hutool.core.date.DateUtil; +import cn.lili.modules.store.entity.dto.StoreSettlementDay; +import cn.lili.modules.store.mapper.StoreDetailMapper; +import cn.lili.modules.store.service.BillService; +import cn.lili.timetask.handler.EveryDayExecute; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 店铺结算执行 + * + * @author Bulbasaur + * @date 2021/2/18 3:45 下午 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BillExecute implements EveryDayExecute { + + //结算单 + private final BillService billService; + //店铺详情 + private final StoreDetailMapper storeDetailMapper; + + /** + * 1.查询今日待结算的商家 + * 2.查询商家上次结算日期,生成本次结算单 + * 3.记录商家结算日 + */ + @Override + public void execute() { + + //获取当前时间的前一天 + String day = "," + DateUtil.date().dayOfMonth() + ","; + + //获取待结算商家列表 + List storeList = storeDetailMapper.getSettlementStore(new QueryWrapper().like("settlement_cycle", day)); + + //批量商家结算 + for (StoreSettlementDay storeSettlementDay : storeList) { + + //生成结算单 + billService.createBill(storeSettlementDay.getStoreId(), storeSettlementDay.getSettlementDay()); + + //修改店铺结算时间 + storeDetailMapper.updateSettlementDay(storeSettlementDay.getStoreId(), DateUtil.date()); + } + } +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/impl/goods/GoodsExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/impl/goods/GoodsExecute.java new file mode 100644 index 00000000..f5ae0203 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/impl/goods/GoodsExecute.java @@ -0,0 +1,48 @@ +package cn.lili.timetask.handler.impl.goods; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.lili.modules.goods.mapper.GoodsMapper; +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import cn.lili.modules.member.mapper.MemberEvaluationMapper; +import cn.lili.timetask.handler.EveryDayExecute; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * 商品定时器 + * + * @author Chopper + * @date 2021/3/18 3:23 下午 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsExecute implements EveryDayExecute { + //会员评价 + private final MemberEvaluationMapper memberEvaluationMapper; + //商品 + private final GoodsMapper goodsMapper; + + /** + * 查询已上架的商品的评价数量并赋值 + */ + @Override + public void execute() { + + //查询上次统计到本次的评价数量 + List> list = memberEvaluationMapper.memberEvaluationNum( + new QueryWrapper() + .between("create_time", DateUtil.yesterday(), new DateTime())); + + System.out.println("评论数量" + list.size()); + for (Map map : list) { + goodsMapper.addGoodsCommentNum(Integer.parseInt(map.get("num").toString()), map.get("goods_id").toString()); + } + + } +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/impl/order/CancelOrderTaskExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/impl/order/CancelOrderTaskExecute.java new file mode 100644 index 00000000..6e10e4a2 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/impl/order/CancelOrderTaskExecute.java @@ -0,0 +1,57 @@ +package cn.lili.timetask.handler.impl.order; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.OrderSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import cn.lili.timetask.handler.EveryMinuteExecute; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 订单自动取消(每分钟执行) + * + * @author paulG + * @date 2021/3/11 + **/ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CancelOrderTaskExecute implements EveryMinuteExecute { + //订单 + private final OrderService orderService; + //设置 + private final SettingService settingService; + + + @Override + public void execute() { + Setting setting = settingService.get(SettingEnum.ORDER_SETTING.name()); + OrderSetting orderSetting = JSONUtil.toBean(setting.getSettingValue(), OrderSetting.class); + if (orderSetting != null && orderSetting.getAutoCancel() != null) { + // 订单自动取消时间 = 当前时间 - 自动取消时间分钟数 + DateTime cancelTime = DateUtil.offsetMinute(DateUtil.date(), -orderSetting.getAutoCancel()); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Order::getOrderStatus, OrderStatusEnum.UNPAID.name()); + // 订单创建时间 <= 订单自动取消时间 + queryWrapper.le(Order::getCreateTime, cancelTime); + List list = orderService.list(queryWrapper); + List cancelSnList = list.stream().map(Order::getSn).collect(Collectors.toList()); + for (String sn : cancelSnList) { + orderService.systemCancel(sn, "超时未支付自动取消"); + } + } + } +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/impl/order/OrderEveryDayTaskExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/impl/order/OrderEveryDayTaskExecute.java new file mode 100644 index 00000000..13a3d372 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/impl/order/OrderEveryDayTaskExecute.java @@ -0,0 +1,121 @@ +package cn.lili.timetask.handler.impl.order; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.member.entity.dto.MemberEvaluationDTO; +import cn.lili.modules.member.entity.enums.EvaluationGradeEnum; +import cn.lili.modules.member.service.MemberEvaluationService; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.OrderSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import cn.lili.timetask.handler.EveryDayExecute; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author paulG + * @since 2021/3/11 + **/ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderEveryDayTaskExecute implements EveryDayExecute { + + //订单 + private final OrderService orderService; + //订单货物 + private final OrderItemService orderItemService; + //设置 + private final SettingService settingService; + //会员评价 + private final MemberEvaluationService memberEvaluationService; + + /** + * 执行每日任务 + */ + @Override + public void execute() { + Setting setting = settingService.get(SettingEnum.ORDER_SETTING.name()); + //自动确认收货 + OrderSetting orderSetting = JSONUtil.toBean(setting.getSettingValue(), OrderSetting.class); + if (orderSetting == null) { + throw new ServiceException("系统配置异常"); + } + + //自动确认收货 + completedOrder(orderSetting); + //自动好评 + memberEvaluation(orderSetting); + } + + /** + * 自动确认收获,订单完成 + * + * @param orderSetting 订单设置 + */ + private void completedOrder(OrderSetting orderSetting) { + // 订单自动收货时间 = 当前时间 - 自动收货时间天数 + DateTime receiveTime = DateUtil.offsetDay(DateUtil.date(), -orderSetting.getAutoEvaluation()); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Order::getOrderStatus, OrderStatusEnum.DELIVERED.name()); + // 订单发货时间 >= 订单自动收货时间 + queryWrapper.ge(Order::getLogisticsTime, receiveTime); + List list = orderService.list(queryWrapper); + List receiveSnList = list.stream().map(Order::getSn).collect(Collectors.toList()); + if (!receiveSnList.isEmpty()) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.in(Order::getSn, receiveSnList); + updateWrapper.set(Order::getOrderStatus, OrderStatusEnum.COMPLETED.name()).set(Order::getCompleteTime, new Date()); + boolean update = orderService.update(updateWrapper); + if (Boolean.FALSE.equals(update)) { + log.error("自动收货订单失败!订单编号为[{}]", receiveSnList); + } + } + } + + /** + * 自动好评 + * + * @param orderSetting 订单设置 + */ + private void memberEvaluation(OrderSetting orderSetting) { + // 订单自动收货时间 = 当前时间 - 自动收货时间天数 + DateTime receiveTime = DateUtil.offsetDay(DateUtil.date(), -orderSetting.getAutoReceive()); + // 订单完成时间 <= 订单自动好评时间 + List orderItems = orderItemService.waitEvaluate(receiveTime); + + for (OrderItem orderItem : orderItems) { + + MemberEvaluationDTO memberEvaluationDTO = new MemberEvaluationDTO(); + memberEvaluationDTO.setOrderItemSn(orderItem.getSn()); + memberEvaluationDTO.setContent("系统默认好评"); + memberEvaluationDTO.setGoodsId(orderItem.getGoodsId()); + memberEvaluationDTO.setSkuId(orderItem.getSkuId()); + memberEvaluationDTO.setGrade(EvaluationGradeEnum.GOOD.name()); + memberEvaluationDTO.setDeliveryScore(5); + memberEvaluationDTO.setDescriptionScore(5); + memberEvaluationDTO.setServiceScore(5); + + memberEvaluationService.addMemberEvaluation(memberEvaluationDTO); + + } + } + + +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/impl/promotion/PromotionEverydayExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/impl/promotion/PromotionEverydayExecute.java new file mode 100644 index 00000000..d83ef5ad --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/impl/promotion/PromotionEverydayExecute.java @@ -0,0 +1,131 @@ +package cn.lili.timetask.handler.impl.promotion; + +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.enums.MemberCouponStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.vos.CouponVO; +import cn.lili.modules.promotion.entity.vos.PintuanVO; +import cn.lili.modules.promotion.service.*; +import cn.lili.timetask.handler.EveryDayExecute; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 促销活动每日定时器 + * + * @author Chopper + * @date 2021/3/18 3:23 下午 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PromotionEverydayExecute implements EveryDayExecute { + + //Mongo + private final MongoTemplate mongoTemplate; + //满额活动 + private final FullDiscountService fullDiscountService; + //拼团 + private final PintuanService pintuanService; + //优惠券 + private final CouponService couponService; + //会员优惠券 + private final MemberCouponService memberCouponService; + //促销商品 + private final PromotionGoodsService promotionGoodsService; + + + /** + * 将已过期的促销活动置为结束 + */ + @Override + public void execute() { + + Query query = new Query(); + query.addCriteria(Criteria.where("promotionStatus").ne(PromotionStatusEnum.END.name()).orOperator(Criteria.where("promotionStatus").ne(PromotionStatusEnum.CLOSE.name()))); + query.addCriteria(Criteria.where("endTime").lt(new Date())); + + List promotionIds = new ArrayList<>(); + + List fullDiscountVOS = mongoTemplate.find(query, FullDiscountVO.class); + if (!fullDiscountVOS.isEmpty()) { + List ids = new ArrayList<>(); + for (FullDiscountVO vo : fullDiscountVOS) { + vo.setPromotionStatus(PromotionStatusEnum.END.name()); + if (vo.getPromotionGoodsList() != null && !vo.getPromotionGoodsList().isEmpty()) { + for (PromotionGoods promotionGoods : vo.getPromotionGoodsList()) { + promotionGoods.setPromotionStatus(PromotionStatusEnum.END.name()); + } + } + mongoTemplate.save(vo); + ids.add(vo.getId()); + } + fullDiscountService.update(this.getUpdatePromotionWrapper(ids)); + promotionIds.addAll(ids); + } + + List pintuanVOS = mongoTemplate.find(query, PintuanVO.class); + if (!pintuanVOS.isEmpty()) { + List ids = new ArrayList<>(); + for (PintuanVO vo : pintuanVOS) { + vo.setPromotionStatus(PromotionStatusEnum.END.name()); + if (vo.getPromotionGoodsList() != null && !vo.getPromotionGoodsList().isEmpty()) { + for (PromotionGoods promotionGoods : vo.getPromotionGoodsList()) { + promotionGoods.setPromotionStatus(PromotionStatusEnum.END.name()); + } + } + mongoTemplate.save(vo); + ids.add(vo.getId()); + } + pintuanService.update(this.getUpdatePromotionWrapper(ids)); + promotionIds.addAll(ids); + } + + List couponVOS = mongoTemplate.find(query, CouponVO.class); + if (!couponVOS.isEmpty()) { + List ids = new ArrayList<>(); + for (CouponVO vo : couponVOS) { + vo.setPromotionStatus(PromotionStatusEnum.END.name()); + if (vo.getPromotionGoodsList() != null && !vo.getPromotionGoodsList().isEmpty()) { + for (PromotionGoods promotionGoods : vo.getPromotionGoodsList()) { + promotionGoods.setPromotionStatus(PromotionStatusEnum.END.name()); + } + } + mongoTemplate.save(vo); + ids.add(vo.getId()); + } + couponService.update(this.getUpdatePromotionWrapper(ids)); + LambdaUpdateWrapper memberCouponLambdaUpdateWrapper = new LambdaUpdateWrapper().in(MemberCoupon::getCouponId, ids).set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.EXPIRE.name()); + memberCouponService.update(memberCouponLambdaUpdateWrapper); + promotionIds.addAll(ids); + } + + promotionGoodsService.update(this.getUpdatePromotionGoodsWrapper(promotionIds)); + + } + + private UpdateWrapper getUpdatePromotionWrapper(List ids) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.in("id", ids); + updateWrapper.set("promotion_status", PromotionStatusEnum.END.name()); + return updateWrapper; + } + + private UpdateWrapper getUpdatePromotionGoodsWrapper(List ids) { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.in("promotion_id", ids); + updateWrapper.set("promotion_status", PromotionStatusEnum.END.name()); + return updateWrapper; + } +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/impl/statistics/MemberStatisticsExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/impl/statistics/MemberStatisticsExecute.java new file mode 100644 index 00000000..257033b9 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/impl/statistics/MemberStatisticsExecute.java @@ -0,0 +1,71 @@ +package cn.lili.timetask.handler.impl.statistics; + +import cn.lili.modules.statistics.model.dos.MemberStatisticsData; +import cn.lili.modules.statistics.service.MemberStatisticsDataService; +import cn.lili.timetask.handler.EveryDayExecute; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Calendar; +import java.util.Date; + +/** + * 会员数据统计 + * + * @author Chopper + * @date 2021-03-02 14:56 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberStatisticsExecute implements EveryDayExecute { + + //会员统计 + private final MemberStatisticsDataService memberStatisticsDataService; + + @Override + public void execute() { + + try { + //统计的时间(开始。结束时间) + Date startTime, endTime; + //初始值 + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 1); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.HOUR_OF_DAY, 0); + endTime = calendar.getTime(); + //-1天,即为开始时间 + calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) - 1); + startTime = calendar.getTime(); + MemberStatisticsData memberStatisticsData = new MemberStatisticsData(); + memberStatisticsData.setMemberCount(memberStatisticsDataService.memberCount(endTime)); + memberStatisticsData.setCreateDate(startTime); + memberStatisticsData.setActiveQuantity(memberStatisticsDataService.activeQuantity(startTime)); + memberStatisticsData.setNewlyAdded(memberStatisticsDataService.newlyAdded(startTime, endTime)); + memberStatisticsDataService.save(memberStatisticsData); + } catch (Exception e) { + log.error("每日会员统计功能异常:", e); + } + } + + public static void main(String[] args) { + + //统计的时间(开始。结束时间) + Date startTime, endTime; + //初始值 + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 1); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.HOUR_OF_DAY, 0); + endTime = calendar.getTime(); + //-1天,即为开始时间 + calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) - 1); + startTime = calendar.getTime(); + System.out.println(startTime); + } +} \ No newline at end of file diff --git a/consumer/src/main/java/cn/lili/timetask/handler/impl/statistics/OnlineMemberStatistics.java b/consumer/src/main/java/cn/lili/timetask/handler/impl/statistics/OnlineMemberStatistics.java new file mode 100644 index 00000000..0dfa4f15 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/impl/statistics/OnlineMemberStatistics.java @@ -0,0 +1,105 @@ +package cn.lili.timetask.handler.impl.statistics; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.config.properties.StatisticsProperties; +import cn.lili.modules.statistics.model.vo.OnlineMemberVO; +import cn.lili.timetask.handler.EveryHourExecute; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 实时在线人数统计 + * + * @author Chopper + * @date 2021-02-21 09:47 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OnlineMemberStatistics implements EveryHourExecute { + + //缓存 + private final Cache cache; + //统计小时 + private final StatisticsProperties statisticsProperties; + + + @Override + public void execute() { + + Calendar calendar = Calendar.getInstance(); + + Object object = cache.get(CachePrefix.ONLINE_MEMBER.getPrefix()); + List onlineMemberVOS; + if (object == null) { + onlineMemberVOS = new ArrayList<>(); + } else { + onlineMemberVOS = (List) object; + } + + //过滤 有效统计时间 + calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - statisticsProperties.getOnlineMember()); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + Calendar finalCalendar = calendar; + onlineMemberVOS = onlineMemberVOS.stream() + .filter(onlineMemberVO -> onlineMemberVO.getDate().after(finalCalendar.getTime())) + .collect(Collectors.toList()); + + //计入新数据 + calendar = Calendar.getInstance(); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + onlineMemberVOS.add(new OnlineMemberVO(calendar.getTime(), cache.keys(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.MEMBER) + "*").size())); + + //写入缓存 + cache.put(CachePrefix.ONLINE_MEMBER.getPrefix(), onlineMemberVOS); + } + + + /** + * 手动设置某一时间,活跃人数 + * + * @param time 时间 + * @param num 人数 + */ + public void execute(Date time, Integer num) { + + Object object = cache.get(CachePrefix.ONLINE_MEMBER.getPrefix()); + List onlineMemberVOS; + if (object == null) { + onlineMemberVOS = new ArrayList<>(); + } else { + onlineMemberVOS = (List) object; + } + + Calendar calendar = Calendar.getInstance(); + calendar.setTime(time); + //过滤 有效统计时间 + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - 48); + + Calendar finalCalendar = calendar; + onlineMemberVOS = onlineMemberVOS.stream() + .filter(onlineMemberVO -> onlineMemberVO.getDate().after(finalCalendar.getTime())) + .collect(Collectors.toList()); + onlineMemberVOS.add(new OnlineMemberVO(time, num)); + + //写入缓存 + cache.put(CachePrefix.ONLINE_MEMBER.getPrefix(), onlineMemberVOS); + } + +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/impl/storeRating/StoreRatingExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/impl/storeRating/StoreRatingExecute.java new file mode 100644 index 00000000..73a6fa26 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/impl/storeRating/StoreRatingExecute.java @@ -0,0 +1,60 @@ +package cn.lili.timetask.handler.impl.storeRating; + +import cn.lili.common.enums.SwitchEnum; +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import cn.lili.modules.member.entity.vo.StoreRatingVO; +import cn.lili.modules.member.mapper.MemberEvaluationMapper; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.entity.enums.StoreStatusEnum; +import cn.lili.modules.store.service.StoreService; +import cn.lili.timetask.handler.EveryDayExecute; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 店铺评分 + * + * @author Chopper + * @date 2021/3/15 5:30 下午 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreRatingExecute implements EveryDayExecute { + //店铺 + private final StoreService storeService; + //会员评价 + private final MemberEvaluationMapper memberEvaluationMapper; + + + @Override + public void execute() { + //获取所有开启的店铺 + List storeList = storeService.list(new LambdaQueryWrapper().eq(Store::getStoreDisable, StoreStatusEnum.OPEN.name())); + for (Store store : storeList) { + //店铺所有开启的评价 + LambdaQueryWrapper QueryWrapper = Wrappers.lambdaQuery(); + QueryWrapper.eq(MemberEvaluation::getStoreId, store.getId()); + QueryWrapper.eq(MemberEvaluation::getStatus, SwitchEnum.OPEN.name()); + StoreRatingVO storeRatingVO = memberEvaluationMapper.getStoreRatingVO(QueryWrapper); + + if (storeRatingVO != null) { + //保存评分 + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.eq(Store::getId, store.getId()); + lambdaUpdateWrapper.set(Store::getDescriptionScore, storeRatingVO.getDescriptionScore()); + lambdaUpdateWrapper.set(Store::getDeliveryScore, storeRatingVO.getDeliveryScore()); + lambdaUpdateWrapper.set(Store::getServiceScore, storeRatingVO.getServiceScore()); + storeService.update(lambdaUpdateWrapper); + } + + } + + + } +} diff --git a/consumer/src/main/java/cn/lili/timetask/handler/impl/view/PageViewStatisticsExecute.java b/consumer/src/main/java/cn/lili/timetask/handler/impl/view/PageViewStatisticsExecute.java new file mode 100644 index 00000000..91f75d15 --- /dev/null +++ b/consumer/src/main/java/cn/lili/timetask/handler/impl/view/PageViewStatisticsExecute.java @@ -0,0 +1,192 @@ +package cn.lili.timetask.handler.impl.view; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.DateUtil; +import cn.lili.modules.statistics.model.dos.PlatformViewData; +import cn.lili.modules.statistics.service.PlatformViewDataService; +import cn.lili.timetask.handler.EveryDayExecute; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * 统计 入库 + * + * @author Chopper + * @date 2021-01-15 18:20 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PageViewStatisticsExecute implements EveryDayExecute { + //缓存 + private final Cache cache; + //平台PV统计 + private final PlatformViewDataService platformViewDataService; + + @Override + public void execute() { + + //1、缓存keys 模糊匹配 + //2、过滤今日的数据,即今天只能统计今日以前的数据 + //4对key value 分别代表平台PV、平台UV、店铺PV、店铺UV + List pvKeys = filterKeys(cache.keys(CachePrefix.PV.getPrefix() + "*")); + List pvValues = cache.multiGet(pvKeys); + + List storePVKeys = filterKeys(cache.keys(CachePrefix.STORE_PV.getPrefix() + "*")); + List storePvValues = cache.multiGet(storePVKeys); + + //备份UV数据,这里赋值之后,会被删除 + List uvKeys = new ArrayList<>(); + List storeUvKeys = new ArrayList<>(); + + log.debug("开始统计平台数据,PV共计【{}】条", pvKeys.size()); + log.debug("开始统计店铺数据,PV共计【{}】条", storePvValues.size()); + + //定义要统计的数据 + List platformViewDataList = new ArrayList<>(); + + //PV 统计 + if (pvKeys.size() > 0) { + for (int i = 0; i < pvKeys.size(); i++) { + String key = pvKeys.get(i); + PageViewStatistics pageViewStatistics = new PageViewStatistics(key); + PlatformViewData platformPVData = new PlatformViewData(); + BeanUtil.copyProperties(pageViewStatistics, platformPVData); + platformPVData.setPvNum(pvValues.get(i).longValue()); + //根据pvkey 获取 uvkey + String uvKey = getUvKey(key); + uvKeys.add(uvKey); + platformPVData.setUvNum(cache.counter(uvKey)); + platformPVData.setStoreId("-1"); + platformViewDataList.add(platformPVData); + } + batchSave(pvKeys, uvKeys, platformViewDataList); + } + //店铺 PV 统计 + if (storePVKeys.size() > 0) { + platformViewDataList = new ArrayList<>(); + for (int i = 0; i < storePVKeys.size(); i++) { + String key = storePVKeys.get(i); + PageViewStatistics pageViewStatistics = new PageViewStatistics(key); + PlatformViewData storePVData = new PlatformViewData(); + BeanUtil.copyProperties(pageViewStatistics, storePVData); + storePVData.setPvNum(storePvValues.get(i).longValue()); + //根据pvkey 获取 uvkey + String uvKey = getUvKey(key); + uvKeys.add(uvKey); + storePVData.setUvNum(cache.counter(uvKey)); + platformViewDataList.add(storePVData); + } + batchSave(storePVKeys, storeUvKeys, platformViewDataList); + } + } + + /** + * 根据缓存的PVkey 获取对应的UVkey + * + * @param key + * @return + */ + private String getUvKey(String key) { + if (StringUtils.isNotEmpty(key)) { + + key = key.replace(CachePrefix.PV.getPrefix(), CachePrefix.UV.getPrefix()); + key = key.replace(CachePrefix.STORE_PV.getPrefix(), CachePrefix.STORE_UV.getPrefix()); + return key; + } + return key; + } + + /** + * 批量保存数据&&清除保存数据的缓存 + * + * @param pvKeys PV key + * @param uvKeys UV key + * @param platformViewData DOS + */ + @Transactional(rollbackFor = Exception.class) + void batchSave(List pvKeys, List uvKeys, List platformViewData) { + log.debug("批量保存流量数据,共计【{}】条", platformViewData.size()); + platformViewDataService.saveBatch(platformViewData); + //批量删除缓存key + cache.multiDel(pvKeys); + cache.multiDel(uvKeys); + log.debug("流量数据保存完成"); + } + + + /** + * 过滤缓存key + * + * @param keys 缓存key集合 + */ + private static List filterKeys(List keys) { + + //只统计一天前的数据 + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, -24); + + List result = new ArrayList<>(); + for (String key : keys) { + PageViewStatistics temp = new PageViewStatistics(key); + //如果需要统计,则将key写入集合 + if (temp.getDate().before(calendar.getTime())) { + result.add(key); + } + } + + return result; + } + +} + +/** + * 根据缓存key 获取其中需要的参数,年月日,以及店铺信息 + */ +@Data +class PageViewStatistics { + /** + * 年 、 月 、 日 、 店铺id + */ + private Date date; + private String storeId; + + public PageViewStatistics(String str) { + //将字符串解析成需要的对象 + str = str.substring(str.indexOf("}") + 2); + String[] dateStr = str.split("-"); + Integer year = Integer.parseInt(dateStr[0]); + Integer month = Integer.parseInt(dateStr[1]); + Integer day; + //是否有店铺id + if (dateStr.length > 3) { + day = Integer.parseInt(dateStr[2]); + this.storeId = dateStr[3]; + } else { + day = Integer.parseInt(dateStr[2]); + } + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month - 1); + calendar.set(Calendar.DAY_OF_MONTH, day); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + this.date = calendar.getTime(); + System.out.println(DateUtil.toString(date,DateUtil.STANDARD_FORMAT)); + } + +} diff --git a/consumer/src/main/java/cn/lili/trigger/TimeTriggerConsumer.java b/consumer/src/main/java/cn/lili/trigger/TimeTriggerConsumer.java new file mode 100644 index 00000000..54efb65b --- /dev/null +++ b/consumer/src/main/java/cn/lili/trigger/TimeTriggerConsumer.java @@ -0,0 +1,53 @@ +package cn.lili.trigger; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.trigger.interfaces.TimeTriggerExecutor; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import cn.lili.common.trigger.util.TimeTriggerUtil; +import cn.lili.common.utils.SpringContextUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 事件触发消费者 + * + * @author paulG + * @date 2020/11/17 7:19 下午 + */ +@Component +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RocketMQMessageListener(topic = "${lili.data.rocketmq.promotion-topic}", consumerGroup = "${lili.data.rocketmq.promotion-group}") +public class TimeTriggerConsumer implements RocketMQListener { + + private final Cache cache; + + @Override + public void onMessage(TimeTriggerMsg timeTriggerMsg) { + try { + String key = TimeTriggerUtil.generateKey(timeTriggerMsg.getTriggerExecutor(), timeTriggerMsg.getTriggerTime(), timeTriggerMsg.getUniqueKey()); + + if (cache.get(key) == null) { + log.info("执行器执行被取消:{} | 任务标识:{}", timeTriggerMsg.getTriggerExecutor(), timeTriggerMsg.getUniqueKey()); + return; + } + + log.info("执行器执行:" + timeTriggerMsg.getTriggerExecutor()); + log.info("执行器参数:" + JSONUtil.toJsonStr(timeTriggerMsg.getParam())); + + cache.remove(key); + + TimeTriggerExecutor executor = (TimeTriggerExecutor) SpringContextUtil.getBean(timeTriggerMsg.getTriggerExecutor()); + executor.execute(timeTriggerMsg.getParam()); + } catch (Exception e) { + log.error("mq延时任务异常", e); + } + + } + +} diff --git a/consumer/src/main/java/cn/lili/trigger/executor/PromotionTimeTriggerExecutor.java b/consumer/src/main/java/cn/lili/trigger/executor/PromotionTimeTriggerExecutor.java new file mode 100644 index 00000000..b0a44642 --- /dev/null +++ b/consumer/src/main/java/cn/lili/trigger/executor/PromotionTimeTriggerExecutor.java @@ -0,0 +1,74 @@ +package cn.lili.trigger.executor; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.delayqueue.PintuanOrderMessage; +import cn.lili.common.delayqueue.PromotionMessage; +import cn.lili.common.trigger.interfaces.TimeTrigger; +import cn.lili.common.trigger.interfaces.TimeTriggerExecutor; +import cn.lili.common.trigger.model.TimeExecuteConstant; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import cn.lili.common.utils.DateUtil; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.service.PromotionService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 促销事件触发 + * + * @author Chopper + * @version v4.1 + * @date 2020/11/17 7:20 下午 + */ +@Slf4j +@Component(TimeExecuteConstant.PROMOTION_EXECUTOR) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PromotionTimeTriggerExecutor implements TimeTriggerExecutor { + //促销 + private final PromotionService promotionService; + //Rocketmq + private final RocketmqCustomProperties rocketmqCustomProperties; + //延时任务 + private final TimeTrigger timeTrigger; + //订单 + private final OrderService orderService; + + + @Override + public void execute(Object object) { + PromotionMessage promotionMessage = JSONUtil.toBean(JSONUtil.parseObj(object), PromotionMessage.class); + // 促销延时信息 + if (promotionMessage != null && promotionMessage.getPromotionId() != null) { + log.info("促销活动信息消费:{}", promotionMessage); + // 如果为促销活动开始,则需要发布促销活动结束的定时任务 + if (PromotionStatusEnum.START.name().equals(promotionMessage.getPromotionStatus())) { + if (!promotionService.updatePromotionStatus(promotionMessage)) { + log.error("开始促销活动失败: {}", promotionMessage); + return; + } + // 促销活动开始后,设置促销活动结束的定时任务 + promotionMessage.setPromotionStatus(PromotionStatusEnum.END.name()); + String uniqueKey = "{TIME_TRIGGER_" + promotionMessage.getPromotionType() + "}_" + promotionMessage.getPromotionId(); + // 结束时间(延时一分钟) + long closeTime = promotionMessage.getEndTime().getTime() + 60000; + TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.PROMOTION_EXECUTOR, closeTime, promotionMessage, uniqueKey, rocketmqCustomProperties.getPromotionTopic()); + timeTrigger.addDelay(timeTriggerMsg, DateUtil.getDelayTime(promotionMessage.getEndTime().getTime())); + } else { + promotionService.updatePromotionStatus(promotionMessage); + } + return; + } + PintuanOrderMessage pintuanOrderMessage = JSONUtil.toBean(JSONUtil.parseObj(object), PintuanOrderMessage.class); + if (pintuanOrderMessage != null && pintuanOrderMessage.getPintuanId() != null) { + log.info("拼团订单信息消费:{}", pintuanOrderMessage); + // 拼团订单自动处理 + orderService.agglomeratePintuanOrder(pintuanOrderMessage.getPintuanId(), pintuanOrderMessage.getOrderSn()); + } + } + + +} diff --git a/consumer/src/main/resources/application.yml b/consumer/src/main/resources/application.yml new file mode 100644 index 00000000..df974ce6 --- /dev/null +++ b/consumer/src/main/resources/application.yml @@ -0,0 +1,301 @@ +server: + port: 8886 + + servlet: + context-path: / + + # 正式部署时候,解开此处配置,防止文件夹被清除导致的文件上传失败问题 + # multipart: + # location: /Users/lifenlong/Desktop/ceshi + tomcat: + uri-encoding: UTF-8 + threads: + min-spare: 50 + max: 1000 + +# 与Spring Boot 2一样,默认情况下,大多数端点都不通过http公开,我们公开了所有端点。对于生产,您应该仔细选择要公开的端点。 +management: + # health: + # elasticsearch: + # enabled: false + # datasource: + # enabled: false + endpoints: + web: + exposure: + include: '*' +spring: + # 要在其中注册的Spring Boot Admin Server的URL。 + boot: + admin: + client: + url: http://127.0.0.1:8000 + # mongodb + data: + mongodb: + host: 127.0.0.1 + port: 27017 + database: lilishop + username: root + password: lilishop + authentication-database: admin + # replica-set-name: mongoreplset + cache: + type: redis + + jpa: + # 自动生成表结构 + generate-ddl: true + open-in-view: false + # Redis + redis: + host: 127.0.0.1 + port: 6379 + password: lilishop + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: 20 + # 连接池中的最大空闲连接 默认 8 + max-idle: 10 + # 连接池中的最小空闲连接 默认 8 + min-idle: 8 + # 文件大小上传配置 + servlet: + multipart: + max-file-size: 20MB + max-request-size: 20MB + jackson: + time-zone: GMT+8 + serialization: + #关闭jackson 对json做解析 + fail-on-empty-beans: false + + shardingsphere: + datasource: + # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 + names: default-datasource + default-datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: lilishop + maxActive: 20 + initialSize: 5 + maxWait: 60000 + minIdle: 5 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 + poolPreparedStatements: false + #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 + maxOpenPreparedStatements: -1 + #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,log4j2 + #通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + #合并多个DruidDataSource的监控数据 + useGlobalDataSourceStat: true + loginUsername: druid + loginPassword: druid + # sharding: + # default-data-source-name: default-datasource + # #需要拆分的表,可以设置多个 在 li_order 级别即可 + # tables: + # #需要进行分表的逻辑表名 + # li_order: + # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 + # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} + # table-strategy: + # # 分表策略,根据创建日期 + # standard: + # sharding-column: create_time + # #分表策略 + # precise-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + # #范围查询实现 + # range-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + props: + #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 + sql: + show: true + +# 忽略鉴权url +ignored: + urls: + - /editor-app/** + - /actuator** + - /actuator/** + - /MP_verify_qSyvBPhDsPdxvOhC.txt + - /weixin/** + - /source/** + - /buyer/mini-program/** + - /buyer/cashier/** + - /buyer/pageData/** + - /buyer/article/** + - /buyer/goods/** + - /buyer/category/** + - /buyer/shop/** + - /buyer/connect/** + - /buyer/members/smsLogin + - /buyer/members/refresh/* + - /buyer/members/refresh** + - /buyer/promotion/pintuan + - /buyer/promotion/seckill + - /buyer/memberEvaluation/**/goodsEvaluation + - /buyer/memberEvaluation/**/evaluationNumber + - /store/login/** + - /manager/user/login + - /manager/user/refresh/** + - /druid/** + - /swagger-ui.html + - /doc.html + - /swagger-resources/** + - /swagger/** + - /**/**.js + - /**/**.png + - /**/**.css + - /webjars/** + - /v2/api-docs + - /configuration/ui + - /boot-admin + statics: + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + +# Swagger界面内容配置 +swagger: + title: lili API接口文档 + description: lili Api Documentation + version: 1.0.0 + termsOfServiceUrl: https://pickmall.cn + contact: + name: lili + url: https://pickmall.cn + email: admin@pickmall.com + +# Mybatis-plus +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + #缓存开启 + cache-enabled: true + #日志 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 日志 +logging: + # 输出级别 + level: + cn.lili: info + # org.hibernate: debug + # org.springframework: debug + # org.springframework.data.mongodb.core: debug + file: + # 指定路径 + path: lili-logs + # 最大保存天数 + max-history: 7 + # 每个文件最大大小 + max-size: 5MB +#加密参数 +jasypt: + encryptor: + password: lili + +lili: + system: + isDemoSite: true + statistics: + # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 + onlineMember: 48 + # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 + currentOnlineUpdate: 600 + #qq lbs 申请 + lbs: + key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT + sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 + #域名 + domain: + pc: https://pc.b2b2c.pickmall.cn + wap: https://m.b2b2c.pickmall.cn + store: https://store.b2b2c.pickmall.cn + admin: https://admin.b2b2c.pickmall.cn + #api地址 + api: + buyer: https://buyer-api.pickmall.cn + common: https://common-api.pickmall.cn + manager: https://admin-api.pickmall.cn + store: https://store-api.pickmall.cn + + # jwt 细节设定 + jwt-setting: + # token过期时间(分钟) + tokenExpireTime: 60 + + # 使用Spring @Cacheable注解失效时间 + cache: + # 过期时间 单位秒 永久不过期设为-1 + timeout: 1500 + #多线程配置 + thread: + corePoolSize: 5 + maxPoolSize: 50 + queueCapacity: 50 + data: + elasticsearch: + cluster-name: elasticsearch + cluster-nodes: 127.0.0.1:9200 + index: + number-of-replicas: 0 + number-of-shards: 3 + index-prefix: lili + schema: http + # account: + # username: elastic + # password: LiLiShopES + + rocketmq: + promotion-topic: lili_promotion_topic + promotion-group: lili_promotion_group + msg-ext-topic: lili_msg_topic + msg-ext-group: lili_msg_group + goods-topic: lili_goods_topic + goods-group: lili_goods_group + order-topic: lili_order_topic + order-group: lili_order_group + member-topic: lili_member_topic + member-group: lili_member_group + other-topic: lili_other_topic + other-group: lili_other_group + notice-topic: lili_notice_topic + notice-group: lili_notice_group + notice-send-topic: lili_send_notice_topic + notice-send-group: lili_send_notice_group + after-sale-topic: lili_after_sale_topic + after-sale-group: lili_after_sale_group +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: lili_group + send-message-timeout: 30000 + +xxl: + job: + admin: + addresses: http://127.0.0.1:9001/xxl-job-admin + executor: + appname: xxl-job-executor-lilishop + address: + ip: + port: 8891 + logpath: ./xxl-job/executor + logretentiondays: 7 \ No newline at end of file diff --git a/consumer/src/test/java/cn/lili/buyer/test/cart/CartTest.java b/consumer/src/test/java/cn/lili/buyer/test/cart/CartTest.java new file mode 100644 index 00000000..97631024 --- /dev/null +++ b/consumer/src/test/java/cn/lili/buyer/test/cart/CartTest.java @@ -0,0 +1,34 @@ +package cn.lili.buyer.test.cart; + +import cn.lili.event.impl.StockUpdateExecute; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import org.junit.jupiter.api.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 +class CartTest { + + @Autowired + private StockUpdateExecute stockUpdateExecute; + + //订单支付,库存扣减单元测试 + @Test + void stockUpdate() { + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setOrderSn("O202102221363668802717351937");//订单sn + orderMessage.setNewStatus(OrderStatusEnum.PAID); + orderMessage.setPaymentMethod(PaymentMethodEnum.WALLET.name()); + stockUpdateExecute.orderChange(orderMessage); + } + + +} diff --git a/consumer/src/test/java/cn/lili/buyer/test/cart/MemberStatisticsTest.java b/consumer/src/test/java/cn/lili/buyer/test/cart/MemberStatisticsTest.java new file mode 100644 index 00000000..de7e3171 --- /dev/null +++ b/consumer/src/test/java/cn/lili/buyer/test/cart/MemberStatisticsTest.java @@ -0,0 +1,22 @@ +package cn.lili.buyer.test.cart; + +import cn.lili.timetask.handler.impl.statistics.MemberStatisticsExecute; +import org.junit.jupiter.api.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 +class MemberStatisticsTest { + + @Autowired + private MemberStatisticsExecute memberStatisticsExecute; + + @Test + void customSetting() { + memberStatisticsExecute.execute(); + } +} diff --git a/consumer/src/test/java/cn/lili/buyer/test/cart/OnlineTest.java b/consumer/src/test/java/cn/lili/buyer/test/cart/OnlineTest.java new file mode 100644 index 00000000..741f002a --- /dev/null +++ b/consumer/src/test/java/cn/lili/buyer/test/cart/OnlineTest.java @@ -0,0 +1,64 @@ +package cn.lili.buyer.test.cart; + +import cn.lili.common.utils.DateUtil; +import cn.lili.timetask.handler.impl.statistics.OnlineMemberStatistics; +import org.junit.jupiter.api.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; + +import java.util.Calendar; +import java.util.Random; + +/** + * 订单库存扣减 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +class OnlineTest { + + @Autowired + private OnlineMemberStatistics onlineMemberStatistics; + + //订单支付,库存扣减单元测试 + @Test + void everyHour() { + onlineMemberStatistics.execute(); + } + + //订单支付,库存扣减单元测试 + @Test + void customSetting() { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - 48 ); + //循环填充数据 + for (int i = 0; i < 48; i++) { + calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1); + System.out.println(DateUtil.toString(calendar.getTime(),"")); + Random random = new Random(); + onlineMemberStatistics.execute(calendar.getTime(), random.nextInt(1000000)); + } + } + + public static void main(String[] args) { + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - 48 - 1); + //循环填充数据 + for (int i = 0; i < 48; i++) { + calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1); + System.out.println(calendar.getTime().getTime()); + } + } + + +} diff --git a/framework/pom.xml b/framework/pom.xml new file mode 100644 index 00000000..a47a8901 --- /dev/null +++ b/framework/pom.xml @@ -0,0 +1,391 @@ + + + 4.0.0 + + + cn.lili + lili-shop-parent + 1.0.1 + + + framework + jar + + + 1.9.6 + 4.13.40.ALL + 5.1.48 + 3.3.1.tmp + 5.5.8 + 2.0.3.RELEASE + 3.0.0 + 2.9.10 + 1.18.10 + 6.0.11 + 4.5.18 + 3.11.1 + 2.0.1 + 2.1.1 + 0.10.7 + 4.7.2 + 4.0.0 + 1.1.20 + 1.0.3 + 4.7.2 + 4.4.1 + 4.5.12 + 2.9.10 + UTF-8 + UTF-8 + 1.8 + true + 2.0.8 + 2.3.1 + 1.21 + 1.2 + + + + + + javax.interceptor + javax.interceptor-api + ${interceptor-api} + + + de.codecentric + spring-boot-admin-starter-client + ${de.codecentric} + + + + com.google.zxing + core + 3.4.1 + + + com.google.zxing + javase + 3.4.1 + + + org.slf4j + slf4j-api + 1.7.28 + + + + com.github.xkzhangsan + xk-time + 2.2.0 + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-data-elasticsearch + + + HdrHistogram + org.hdrhistogram + + + + + org.springframework + spring-context-support + + + org.springframework.integration + spring-integration-redis + + + org.apache.commons + commons-pool2 + + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-websocket + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus-version} + + + org.mybatis.spring.boot + mybatis-spring-boot-starter-test + 1.3.2 + + + + mysql + mysql-connector-java + ${mysql-connector-version} + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + ${knife4j.version} + + + + + + + + + + + + + + + + + + + + + + + com.github.xiaoymin + swagger-bootstrap-ui + ${swagger-bootstrap-ui-version} + + + + cn.hutool + hutool-all + ${Hutool-version} + + + io.github.biezhi + TinyPinyin + ${TinyPinyin-verions} + + + + org.projectlombok + lombok + ${lombok-version} + + + + com.github.ulisesbocchio + jasypt-spring-boot-starter + ${jasypt-version} + + + + com.ibeetl + beetl + ${beetl-version} + + + + io.minio + minio + ${minio-version} + + + jsr305 + com.google.code.findbugs + + + guava + com.google.guava + + + + + + com.aliyun + aliyun-java-sdk-core + ${aliyun-version} + + + + com.aliyun.oss + aliyun-sdk-oss + ${aliyun-sdk-oss-version} + + + aliyun-java-sdk-core + com.aliyun + + + + + + com.aliyun + dysmsapi20170525 + ${aliyun-sdk-dysms-version} + + + + org.codehaus.groovy + groovy + + + + org.apache.rocketmq + rocketmq-spring-boot-starter + ${rocketmq-version} + + + + io.jsonwebtoken + jjwt-api + ${jwt-version} + + + io.jsonwebtoken + jjwt-impl + ${jwt-version} + runtime + + + io.jsonwebtoken + jjwt-jackson + ${jwt-version} + runtime + + + + org.springframework.boot + spring-boot-starter-test + + + + + + + + + + + + org.antlr + antlr4 + ${antlr4-version} + + + + org.apache.commons + commons-lang3 + + + + + + org.apache.shardingsphere + sharding-jdbc-spring-boot-starter + ${sharding-jdbc-version} + + + groovy + org.codehaus.groovy + + + + + + org.apache.shardingsphere + sharding-jdbc-spring-namespace + ${sharding-jdbc-version} + + + + com.alibaba + druid + ${druid-version} + + + com.xkcoding.http + simple-http + ${simple-http-version} + + + + org.antlr + antlr4-runtime + ${antlr4-runtime-version} + + + + + + + + + + + + + + com.alipay.sdk + alipay-sdk-java + ${alipay-sdk-version} + + + + + eu.bitwalker + UserAgentUtils + ${userAgentUtils} + + + + + \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/base/BaseEntity.java b/framework/src/main/java/cn/lili/base/BaseEntity.java new file mode 100644 index 00000000..b87c511a --- /dev/null +++ b/framework/src/main/java/cn/lili/base/BaseEntity.java @@ -0,0 +1,81 @@ +package cn.lili.base; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import java.io.Serializable; +import java.util.Date; + + +/** + * 数据库基础实体类 + * + * @author Chopper + * @version v1.0 + * @since 2020/8/20 14:34 + */ +@Data +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +@JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler", "fieldHandler"}) +@AllArgsConstructor +@NoArgsConstructor +public abstract class BaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @LastModifiedBy + @TableField(fill = FieldFill.UPDATE) + @ApiModelProperty(value = "更新者", hidden = true) + private String updateBy; + + @LastModifiedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.UPDATE) + @ApiModelProperty(value = "更新时间", hidden = true) + private Date updateTime; + + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "删除标志 true/false 删除/未删除", hidden = true) + private Boolean deleteFlag ; + +} diff --git a/framework/src/main/java/cn/lili/base/IdEntity.java b/framework/src/main/java/cn/lili/base/IdEntity.java new file mode 100644 index 00000000..5909ebce --- /dev/null +++ b/framework/src/main/java/cn/lili/base/IdEntity.java @@ -0,0 +1,45 @@ +package cn.lili.base; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import java.io.Serializable; + + +/** + * 数据库基础实体类 + * + * @author Chopper + * @version v1.0 + * @since 2020/8/20 14:34 + */ +@Data +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +@JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler", "fieldHandler"}) +@AllArgsConstructor +@NoArgsConstructor +public abstract class IdEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + +} diff --git a/framework/src/main/java/cn/lili/base/mybatisplus/MyMetaObjectHandler.java b/framework/src/main/java/cn/lili/base/mybatisplus/MyMetaObjectHandler.java new file mode 100644 index 00000000..fd966853 --- /dev/null +++ b/framework/src/main/java/cn/lili/base/mybatisplus/MyMetaObjectHandler.java @@ -0,0 +1,46 @@ +package cn.lili.base.mybatisplus; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.SnowFlake; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.util.Date; + +/** + * 字段填充审计 + * + * @author lili + */ +@Component +public class MyMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + AuthUser authUser = UserContext.getCurrentUser(); + if (authUser != null) { + this.setFieldValByName("createBy", authUser.getUsername(), metaObject); + } + this.setFieldValByName("createTime", new Date(), metaObject); + //有值,则写入 + if (metaObject.hasGetter("deleteFlag")) { + this.setFieldValByName("deleteFlag", false, metaObject); + } + if (metaObject.hasGetter("id")) { + this.setFieldValByName("id", String.valueOf(SnowFlake.getId()), metaObject); + } + } + + @Override + public void updateFill(MetaObject metaObject) { + + AuthUser authUser = UserContext.getCurrentUser(); + if (authUser != null) { + this.setFieldValByName("updateBy", authUser.getUsername(), metaObject); + } + this.setFieldValByName("updateTime", new Date(), metaObject); + } +} + diff --git a/framework/src/main/java/cn/lili/base/mybatisplus/MybatisPlusConfig.java b/framework/src/main/java/cn/lili/base/mybatisplus/MybatisPlusConfig.java new file mode 100644 index 00000000..9e5853a8 --- /dev/null +++ b/framework/src/main/java/cn/lili/base/mybatisplus/MybatisPlusConfig.java @@ -0,0 +1,21 @@ +package cn.lili.base.mybatisplus; + +import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Chopper + */ +@Configuration +@MapperScan({"cn.lili.modules.*.*.mapper","cn.lili.modules.*.mapper"}) +public class MybatisPlusConfig { + /** + * 分页插件,自动识别数据库类型 + */ + @Bean + public PaginationInterceptor paginationInterceptor() { + return new PaginationInterceptor(); + } +} diff --git a/framework/src/main/java/cn/lili/common/aop/limiter/LimitInterceptor.java b/framework/src/main/java/cn/lili/common/aop/limiter/LimitInterceptor.java new file mode 100644 index 00000000..0e35a46a --- /dev/null +++ b/framework/src/main/java/cn/lili/common/aop/limiter/LimitInterceptor.java @@ -0,0 +1,98 @@ +package cn.lili.common.aop.limiter; + +import cn.lili.common.aop.limiter.annotation.LimitPoint; +import com.google.common.collect.ImmutableList; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.io.Serializable; +import java.lang.reflect.Method; + +/** + * 流量拦截 + * @author Chopper + */ +@Aspect +@Configuration +@Slf4j +public class LimitInterceptor { + private RedisTemplate redisTemplate; + + private DefaultRedisScript limitScript; + + @Autowired + public void setRedisTemplate(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(DefaultRedisScript limitScript) { + this.limitScript = limitScript; + } + + @Around("execution(public * *(..)) && @annotation(cn.lili.common.aop.limiter.annotation.LimitPoint)") + public Object interceptor(ProceedingJoinPoint pjp) { + MethodSignature signature = (MethodSignature) pjp.getSignature(); + Method method = signature.getMethod(); + LimitPoint limitPointAnnotation = method.getAnnotation(LimitPoint.class); + LimitType limitType = limitPointAnnotation.limitType(); + String name = limitPointAnnotation.name(); + String key; + int limitPeriod = limitPointAnnotation.period(); + int limitCount = limitPointAnnotation.limit(); + switch (limitType) { + case IP: + key = limitPointAnnotation.key() + getIpAddress(); + break; + case CUSTOMER: + key = limitPointAnnotation.key(); + break; + default: + key = StringUtils.upperCase(method.getName()); + } + ImmutableList keys = ImmutableList.of(StringUtils.join(limitPointAnnotation.prefix(), key)); + try { + Number count = redisTemplate.execute(limitScript, keys, limitCount, limitPeriod); + log.info("Access try count is {} for name={} and key = {}", count, name, key); + if (count != null && count.intValue() <= limitCount) { + return pjp.proceed(); + } else { + throw new RuntimeException("访问过于频繁,请稍后再试"); + } + } catch (Throwable e) { + if (e instanceof RuntimeException) { + throw new RuntimeException(e.getLocalizedMessage()); + } + throw new RuntimeException("服务器异常,请稍后再试"); + } + } + + + private static final String UNKNOWN = "unknown"; + + public String getIpAddress() { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return ip; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/common/aop/limiter/LimitType.java b/framework/src/main/java/cn/lili/common/aop/limiter/LimitType.java new file mode 100644 index 00000000..110f2195 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/aop/limiter/LimitType.java @@ -0,0 +1,20 @@ +package cn.lili.common.aop.limiter; + + +/** + * redis 限流类型 + * + * @author Chopper + * @since 2018年2月02日 下午4:58:52 + */ + +public enum LimitType { + /** + * 自定义key(即全局限流) + */ + CUSTOMER, + /** + * 根据请求者IP(IP限流) + */ + IP +} diff --git a/framework/src/main/java/cn/lili/common/aop/limiter/annotation/LimitPoint.java b/framework/src/main/java/cn/lili/common/aop/limiter/annotation/LimitPoint.java new file mode 100644 index 00000000..cee99c07 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/aop/limiter/annotation/LimitPoint.java @@ -0,0 +1,64 @@ +package cn.lili.common.aop.limiter.annotation; + + +import cn.lili.common.aop.limiter.LimitType; + +import java.lang.annotation.*; + +/** + * 限流注解 + * + * @author Chopper + * @since 2018-02-05 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface LimitPoint { + /** + * 资源的名字 无实际意义,但是可以用于排除异常 + * + * @return String + */ + String name() default ""; + + /** + * 资源的key + *

+ * 如果下方 limitType 值为自定义,那么全局限流参数来自于此 + * 如果limitType 为ip,则限流条件 为 key+ip + * + * @return String + */ + String key() default ""; + + /** + * Key的prefix redis前缀,可选 + * + * @return String + */ + String prefix() default ""; + + /** + * 给定的时间段 单位秒 + * + * @return int + */ + int period() default 60; + + /** + * 最多的访问限制次数 + * + * @return int + */ + int limit() default 10; + + /** + * 类型 ip限制 还是自定义key值限制 + * 建议使用ip,自定义key属于全局限制,ip则是某节点设置,通常情况使用IP + * + * @return LimitType + */ + LimitType limitType() default LimitType.IP; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/common/aop/syslog/annotation/SystemLogPoint.java b/framework/src/main/java/cn/lili/common/aop/syslog/annotation/SystemLogPoint.java new file mode 100644 index 00000000..1928d019 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/aop/syslog/annotation/SystemLogPoint.java @@ -0,0 +1,29 @@ +package cn.lili.common.aop.syslog.annotation; + +import java.lang.annotation.*; + +/** + * 系统日志AOP注解 + * + * @author Chopper + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface SystemLogPoint { + + /** + * 日志名称 + * + * @return + */ + String description() default ""; + + /** + * 自定义日志内容 + * + * @return + */ + String customerLog() default ""; +} diff --git a/framework/src/main/java/cn/lili/common/aop/syslog/interceptor/SystemLogAspect.java b/framework/src/main/java/cn/lili/common/aop/syslog/interceptor/SystemLogAspect.java new file mode 100644 index 00000000..e5b7f90d --- /dev/null +++ b/framework/src/main/java/cn/lili/common/aop/syslog/interceptor/SystemLogAspect.java @@ -0,0 +1,172 @@ +package cn.lili.common.aop.syslog.interceptor; + +import cn.lili.common.aop.syslog.annotation.SystemLogPoint; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.IpHelper; +import cn.lili.common.utils.SpelUtil; +import cn.lili.common.utils.ThreadPoolUtil; +import cn.lili.modules.base.entity.systemlog.SystemLogVO; +import cn.lili.modules.connect.util.IpUtils; +import cn.lili.modules.permission.service.SystemLogService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.NamedThreadLocal; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Spring AOP实现日志管理 + * + * @author Chopper + */ +@Aspect +@Component +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SystemLogAspect { + + /** + * 启动线程异步记录日志 + */ + private static final ThreadLocal beginTimeThreadLocal = new NamedThreadLocal<>("SYSTEM-LOG"); + + + private final SystemLogService systemLogService; + + private final HttpServletRequest request; + + private final IpHelper ipHelper; + + /** + * Controller层切点,注解方式 + */ + @Pointcut("@annotation(cn.lili.common.aop.syslog.annotation.SystemLogPoint)") + public void controllerAspect() { + + } + + /** + * 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间 + */ + @Before("controllerAspect()") + public void doBefore() { + beginTimeThreadLocal.set(new Date()); + } + + + /** + * 后置通知(在方法执行之后并返回数据) 用于拦截Controller层无异常的操作 + * + * @param joinPoint 切点 + */ + @AfterReturning(returning = "rvt", pointcut = "controllerAspect()") + public void after(JoinPoint joinPoint, Object rvt) { + try { + Map map = spelFormat(joinPoint, rvt); + String description = map.get("description").toString(); + String customerLog = map.get("customerLog").toString(); + + Map logParams = request.getParameterMap(); + AuthUser authUser = UserContext.getCurrentUser(); + SystemLogVO systemLogVO = new SystemLogVO(); + + if (authUser == null) { + //如果是商家则记录商家id,否则记录-1,代表平台id + systemLogVO.setStoreId(-2L); + //请求用户 + systemLogVO.setUsername("游客"); + } else { + //如果是商家则记录商家id,否则记录-1,代表平台id + systemLogVO.setStoreId(authUser.getRole().equals(UserEnums.STORE) ? Long.parseLong(authUser.getStoreId()) : -1); + //请求用户 + systemLogVO.setUsername(authUser.getUsername()); + } + + //日志标题 + systemLogVO.setName(description); + //日志请求url + systemLogVO.setRequestUrl(request.getRequestURI()); + //请求方式 + systemLogVO.setRequestType(request.getMethod()); + //请求参数 + systemLogVO.setMapToParams(logParams); + //响应参数 此处数据太大了,所以先注释掉 +// systemLogVO.setResponseBody(JSONUtil.toJsonStr(rvt)); + //请求IP + systemLogVO.setIp(IpUtils.getIpAddress(request)); + //IP地址 + systemLogVO.setIpInfo(ipHelper.getIpCity(request)); + //写入自定义日志内容 + systemLogVO.setCustomerLog(customerLog); + //请求开始时间 + long beginTime = beginTimeThreadLocal.get().getTime(); + long endTime = System.currentTimeMillis(); + //请求耗时 + Long usedTime = endTime - beginTime; + systemLogVO.setCostTime(usedTime.intValue()); + //调用线程保存 + ThreadPoolUtil.getPool().execute(new SaveSystemLogThread(systemLogVO, systemLogService)); + + } catch (Exception e) { + log.error("系统日志保存异常", e); + } + } + + /** + * 保存日志 + */ + private static class SaveSystemLogThread implements Runnable { + + private final SystemLogVO systemLogVO; + private final SystemLogService systemLogService; + + public SaveSystemLogThread(SystemLogVO systemLogVO, SystemLogService systemLogService) { + this.systemLogVO = systemLogVO; + this.systemLogService = systemLogService; + } + + @Override + public void run() { + try { + systemLogService.saveLog(systemLogVO); + } catch (Exception e) { + log.error("系统日志保存异常,内容{}:", systemLogVO, e); + } + } + } + + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param joinPoint 切点 + * @return 方法描述 + * @throws Exception + */ + private static Map spelFormat(JoinPoint joinPoint, Object rvt) { + + Map result = new HashMap<>(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + SystemLogPoint systemLogPoint = signature.getMethod().getAnnotation(SystemLogPoint.class); + String description = systemLogPoint.description(); + String customerLog = SpelUtil.compileParams(joinPoint, rvt, systemLogPoint.customerLog()); + + result.put("description", description); + result.put("customerLog", customerLog); + return result; + } + +} diff --git a/framework/src/main/java/cn/lili/common/cache/Cache.java b/framework/src/main/java/cn/lili/common/cache/Cache.java new file mode 100644 index 00000000..7b027b73 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/cache/Cache.java @@ -0,0 +1,235 @@ +package cn.lili.common.cache; + + +import org.springframework.data.redis.core.ZSetOperations; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * 缓存接口 + * + * @author Chopper + */ +public interface Cache { + + /** + * Get an item from the cache, nontransactionally + * + * @param key + * @return the cached object or null + */ + T get(Object key); + + /** + * Get an item from the cache, nontransactionally + * + * @param key + * @return the cached object or null + */ + String getString(Object key); + + + /** + * multiGet + * + * @param keys 要查询的key集合 + * @return + */ + List multiGet(Collection keys); + + /** + * 批量set + * + * @param map + */ + void multiSet(Map map); + + + /** + * 批量删除 + * + * @param keys 要删除的key集合 + */ + void multiDel(Collection keys); + + /** + * Add an item to the cache, nontransactionally, with + * failfast semantics + * + * @param key + * @param value + */ + void put(Object key, T value); + + /** + * 往缓存中写入内容 + * + * @param key + * @param value + * @param exp 超时时间,单位为秒 + */ + void put(Object key, T value, Long exp); + + /** + * 往缓存中写入内容 + * + * @param key + * @param value + * @param exp + * @param timeUnit 写入单位 + */ + void put(Object key, T value, Long exp, TimeUnit timeUnit); + + /** + * 删除 + * + * @param key + */ + void remove(Object key); + + /** + * 删除 + * + * @param key + */ + void vagueDel(Object key); + + /** + * Clear the cache + */ + void clear(); + + + /** + * 往缓存中写入内容 + * + * @param key 缓存key + * @param hashKey 缓存中hashKey + * @param hashValue hash值 + */ + void putHash(Object key, Object hashKey, Object hashValue); + + /** + * 玩缓存中写入内容 + * + * @param key + * @param map + */ + void putAllHash(Object key, Map map); + + /** + * 读取缓存值 + * + * @param key + * @param hashKey + * @return + */ + T getHash(Object key, Object hashKey); + + /** + * 读取缓存值 + * + * @param key + * @return + */ + Map getHash(Object key); + + /** + * 是否包含 + * + * @param key + * @return + */ + boolean hasKey(Object key); + + + /** + * 模糊匹配key + * + * @param pattern + * @return + */ + List keys(String pattern); + + + //-----------------------------------------------用于特殊场景,redis去重计数--------------------------------------------- + + /** + * 累计数目 + * 效率较高的 计数器 + * 如需清零,按照普通key 移除即可 + * + * @param key + * @return + */ + Long cumulative(Object key, Object value); + + /** + * 计数器结果 + *

+ * 效率较高的 计数器 统计返回 + * 如需清零,按照普通key 移除即可 + * + * @param key + * @return + */ + Long counter(Object key); + + /** + * 批量计数 + * + * @param keys 要查询的key集合 + * @return + */ + List multiCounter(Collection keys); + + /** + * 计数器结果 + *

+ * 效率较高的 计数器 统计返回 + * 如需清零,按照普通key 移除即可 + * + * @param key + * @return + */ + Long mergeCounter(Object... key); + //---------------------------------------------------用于特殊场景,redis去重统计----------------------------------------- + + //-----------------------------------------------redis计数--------------------------------------------- + + /** + * redis 计数器 累加 + * 注:到达liveTime之后,该次增加取消,即自动-1,而不是redis值为空 + * + * @param key 为累计的key,同一key每次调用则值 +1 + * @param liveTime 单位秒后失效 + * @return + */ + Long incr(String key, long liveTime); + //-----------------------------------------------redis计数--------------------------------------------- + + /** + * 使用Sorted Set记录keyword + * zincrby命令,对于一个Sorted Set,存在的就把分数加x(x可自行设定),不存在就创建一个分数为1的成员 + * + * @param sortedSetName sortedSetName的Sorted Set不用预先创建,不存在会自动创建,存在则向里添加数据 + * @param keyword 关键词 + */ + void incrementScore(String sortedSetName, String keyword); + + /** + * zrevrange命令, 查询Sorted Set中指定范围的值 + * 返回的有序集合中,score大的在前面 + * zrevrange方法无需担心用于指定范围的start和end出现越界报错问题 + * + * @param sortedSetName sortedSetName + * @param start 查询范围开始位置 + * @param end 查询范围结束位置 + * @return + */ + Set> reverseRangeWithScores(String sortedSetName, Integer start, Integer end); +} diff --git a/framework/src/main/java/cn/lili/common/cache/CachePrefix.java b/framework/src/main/java/cn/lili/common/cache/CachePrefix.java new file mode 100644 index 00000000..65466d36 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/cache/CachePrefix.java @@ -0,0 +1,474 @@ +package cn.lili.common.cache; + +import cn.lili.common.security.enums.UserEnums; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; + +/** + * 缓存前缀 + * + * @author pikachu + * @version 4.1 + * @since 1.0 + * 2018/3/19 + */ +public enum CachePrefix { + + + /** + * nonce + */ + NONCE, + + /** + * 在线人数 + */ + ONLINE_NUM, + + + /** + * 会员分布数据 + */ + MEMBER_DISTRIBUTION, + + /** + * 在线会员统计 + */ + ONLINE_MEMBER, + + /** + * token 信息 + */ + ACCESS_TOKEN, + /** + * token 信息 + */ + REFRESH_TOKEN, + /** + * 联合登录响应 + */ + CONNECT_RESULT, + /** + * 微信联合登陆 session key + */ + SESSION_KEY, + /** + * 权限 + */ + PERMISSION_LIST, + /** + * 部门id列表 + */ + DEPARTMENT_IDS, + + /** + * 用户错误登录限制 + */ + LOGIN_TIME_LIMIT, + /** + * 系统设置 + */ + SETTING, + + /** + * 验证码滑块源 + */ + VERIFICATION, + + /** + * 验证码滑块源 + */ + VERIFICATION_IMAGE, + + /** + * 快递平台 + */ + EXPRESS, + + /** + * 图片验证码 + */ + CAPTCHA, + + /** + * 商品 + */ + GOODS, + + /** + * 商品SKU + */ + GOODS_SKU, + + /** + * 运费模板脚本 + */ + SHIP_SCRIPT, + + /** + * 商品sku + */ + SKU, + + /** + * sku库存 + */ + SKU_STOCK, + + /** + * 促销商品sku库存 + */ + PROMOTION_GOODS_STOCK, + + /** + * 商品库存 + */ + GOODS_STOCK, + + /** + * 商品分类 + */ + CATEGORY, + /** + * 浏览次数 + */ + VISIT_COUNT, + /** + * 存储方案 + */ + UPLOADER, + /** + * 地区 + */ + REGION, + + /** + * 短信网关 + */ + SPlATFORM, + /** + * 短信验证码前缀 + */ + _CODE_PREFIX, + /** + * smtp + */ + SMTP, + /** + * 系统设置 + */ + SETTINGS, + /** + * 电子面单 + */ + WAYBILL, + /** + * 短信验证码 + */ + SMS_CODE, + /** + * 邮箱验证码 + */ + EMAIL_CODE, + /** + * 管理员角色权限对照表 + */ + ADMIN_URL_ROLE, + + /** + * 店铺管理员角色权限对照表 + */ + SHOP_URL_ROLE, + + /** + * 手机验证标识 + */ + MOBILE_VALIDATE, + + /** + * 邮箱验证标识 + */ + EMAIL_VALIDATE, + + /** + * 店铺运费模版列表 + */ + SHIP_TEMPLATE, + + /** + * 店铺中某个运费模版 + */ + SHIP_TEMPLATE_ONE, + + //================促销================= + /** + * 促销活动 + */ + PROMOTION, + /** + * 促销活动 + */ + PROMOTION_GOODS, + + /*** 单品立减 */ + STORE_ID_MINUS, + + /*** 第二件半价 */ + STORE_ID_HALF_PRICE, + + /*** 满优惠 */ + STORE_ID_FULL_DISCOUNT, + + /** + * 限时抢购活动缓存key前缀 + */ + STORE_ID_SECKILL, + + /** + * 团购活动缓存key前缀 + */ + STORE_ID_GROUP_BUY, + + /** + * 积分商品缓存key前缀 + */ + STORE_ID_EXCHANGE, + + + //================交易================= + + /** + * 购物车原始数据 + */ + CART_ORIGIN_DATA_PREFIX, + + /** + * 立即购买原始数据 + */ + BUY_NOW_ORIGIN_DATA_PREFIX, + + /** + * 交易原始数据 + */ + TRADE_ORIGIN_DATA_PREFIX, + + /** + * 立即购买sku + */ + CART_SKU_PREFIX, + + /** + * 购物车视图 + */ + CART_MEMBER_ID_PREFIX, + + /** + * 购物车,用户选择的促销信息 + */ + CART_PROMOTION_PREFIX, + + + /** + * 交易_交易价格的前缀 + */ + PRICE_SESSION_ID_PREFIX, + + /** + * 交易_交易单 + */ + TRADE_SESSION_ID_PREFIX, + + + /** + * 结算参数 + */ + CHECKOUT_PARAM_ID_PREFIX, + + /** + * 交易单号前缀 + */ + TRADE_SN_CACHE_PREFIX, + + /** + * 订单编号前缀 + */ + ORDER_SN_CACHE_PREFIX, + /** + * 订单编号标记 + */ + ORDER_SN_SIGN_CACHE_PREFIX, + /** + * 订单编号前缀 + */ + PAY_LOG_SN_CACHE_PREFIX, + + /** + * 合同编号 + */ + CONTRACT_SN_CACHE_PREFIX, + + + /** + * 零钱 + */ + SMALL_CHANGE_CACHE_PREFIX, + + /** + * 售后服务单号前缀 + */ + AFTER_SALE_SERVICE_PREFIX, + + /** + * 交易 + */ + TRADE, + + /** + * 站点导航栏 + */ + SITE_NAVIGATION, + + /** + * 支付参数 + */ + PAYMENT_CONFIG, + + /** + * 流程控制 + */ + FLOW, + + /** + * 热门搜索 + */ + HOT_WORD, + + /** + * 会员积分 + */ + MEMBER_POINT, + + /** + * 会员积分 + */ + POINT_ORDER, + + + /** + * 微博登录 + */ + WEIBO_STATE, + /** + * 微博登录 + */ + QQ_STATE, + /** + * 微博登录 + */ + GITHUB_STATE, + /** + * 验证码key + */ + VERIFICATION_KEY, + /** + * 验证码验证结果 + */ + VERIFICATION_RESULT, + /** + * 微信CGItoken + */ + WECHAT_CGI_ACCESS_TOKEN, + /** + * 微信JSApitoken + */ + WECHAT_JS_API_TOKEN, + /** + * 微信会话信息 + */ + WECHAT_SESSION_PARAMS, + /** + * 第三方用户权限 + */ + ALIPAY_CONFIG, + /** + * 微信支付配置 + */ + WECHAT_PAY_CONFIG, + /** + * 微信支付平台证书配置 + */ + WECHAT_PLAT_FORM_CERT, + /** + * 第三方用户权限 + */ + CONNECT_AUTH, + /** + * 平台PageView 统计 + */ + PV, + /** + * 平台UserView 统计 + */ + UV, + /** + * 平台 商品PV 统计 + */ + GOODS_PV, + /** + * 平台 商品UV 统计 + */ + GOODS_UV, + /** + * 店铺PageView 统计 + */ + STORE_PV, + /** + * 店铺UserView 统计 + */ + STORE_UV, + /** + * 店铺 商品PV 统计 + */ + STORE_GOODS_PV, + /** + * 店铺 商品UV 统计 + */ + STORE_GOODS_UV, + /** + * 分销员 + */ + DISTRIBUTION, + + /** + * 找回手机 + */ + FIND_MOBILE, + /** + * 文章分类 + */ + ARTICLE_CATEGORY, + /** + * 文章 + */ + ARTICLE_CACHE + ; + + + public static String removePrefix(String str) { + return str.substring(str.lastIndexOf("}_") + 2); + } + + //通用获取缓存key值 + public String getPrefix() { + return "{" + this.name() + "}_"; + } + + //通用获取缓存key值 + public String getPrefix(PromotionTypeEnum typeEnum) { + return "{" + this.name() + "_" + typeEnum.name() + "}_"; + } + + //获取缓存key值 + 用户端,例如:三端都有用户体系,需要分别登录,如果用户名一致,则redis中的权限可能会冲突出错 + public String getPrefix(UserEnums user) { + return "{" + this.name() + "_" + user.name() + "}_"; + } +} diff --git a/framework/src/main/java/cn/lili/common/cache/MybatisRedisCache.java b/framework/src/main/java/cn/lili/common/cache/MybatisRedisCache.java new file mode 100644 index 00000000..e47185bd --- /dev/null +++ b/framework/src/main/java/cn/lili/common/cache/MybatisRedisCache.java @@ -0,0 +1,101 @@ +package cn.lili.common.cache; + +import cn.lili.common.utils.SpringContextUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.cache.Cache; +import org.springframework.data.redis.connection.RedisServerCommands; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.util.CollectionUtils; + +import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * MybatisRedisCache + * + * @author Chopper + * @version v1.0 + * @since + * 2020-04-01 2:59 下午 + * 不赞成使用此方式注解,统一使用Cacheable 更为合适 + * + * 使用方法 @CacheNamespace(implementation= MybatisRedisCache.class,eviction=MybatisRedisCache.class) + */ +@Deprecated +@Slf4j +public class MybatisRedisCache implements Cache { + + private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true); + + + private RedisTemplate getRedisTemplate() { + return (RedisTemplate) SpringContextUtil.getBean("redisTemplate"); + } + + private final String id; + + public MybatisRedisCache(final String id) { + if (id == null) { + throw new IllegalArgumentException("Cache instances require an ID"); + } + this.id = id; + } + + @Override + public String getId() { + return this.id; + } + + @Override + public void putObject(Object key, Object value) { + try { + if (value != null) { + log.info("写入缓存:" + key.toString()+"----"+value.toString()); + getRedisTemplate().opsForValue().set(key.toString(), value); + } + } catch (Exception e) { + log.error("写入mybatis缓存异常 ", e); + } + } + + @Override + public Object getObject(Object key) { + try { + if (key != null) { + log.info("获取缓存:" + key); + return getRedisTemplate().opsForValue().get(key.toString()); + } + } catch (Exception e) { + log.error("mybatis缓存获取异常 ", e); + } + return null; + } + + @Override + public Object removeObject(Object key) { + if (key != null) { + getRedisTemplate().delete(key.toString()); + } + return null; + } + + @Override + public void clear() { + Set keys = getRedisTemplate().keys("*:" + this.id + "*"); + if (!CollectionUtils.isEmpty(keys)) { + getRedisTemplate().delete(keys); + } + } + + @Override + public int getSize() { + Long size = getRedisTemplate().execute(RedisServerCommands::dbSize); + return size.intValue(); + } + + @Override + public ReadWriteLock getReadWriteLock() { + return this.readWriteLock; + } +} diff --git a/framework/src/main/java/cn/lili/common/cache/impl/RedisCache.java b/framework/src/main/java/cn/lili/common/cache/impl/RedisCache.java new file mode 100644 index 00000000..39925133 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/cache/impl/RedisCache.java @@ -0,0 +1,243 @@ +package cn.lili.common.cache.impl; + +import cn.lili.common.cache.Cache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.core.*; +import org.springframework.data.redis.support.atomic.RedisAtomicLong; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * redis 缓存实现 + * + * @author Chopepr + */ +@Component +public class RedisCache implements Cache { + + @Autowired + private RedisTemplate redisTemplate; + + public RedisCache() { + + } + + @Override + public Object get(Object key) { + + return redisTemplate.opsForValue().get(key); + } + + @Override + public String getString(Object key) { + try { + return redisTemplate.opsForValue().get(key).toString(); + } catch (Exception e) { + return null; + } + } + + @Override + public List multiGet(Collection keys) { + return redisTemplate.opsForValue().multiGet(keys); + + } + + + @Override + public void multiSet(Map map) { + redisTemplate.opsForValue().multiSet(map); + } + + @Override + public void multiDel(Collection keys) { + redisTemplate.delete(keys); + } + + @Override + public void put(Object key, Object value) { + redisTemplate.opsForValue().set(key, value); + } + + @Override + public void put(Object key, Object value, Long exp) { + put(key, value, exp, TimeUnit.SECONDS); + } + + @Override + public void put(Object key, Object value, Long exp, TimeUnit timeUnit) { + redisTemplate.opsForValue().set(key, value, exp, timeUnit); + } + + @Override + public void remove(Object key) { + + redisTemplate.delete(key); + } + + /** + * 删除 + * + * @param key + */ + @Override + public void vagueDel(Object key) { + Set keys = redisTemplate.keys(key + "*"); + redisTemplate.delete(keys); + } + + @Override + public void clear() { + + Set keys = redisTemplate.keys("*"); + redisTemplate.delete(keys); + } + + @Override + public void putHash(Object key, Object hashKey, Object hashValue) { + redisTemplate.opsForHash().put(key, hashKey, hashValue); + } + + @Override + public void putAllHash(Object key, Map map) { + redisTemplate.opsForHash().putAll(key, map); + } + + @Override + public Object getHash(Object key, Object hashKey) { + return redisTemplate.opsForHash().get(key, hashKey); + } + + @Override + public Map getHash(Object key) { + return this.redisTemplate.opsForHash().entries(key); + } + + @Override + public boolean hasKey(Object key) { + return this.redisTemplate.opsForValue().get(key) != null; + } + + /** + * 获取符合条件的key + * + * @param pattern 表达式 + * @return + */ + @Override + public List keys(String pattern) { + List keys = new ArrayList<>(); + this.scan(pattern, item -> { + //符合条件的key + String key = new String(item, StandardCharsets.UTF_8); + keys.add(key); + }); + return keys; + } + + + /** + * scan 实现 + * + * @param pattern 表达式 + * @param consumer 对迭代到的key进行操作 + */ + private void scan(String pattern, Consumer consumer) { + this.redisTemplate.execute((RedisConnection connection) -> { + try (Cursor cursor = + connection.scan(ScanOptions.scanOptions() + .count(Long.MAX_VALUE) + .match(pattern).build())) { + cursor.forEachRemaining(consumer); + return null; + + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + }); + } + + + @Override + public Long cumulative(Object key, Object value) { + HyperLogLogOperations operations = redisTemplate.opsForHyperLogLog(); + // add 方法对应 PFADD 命令 + return operations.add(key, value); + + } + + @Override + public Long counter(Object key) { + HyperLogLogOperations operations = redisTemplate.opsForHyperLogLog(); + + // add 方法对应 PFADD 命令 + return operations.size(key); + } + + @Override + public List multiCounter(Collection keys) { + if (keys == null) { + return new ArrayList(); + } + List result = new ArrayList<>(); + for (Object key : keys) { + result.add(counter(key)); + } + return result; + } + + @Override + public Long mergeCounter(Object... key) { + HyperLogLogOperations operations = redisTemplate.opsForHyperLogLog(); + // 计数器合并累加 + return operations.union(key[0], key); + } + + @Override + public Long incr(String key, long liveTime) { + RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory()); + Long increment = entityIdCounter.getAndIncrement(); + + if ((null == increment || increment.longValue() == 0) && liveTime > 0) {//初始设置过期时间 + entityIdCounter.expire(liveTime, TimeUnit.SECONDS); + } + + return increment; + } + + /** + * 使用Sorted Set记录keyword + * zincrby命令,对于一个Sorted Set,存在的就把分数加x(x可自行设定),不存在就创建一个分数为1的成员 + * + * @param sortedSetName sortedSetName的Sorted Set不用预先创建,不存在会自动创建,存在则向里添加数据 + * @param keyword 关键词 + */ + @Override + public void incrementScore(String sortedSetName, String keyword) { + // x 的含义请见本方法的注释 + double x = 1.0; + this.redisTemplate.opsForZSet().incrementScore(sortedSetName, keyword, x); + } + + /** + * zrevrange命令, 查询Sorted Set中指定范围的值 + * 返回的有序集合中,score大的在前面 + * zrevrange方法无需担心用于指定范围的start和end出现越界报错问题 + * + * @param sortedSetName sortedSetName + * @param start 查询范围开始位置 + * @param end 查询范围结束位置 + * @return + */ + @Override + public Set> reverseRangeWithScores(String sortedSetName, Integer start, Integer end) { + return this.redisTemplate.opsForZSet().reverseRangeWithScores(sortedSetName, start, end); + } +} diff --git a/framework/src/main/java/cn/lili/common/delayqueue/AbstractDelayQueueMachineFactory.java b/framework/src/main/java/cn/lili/common/delayqueue/AbstractDelayQueueMachineFactory.java new file mode 100644 index 00000000..5189cff0 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/delayqueue/AbstractDelayQueueMachineFactory.java @@ -0,0 +1,100 @@ +package cn.lili.common.delayqueue; + +import cn.lili.common.utils.RedisUtil; +import cn.lili.common.utils.ThreadPoolUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.DefaultTypedTuple; +import org.springframework.util.CollectionUtils; + +import javax.annotation.PostConstruct; +import java.util.Calendar; +import java.util.Set; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + * 延时队列工厂 + * + * @author paulG + * @since 2020/11/7 + **/ +@Slf4j +public abstract class AbstractDelayQueueMachineFactory { + + @Autowired + private RedisUtil redisUtil; + + /** + * 插入任务id + * + * @param jobId 任务id(队列内唯一) + * @param time 延时时间(单位 :秒) + * @return 是否插入成功 + */ + public boolean addJobId(String jobId, Integer time) { + Calendar instance = Calendar.getInstance(); + instance.add(Calendar.SECOND, time); + long delaySeconds = instance.getTimeInMillis() / 1000; + return redisUtil.zadd(setDelayQueueName(), delaySeconds, jobId); + + } + + private void startDelayQueueMachine() { + log.info(String.format("延时队列机器{%s}开始运作", setDelayQueueName())); + + // 发生异常捕获并且继续不能让战斗停下来 + while (true) { + try { + // 获取当前时间的时间戳 + long now = System.currentTimeMillis() / 1000; + // 获取当前时间前的任务列表 + Set tuples = redisUtil.zrangeByScoreWithScores(setDelayQueueName(), 0, now); + // 如果不为空则遍历判断其是否满足取消要求 + if (!CollectionUtils.isEmpty(tuples)) { + for (DefaultTypedTuple tuple : tuples) { + + String jobId = (String) tuple.getValue(); + Long num = redisUtil.zremove(setDelayQueueName(), jobId); + // 如果移除成功, 则执行 + if (num > 0) { + ThreadPoolUtil.execute(() -> invoke(jobId)); + } + } + } + + } catch (Exception e) { +// log.warn(String.format("处理延时任务发生异常,异常原因为{%s}", e.getMessage()), e); + } finally { + // 间隔一秒钟搞一次 + try { + TimeUnit.SECONDS.sleep(1L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + } + + } + + /** + * 最终执行的任务方法 + * + * @param jobId 任务id + */ + public abstract void invoke(String jobId); + + + /** + * 要实现延时队列的名字 + */ + public abstract String setDelayQueueName(); + + + @PostConstruct + public void init() { + new Thread(this::startDelayQueueMachine).start(); + } + +} diff --git a/framework/src/main/java/cn/lili/common/delayqueue/DelayQueueTools.java b/framework/src/main/java/cn/lili/common/delayqueue/DelayQueueTools.java new file mode 100644 index 00000000..e6be4cf9 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/delayqueue/DelayQueueTools.java @@ -0,0 +1,22 @@ +package cn.lili.common.delayqueue; + +/** + * 延时任务工具类 + * + * @author paulG + * @since 2021/5/7 + **/ +public class DelayQueueTools { + + /** + * 组装延时任务唯一键 + * + * @param type 延时任务类型 + * @param id id + * @return 唯一键 + */ + public static String wrapperUniqueKey(DelayQueueType type, String id) { + return "{TIME_TRIGGER_" + type.name() + "}_" + id; + } + +} diff --git a/framework/src/main/java/cn/lili/common/delayqueue/DelayQueueType.java b/framework/src/main/java/cn/lili/common/delayqueue/DelayQueueType.java new file mode 100644 index 00000000..bb5c1a87 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/delayqueue/DelayQueueType.java @@ -0,0 +1,30 @@ +package cn.lili.common.delayqueue; + +/** + * 延时任务类型 + * + * @author paulG + * @since 2021/5/7 + **/ +public enum DelayQueueType { + + /** + * 促销活动 + */ + PROMOTION("促销活动"), + /** + * 拼团订单 + */ + PINTUAN_ORDER("拼团订单"); + + private final String description; + + DelayQueueType(String des) { + this.description = des; + } + + public String description() { + return this.description; + } + +} diff --git a/framework/src/main/java/cn/lili/common/delayqueue/PintuanOrderMessage.java b/framework/src/main/java/cn/lili/common/delayqueue/PintuanOrderMessage.java new file mode 100644 index 00000000..75b17a11 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/delayqueue/PintuanOrderMessage.java @@ -0,0 +1,29 @@ +package cn.lili.common.delayqueue; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 拼团订单延时任务信息 + * + * @author paulG + * @since 2021/5/7 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PintuanOrderMessage { + + /** + * 拼团活动id + */ + private String pintuanId; + + /** + * 父拼团订单sn + */ + private String orderSn; + + +} diff --git a/framework/src/main/java/cn/lili/common/delayqueue/PromotionMessage.java b/framework/src/main/java/cn/lili/common/delayqueue/PromotionMessage.java new file mode 100644 index 00000000..c266f022 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/delayqueue/PromotionMessage.java @@ -0,0 +1,52 @@ +package cn.lili.common.delayqueue; + +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * 信息队列传输促销信息实体 + * + * @author paulG + * @date 2020/10/30 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PromotionMessage { + + /** + * 促销id + */ + private String promotionId; + /** + * 促销类型 + */ + private String promotionType; + + /** + * 促销状态 + */ + private String promotionStatus; + + /** + * 开始时间 + */ + private Date startTime; + + /** + * 结束时间 + */ + private Date endTime; + + public UpdateWrapper updateWrapper() { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", promotionId); + updateWrapper.set("promotion_status", PromotionStatusEnum.valueOf(promotionStatus)); + return updateWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/common/elasticsearch/BaseElasticsearchService.java b/framework/src/main/java/cn/lili/common/elasticsearch/BaseElasticsearchService.java new file mode 100644 index 00000000..906f5876 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/elasticsearch/BaseElasticsearchService.java @@ -0,0 +1,186 @@ +package cn.lili.common.elasticsearch; + +import cn.hutool.core.bean.BeanUtil; +import cn.lili.config.elasticsearch.ElasticsearchProperties; +import lombok.extern.slf4j.Slf4j; +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; +import org.elasticsearch.action.delete.DeleteRequest; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.search.SearchRequest; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.client.HttpAsyncResponseConsumerFactory; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.indices.CreateIndexRequest; +import org.elasticsearch.client.indices.CreateIndexResponse; +import org.elasticsearch.client.indices.GetIndexRequest; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +import java.io.IOException; + +/** + * @author paulG + * @since 2020/10/14 + **/ +@Slf4j +public abstract class BaseElasticsearchService { + + protected static final RequestOptions COMMON_OPTIONS; + + static { + RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder(); + + // 默认缓冲限制为100MB,此处修改为30MB。 + builder.setHttpAsyncResponseConsumerFactory(new HttpAsyncResponseConsumerFactory.HeapBufferedResponseConsumerFactory(30 * 1024 * 1024)); + COMMON_OPTIONS = builder.build(); + } + + @Autowired + @Qualifier("elasticsearchClient") + protected RestHighLevelClient client; + + @Autowired + private ElasticsearchProperties elasticsearchProperties; + + /** + * build DeleteIndexRequest + * + * @param index elasticsearch index name + * @author fxbin + */ + private static DeleteIndexRequest buildDeleteIndexRequest(String index) { + return new DeleteIndexRequest(index); + } + + /** + * build IndexRequest + * + * @param index elasticsearch index name + * @param id request object id + * @param object request object + * @return {@link IndexRequest} + * @author fxbin + */ + protected static IndexRequest buildIndexRequest(String index, String id, Object object) { + return new IndexRequest(index).id(id).source(BeanUtil.beanToMap(object), XContentType.JSON); + } + + /** + * create elasticsearch index (asyc) + * + * @param index elasticsearch index + * @author fxbin + */ + protected void createIndexRequest(String index) { + try { + CreateIndexRequest request = new CreateIndexRequest(index); + // Settings for this index + request.settings(Settings.builder().put("index.number_of_shards", elasticsearchProperties.getIndex().getNumberOfShards()).put("index.number_of_replicas", elasticsearchProperties.getIndex().getNumberOfReplicas())); + + CreateIndexResponse createIndexResponse = client.indices().create(request, COMMON_OPTIONS); + + log.info(" whether all of the nodes have acknowledged the request : {}", createIndexResponse.isAcknowledged()); + log.info(" Indicates whether the requisite number of shard copies were started for each shard in the index before timing out :{}", createIndexResponse.isShardsAcknowledged()); + } catch (Exception e) { + throw new ElasticsearchException("创建索引 {" + index + "} 失败:" + e.getMessage()); + } + } + + /** + * Description: 判断某个index是否存在 + * + * @param index index名 + * @return boolean + * @author fanxb + * @date 2019/7/24 14:57 + */ + public boolean indexExist(String index) { + try { + GetIndexRequest request = new GetIndexRequest(index); + request.local(false); + request.humanReadable(true); + request.includeDefaults(false); + return client.indices().exists(request, RequestOptions.DEFAULT); + } catch (Exception e) { + throw new ElasticsearchException("获取索引 {" + index + "} 是否存在失败:" + e.getMessage()); + } + } + + /** + * delete elasticsearch index + * + * @param index elasticsearch index name + * @author fxbin + */ + protected void deleteIndexRequest(String index) { + DeleteIndexRequest deleteIndexRequest = buildDeleteIndexRequest(index); + try { + client.indices().delete(deleteIndexRequest, COMMON_OPTIONS); + } catch (IOException e) { + throw new ElasticsearchException("删除索引 {" + index + "} 失败:" + e.getMessage()); + } + } + + /** + * exec updateRequest + * + * @param index elasticsearch index name + * @param id Document id + * @param object request object + * @author fxbin + */ + protected void updateRequest(String index, String id, Object object) { + try { + UpdateRequest updateRequest = new UpdateRequest(index, id).doc(BeanUtil.beanToMap(object), XContentType.JSON); + client.update(updateRequest, COMMON_OPTIONS); + } catch (IOException e) { + throw new ElasticsearchException("更新索引 {" + index + "} 数据 {" + object + "} 失败: " + e.getMessage()); + } + } + + /** + * exec deleteRequest + * + * @param index elasticsearch index name + * @param id Document id + * @author fxbin + */ + protected void deleteRequest(String index, String id) { + try { + DeleteRequest deleteRequest = new DeleteRequest(index, id); + client.delete(deleteRequest, COMMON_OPTIONS); + } catch (IOException e) { + throw new ElasticsearchException("删除索引 {" + index + "} 数据id {" + id + "} 失败: " + e.getMessage()); + } + } + + /** + * search all + * + * @param index elasticsearch index name + * @return {@link SearchResponse} + * @author fxbin + */ + protected SearchResponse search(String index) { + SearchRequest searchRequest = new SearchRequest(index); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.query(QueryBuilders.matchAllQuery()); + searchRequest.source(searchSourceBuilder); + SearchResponse searchResponse = null; + try { + searchResponse = client.search(searchRequest, COMMON_OPTIONS); + } catch (IOException e) { + e.printStackTrace(); + } + return searchResponse; + } + + +} diff --git a/framework/src/main/java/cn/lili/common/elasticsearch/EsSuffix.java b/framework/src/main/java/cn/lili/common/elasticsearch/EsSuffix.java new file mode 100644 index 00000000..e7fe6f8e --- /dev/null +++ b/framework/src/main/java/cn/lili/common/elasticsearch/EsSuffix.java @@ -0,0 +1,16 @@ +package cn.lili.common.elasticsearch; + +/** + * elasticsearch 索引后缀 + * + * @author paulG + * @since 2020/10/13 + **/ +public class EsSuffix { + + /** + * 商品索引后缀 + */ + public static final String GOODS_INDEX_NAME = "goods"; + +} diff --git a/framework/src/main/java/cn/lili/common/enums/MessageCode.java b/framework/src/main/java/cn/lili/common/enums/MessageCode.java new file mode 100644 index 00000000..92b0e7d0 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/enums/MessageCode.java @@ -0,0 +1,166 @@ +package cn.lili.common.enums; + +/** + * @author Chopper + * @version v4.1 + * @Description: 错误码 5位 + * 第一位 1:商品;2:会员;3:订单,4:店铺,5:页面,6:其他 + * @since 2020/11/17 4:44 下午 + */ +public enum MessageCode { + + E401("认证过期"), + + E10001("商品ID不存在"), + E10002("商品名称不正确,名称应为2-50字符"), + E10003("此规格已绑定分类不允许删除"), + E10004("该分类名称已存在"), + E10005("父分类不存在"), + E10006("最多为三级分类,添加失败"), + E10007("此类别下存在子类别不能删除"), + E10008("此类别下存在商品不能删除"), + E10009("通过categoryId获取分类失败"), + E10010("不能重复添加分销商品"), + E10011("无法将此商品放入回收站"), + E10012("无法彻底删除从此商品"), + E10013("无法下架此商品"), + E10014("无法还原此商品"), + + + E20001("用户名密码不能为空"), + E20002("该用户名已被注册"), + E20003("该手机号已被注册"), + E20004("用户不存在"), + E20005("修改资料成功"), + E20006("修改资料失败"), + E20007("手机号不存在"), + E20008("重置密码成功"), + E20009("修改手机号成功"), + E20010("旧密码不正确"), + E20011("修改密码成功"), + E20012("用户未登录"), + E20013("发布评价成功"), + E20014("领取优惠券成功"), + E20015("分销商不存在"), + E20016("分销商已申请,无需重复提交"), + E20017("密码错误"), + E20018("分销商清退失败"), + E20019("审核分销商失败"), + E20020("用户名或密码错误"), + E20021("订单状态不允许申请售后,请联系平台或商家"), + E20022("无权操作此数据!"), + E20023("此用户已禁用!"), + E20024("未对当前用户做手机号码校验!"), + E20025("可提现金额不足!"), + E20026("零钱提现失败!"), + E20027("请填写审核备注!"), + E20028("请勿重复签到"), + E20029("分销商不存在或已被清退"), + E20030("分销商提现记录不存在"), + E20031("分销商提现状态不正确,无法提现"), + E20032("余额账户不存在,无法操作"), + E20033("验证码错误,请重新输入"), + E20034("最多可以设置10个角色"), + E20035("订单已支付,请勿反复支付"), + E20036("该订单已部分支付,请前往订单中心进行支付"), + E20037("余额充值已支付成功,请勿重复支付"), + E20038("余额不足以支付订单,请充值"), + E20039("支付业务异常,请稍后重试"), + E20040("当前订单不需要付款,请返回订单列表重新操作"), + E20041("非当前用户的手机号"), + E20101("联合第三方登录,未绑定会员"), + E20102("联合第三方登录,授权信息错误"), + E20103("会员发票信息重复"), + E20104("会员发票信息不存在"), + E20105("权限不足"), + + E30001("购物车添加成功"), + E30002("购物车数量更新成功"), + E30003("购物车选中更新成功"), + E30004("清空购物车成功"), + E30005("删除购物车中的一个或多个产品成功"), + E30006("读取结算页的购物车异常"), + E30007("创建订单异常,请稍后重试"), + E30008("订单状态错误,无法进行确认收货"), + E30009("订单确定收货成功"), + E30010("订单不存在"), + E30011("已支付的订单不能修改金额"), + E30013("物流错误"), + E30014("当前订单不能发货"), + E30015("非当前会员的订单"), + E30016("无法重复提交评价"), + E30017("付款金额和应付金额不一致"), + E30018("售后已审核,无法重复操作"), + E30019("物流公司错误,请重新选择"), + E30020("请选择要投诉的商品"), + E30021("当前订单无法核销"), + E30022("当前售后单无法取消"), + E30023("当前订单未支付,不能申请售后"), + E30024("订单已支付,不能再次进行支付"), + E30025("收银台信息获取错误"), + E30026("积分不足,不能兑换"), + E30027("优惠券已使用/已过期,不能使用"), + E30028("投诉异常,请稍后重试"), + + E40001("非当前店铺数据,无法操作"), + E40002("此店铺不存在"), + E40003("店铺名称已存在!"), + E40004("参数有误!"), + E40005("已有店铺,无需重复申请"), + E40006("只有已出账结算单可以核对"), + E40007("只有已核对结算单可以审核"), + E40008("只有已审核结算单可以支付"), + E40009("只有商家可以操作结算单核对"), + E40010("只有管理可以操作结算单核对"), + E40011("存在不属于此店铺的商品"), + + E50001("该分类名称已存在"), + E50002("父分类不存在"), + E50003("最多为三级分类,添加失败"), + E50004("最多为三级分类,修改失败"), + E50005("文章分类名称重复,修改失败"), + E50006("该文章分类下存在子分类,不能删除"), + E50007("专题自动发布,无需操作发布"), + E50008("当前页面为开启状态,无法删除"), + E50009("当前页面为唯一页面,无法删除"), + E50010("页面已发布,无需重复提交"), + E50011("需传入查询ID"), + E50012("当前消息模板不存在"), + E50013("短信签名已存在"), + E50014("该文章分类下存在文章,不能删除"), + + E60000("请先登录再访问此接口"), + E60001("操作失败"), + E60002("非法请求"), + E60003("请求正在处理,请稍后再试"), + E60004("验证码为空或已过期,请重新获取"), + E60005("验证失败"), + E60007("OSS未配置"), + E60008("支付暂不支持"), + E60009("错误的客户端"), + + E60010("角色已绑定部门,请逐个删除"), + E60011("角色已绑定管理员,请逐个删除"), + E60012("菜单已绑定角色,请先删除或编辑角色"), + E60013("部门已经绑定管理员,请先删除或编辑管理员"), + + S21001("分销商清退成功"), + S21002("审核分销商成功"), + + S44000("操作成功"), + S50001("删除文章分类成功"); + + private final String value; + + MessageCode(String value) { + this.value = value; + } + + public Integer getCode() { + return Integer.parseInt(this.name().replace("E", "").replace("S", "")); + } + + public String getValue() { + return value; + } +} diff --git a/framework/src/main/java/cn/lili/common/enums/ResultCode.java b/framework/src/main/java/cn/lili/common/enums/ResultCode.java new file mode 100644 index 00000000..5ee40677 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/enums/ResultCode.java @@ -0,0 +1,284 @@ +package cn.lili.common.enums; + +/** + * 返回状态码 + * 第一位 1:商品;2:用户;3:交易,4:促销,5:店铺,6:页面,7:设置,8:其他 + * @author Chopper + * @date 2020/4/8 1:36 下午 + */ +public enum ResultCode { + + /** + * 成功状态码 + */ + SUCCESS(200, "成功"), + + /** + * 失败返回码 + */ + ERROR(400,"失败"), + /** + * 失败返回码 + */ + DEMO_SITE_EXCEPTION(400,"演示站点禁止使用"), + + /** + * 分类 + */ + CATEGORY_NOT_EXIST(10001,"商品分类不存在"), + CATEGORY_NAME_IS_EXIST(10002,"该分类名称已存在"), + CATEGORY_PARENT_NOT_EXIST(10003,"该分类名称已存在"), + CATEGORY_BEYOND_THREE(10004,"最多为三级分类,添加失败"), + CATEGORY_HAS_CHILDREN(10005,"此类别下存在子类别不能删除"), + CATEGORY_HAS_GOODS(10006,"此类别下存在商品不能删除"), + CATEGORY_SAVE_ERROR(10007,"此类别下存在商品不能删除"), + CATEGORY_PARAMETER_SAVE_ERROR(10008,"分类绑定参数组添加失败"), + CATEGORY_PARAMETER_UPDATE_ERROR(10009,"分类绑定参数组添加失败"), + + /** + * 商品 + */ + GOODS_NOT_EXIST(11001,"商品已下架"), + GOODS_NAME_ERROR(11002,"商品名称不正确,名称应为2-50字符"), + GOODS_UNDER_ERROR(11003,"商品下架失败"), + GOODS_UPPER_ERROR(11004,"商品上架失败"), + GOODS_AUTH_ERROR(11005,"商品审核失败"), + + /** + * 参数 + */ + PARAMETER_SAVE_ERROR(12001,"参数添加失败"), + PARAMETER_UPDATE_ERROR(12002,"参数编辑失败"), + + /** + * 规格 + */ + SPEC_SAVE_ERROR(13001,"规格修改失败"), + SPEC_UPDATE_ERROR(13002,"规格修改失败"), + SPEC_DELETE_ERROR(13003,"此规格已绑定分类不允许删除"), + + /** + * 品牌 + */ + BRAND_SAVE_ERROR(14001,"品牌添加失败"), + BRAND_UPDATE_ERROR(14002,"品牌修改失败"), + BRAND_DISABLE_ERROR(14003,"品牌禁用失败"), + BRAND_DELETE_ERROR(14004,"品牌删除失败"), + + /** + * 用户 + */ + USER_EDIT_SUCCESS(20001,"用户修改成功"), + USER_NOT_EXIST(20002, "用户不存在"), + USER_NOT_LOGIN(20003, "用户未登录"), + USER_AUTH_EXPIRED(20004, "认证过期"), + USER_AUTHORITY_ERROR(20005,"权限不足"), + USER_CONNECT_LOGIN_ERROR(20006,"未找到登录信息"), + USER_NAME_EXIST(20007, "该用户名已被注册"), + USER_PHONE_EXIST(20008, "该手机号已被注册"), + USER_PHONE_NOT_EXIST(20009, "手机号不存在"), + USER_PASSWORD_ERROR(20010, "密码不正确"), + USER_NOT_PHONE(20011,"非当前用户的手机号"), + USER_CONNECT_ERROR(20012,"联合第三方登录,授权信息错误"), + USER_RECEIPT_REPEAT_ERROR(20013,"会员发票信息重复"), + USER_RECEIPT_NOT_EXIST(20014,"会员发票信息不存在"), + USER_EDIT_ERROR(20015,"用户修改失败"), + USER_OLD_PASSWORD_ERROR(20016, "旧密码不正确"), + USER_COLLECTION_EXIST(2001,"无法重复收藏"), + + /** + * 权限 + */ + PERMISSION_DEPARTMENT_ROLE_ERROR(21001,"角色已绑定部门,请逐个删除"), + PERMISSION_USER_ROLE_ERROR(21002,"角色已绑定管理员,请逐个删除"), + PERMISSION_MENU_ROLE_ERROR(21003,"菜单已绑定角色,请先删除或编辑角色"), + PERMISSION_DEPARTMENT_DELETE_ERROR(21004,"部门已经绑定管理员,请先删除或编辑管理员"), + PERMISSION_BEYOND_TEN(21005,"最多可以设置10个角色"), + + /** + * 分销 + */ + DISTRIBUTION_CLOSE(22000, "分销功能关闭"), + DISTRIBUTION_NOT_EXIST(22001, "分销员不存在"), + DISTRIBUTION_IS_APPLY(22002, "分销员已申请,无需重复提交"), + DISTRIBUTION_AUDIT_ERROR(22003, "审核分销员失败"), + DISTRIBUTION_RETREAT_ERROR(22004, "分销员清退失败"), + DISTRIBUTION_CASH_NOT_EXIST(22005, "分销员提现记录不存在"), + DISTRIBUTION_GOODS_DOUBLE(22006, "不能重复添加分销商品"), + + /** + * 购物车 + */ + CART_ERROR(30001, "读取结算页的购物车异常"), + + SHIPPING_NOT_APPLY(30005, "当前选择地址暂不支持配送!"), + /** + * 订单 + */ + ORDER_ERROR(31001, "创建订单异常,请稍后重试"), + ORDER_NOT_EXIST(31002, "订单不存在"), + ORDER_DELIVERED_ERROR(31003, "订单状态错误,无法进行确认收货"), + ORDER_UPDATE_PRICE_ERROR(31004, "已支付的订单不能修改金额"), + ORDER_LOGISTICS_ERROR(31005, "物流错误"), + ORDER_DELIVER_ERROR(31006, "物流错误"), + ORDER_NOT_USER(31007, "非当前会员的订单"), + ORDER_TAKE_ERROR(31008,"当前订单无法核销"), + + /** + * 支付 + */ + PAY_SUCCESS(32001,"支付成功"), + PAY_INCONSISTENT_ERROR(32002,"付款金额和应付金额不一致"), + PAY_DOUBLE_ERROR(32003,"订单已支付,不能再次进行支付"), + PAY_CASHIER_ERROR(32004,"收银台信息获取错误"), + PAY_ERROR(32005,"支付业务异常,请稍后重试"), + PAY_BAN(32006,"当前订单不需要付款,请返回订单列表重新操作"), + PAY_PARTIAL_ERROR(32007,"该订单已部分支付,请前往订单中心进行支付"), + PAY_NOT_SUPPORT(32008,"支付暂不支持"), + PAY_CLIENT_TYPE_ERROR(32009,"错误的客户端"), + PAY_POINT_ENOUGH(32010,"积分不足,不能兑换"), + PAY_NOT_EXIST_ORDER(32011,"支付订单不存在"), + + /** + * 售后 + */ + AFTER_SALES_NOT_PAY_ERROR(33001,"当前订单未支付,不能申请售后"), + AFTER_SALES_CANCEL_ERROR(33002,"当前售后单无法取消"), + AFTER_SALES_BAN(33003,"订单状态不允许申请售后,请联系平台或商家"), + AFTER_SALES_DOUBLE_ERROR(33004,"售后已审核,无法重复操作"), + AFTER_SALES_LOGISTICS_ERROR(33005,"物流公司错误,请重新选择"), + + /** + * 投诉 + */ + COMPLAINT_ORDER_ITEM_EMPTY_ERROR(33100,"订单不存在"), + COMPLAINT_SKU_EMPTY_ERROR(33101,"商品已下架,如需投诉请联系平台客服"), + COMPLAINT_ERROR(33102,"投诉异常,请稍后重试"), + + /** + * 余额 + */ + WALLET_INSUFFICIENT(34001,"余额不足以支付订单,请充值!"), + WALLET_WITHDRAWAL_INSUFFICIENT(34002,"可提现金额不足!"), + WALLET_ERROR_INSUFFICIENT(34003,"零钱提现失败!"), + WALLET_REMARK_ERROR(34004,"请填写审核备注!"), + + /** + * 评价 + */ + EVALUATION_DOUBLE_ERROR(35001,"无法重复提交评价"), + + /** + * 签到 + */ + MEMBER_SIGN_REPEAT(40001,"请勿重复签到"), + + /** + * 优惠券 + */ + COUPON_EDIT_STATUS_SUCCESS(41001,"修改状态成功!"), + COUPON_CANCELLATION_SUCCESS(41002,"会员优惠券作废成功"), + COUPON_EXPIRED(41003,"优惠券已使用/已过期,不能使用"), + COUPON_EDIT_STATUS_ERROR(41004,"优惠券修改状态失败!"), + + /** + * 拼团 + */ + PINTUAN_MANUAL_OPEN_SUCCESS(42001,"手动开启拼团活动成功"), + PINTUAN_MANUAL_CLOSE_SUCCESS(42002,"手动关闭拼团活动成功"), + PINTUAN_ADD_SUCCESS(42003,"添加拼团活动成功"), + PINTUAN_EDIT_SUCCESS(42004,"修改拼团活动成功"), + PINTUAN_DELETE_SUCCESS(42005,"删除拼团活动成功"), + PINTUAN_MANUAL_OPEN_ERROR(42006,"手动开启拼团活动失败"), + PINTUAN_MANUAL_CLOSE_ERROR(42007,"手动关闭拼团活动失败"), + PINTUAN_ADD_ERROR(42008,"添加拼团活动失败"), + PINTUAN_EDIT_ERROR(42009,"修改拼团活动失败"), + PINTUAN_DELETE_ERROR(42010,"删除拼团活动失败"), + + /** + * 满额活动 + */ + FULL_DISCOUNT_EDIT_SUCCESS(43001,"修改满优惠活动成功"), + FULL_DISCOUNT_EDIT_DELETE(43002,"删除满优惠活动成功"), + + /** + * 店铺 + */ + STORE_NOT_EXIST(50001,"此店铺不存在"), + STORE_NAME_EXIST_ERROR(50002,"店铺名称已存在!"), + STORE_APPLY_DOUBLE_ERROR(50003,"已有店铺,无需重复申请!"), + + /** + * 结算单 + */ + BILL_CHECK_ERROR(51001,"只有已出账结算单可以核对"), + BILL_COMPLETE_ERROR(51002,"只有已审核结算单可以支付"), + + /** + * 文章 + */ + ARTICLE_CATEGORY_NAME_EXIST(60001,"文章分类名称已存在"), + ARTICLE_CATEGORY_PARENT_NOT_EXIST(60002,"文章分类父分类不存在"), + ARTICLE_CATEGORY_BEYOND_TWO(60003,"最多为二级分类,操作失败"), + ARTICLE_CATEGORY_DELETE_ERROR(60004,"该文章分类下存在子分类,不能删除"), + ARTICLE_CATEGORY_HAS_ARTICLE(60005,"该文章分类下存在文章,不能删除"), + ARTICLE_CATEGORY_NO_DELETION(60007,"默认文章分类不能进行删除"), + ARTICLE_NO_DELETION(60008,"默认文章不能进行删除"), + + + + /** + * 页面 + */ + PAGE_NOT_EXIST(61001,"页面不存在"), + PAGE_OPEN_DELETE_ERROR(61002,"当前页面为开启状态,无法删除"), + PAGE_DELETE_ERROR(61003,"当前页面为唯一页面,无法删除"), + PAGE_RELEASE_ERROR(61004,"页面已发布,无需重复提交"), + + /** + * 设置 + */ + SETTING_NOT_TO_SET(70001,"该参数不需要设置"), + + /** + * 短信 + */ + SMS_SIGN_EXIST_ERROR(80001,"短信签名已存在"), + + /** + * 站内信 + */ + NOTICE_NOT_EXIST(80101,"当前消息模板不存在"), + + /** + * OSS + */ + OSS_NOT_EXIST(80201,"OSS未配置"), + + /** + * 验证码 + */ + VERIFICATION_SEND_SUCCESS(80301,"短信验证码,发送成功"), + VERIFICATION_ERROR(80302,"验证失败"), + VERIFICATION_SMS_ERROR(80303,"短信验证码错误,请重新校验"), + VERIFICATION_SMS_EXPIRED_ERROR(80304,"验证码已失效,请重新校验") + + ; + private final Integer code; + private final String message; + + + ResultCode(Integer code, String message) { + this.code = code; + this.message = message; + } + + public Integer code() { + return this.code; + } + + public String message() { + return this.message; + } + +} diff --git a/framework/src/main/java/cn/lili/common/enums/StoreLogType.java b/framework/src/main/java/cn/lili/common/enums/StoreLogType.java new file mode 100644 index 00000000..40c56ca6 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/enums/StoreLogType.java @@ -0,0 +1,129 @@ +package cn.lili.common.enums; + +/** + * 日志枚举 + * + * @author Chopper + */ +public enum StoreLogType { + + /** + * 默认0操作 + */ + OPERATION, + + /** + * 1登录 + */ + LOGIN, + + /** + * 商品 + */ + + //商品上架 + + //商品下架 + + //发布商品 + + //修改商品 + + //删除商品 + + //添加商品模板 + + //编辑商品库存 + + //增加店铺商品分类 + + //编辑店铺商品分类 + + /** + * 订单 + */ + + //发货 + + //修改收件信息 + + /** + * 售后 + */ + + //审核退货单 + + //确定收货 + + //拒绝收货 + + //退款 + + //拒绝退款 + + + + /** + * 营销 + */ + + //添加分销商品 + + //下架分销商品 + + //编辑分销商品 + + //创建满减活动 + + //编辑满减活动 + + //下架满减活动 + + //上架满减活动 + + //删除满减活动 + + //添加优惠券 + + //编辑优惠券 + + //关闭优惠券 + + //参与秒杀活动 + + //编辑秒杀活动 + + //下架秒杀活动 + + //上架秒杀活动 + + //删除秒杀活动 + + + /** + * 设置 + */ + + //选择物流公司 + + //取消物流公司 + + //添加运费模板 + + //编辑运费模板 + + //删除运费模板 + + //设置店铺信息 + + //修改店铺退货地址 + + //新增自提点 + + //编辑自提点 + + //删除自提点 + + //发布店铺首页 + +} diff --git a/framework/src/main/java/cn/lili/common/enums/SwitchEnum.java b/framework/src/main/java/cn/lili/common/enums/SwitchEnum.java new file mode 100644 index 00000000..20b5eee6 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/enums/SwitchEnum.java @@ -0,0 +1,25 @@ +package cn.lili.common.enums; + +/** + * 开关枚举 + * @author Chopper + */ +public enum SwitchEnum { + + /** + * 开关 + */ + OPEN("开启"), CLOSE("关闭"); + + private String description; + + SwitchEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/common/exception/GlobalControllerExceptionHandler.java b/framework/src/main/java/cn/lili/common/exception/GlobalControllerExceptionHandler.java new file mode 100644 index 00000000..3da03776 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/exception/GlobalControllerExceptionHandler.java @@ -0,0 +1,109 @@ +package cn.lili.common.exception; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.BindException; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 全局异常异常处理 + * + * @author Chopper + */ +@RestControllerAdvice +@Slf4j +public class GlobalControllerExceptionHandler { + + /** + * 如果超过长度,则前后段交互体验不佳,使用默认错误消息 + */ + static Integer MAX_LENGTH = 200; + + /** + * 自定义异常 + * + * @param e + * @return + */ + @ExceptionHandler(ServiceException.class) + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + public ResultMessage handleServiceException(HttpServletRequest request, final Exception e, HttpServletResponse response) { + + log.error("全局异常[ServiceException]:", e); + + //如果是自定义异常,则获取异常,返回自定义错误消息 + if (e instanceof ServiceException) { + ResultCode resultCode=((ServiceException) e).getResultCode(); + if (resultCode != null) { + return ResultUtil.error(resultCode.code(), resultCode.message()); + } + } + + //默认错误消息 + String errorMsg = "服务器异常,请稍后重试"; + if (e != null && e.getMessage() != null && e.getMessage().length() < MAX_LENGTH) { + errorMsg = e.getMessage(); + } + return ResultUtil.error(400, errorMsg); + } + + @ExceptionHandler(RuntimeException.class) + @ResponseStatus(value = HttpStatus.BAD_REQUEST) + public ResultMessage runtimeExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) { + + log.error("全局异常[RuntimeException]:", e); + + return ResultUtil.error(400, "服务器异常,请稍后重试"); + } + +// /** +// * 通用的接口映射异常处理方 +// */ +// @Override +// protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { +// if (ex instanceof MethodArgumentNotValidException) { +// MethodArgumentNotValidException exception = (MethodArgumentNotValidException) ex; +// return new ResponseEntity<>(new ResultUtil<>().setErrorMsg(exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()), status); +// } +// if (ex instanceof MethodArgumentTypeMismatchException) { +// MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException) ex; +// logger.error("参数转换失败,方法:" + exception.getParameter().getMethod().getName() + ",参数:" + exception.getName() +// + ",信息:" + exception.getLocalizedMessage()); +// return new ResponseEntity<>(new ResultUtil<>().setErrorMsg("参数转换失败"), status); +// } +// ex.printStackTrace(); +// return new ResponseEntity<>(new ResultUtil<>().setErrorMsg("未知异常,请联系管理员"), status); +// } + + /** + * bean校验未通过异常 + * + * @see javax.validation.Valid + * @see org.springframework.validation.Validator + * @see org.springframework.validation.DataBinder + */ + @ExceptionHandler(BindException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseBody + public ResultMessage validExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) { + + BindException exception = (BindException) e; + List fieldErrors = exception.getBindingResult().getFieldErrors(); + for (FieldError error : fieldErrors) { + return ResultUtil.error(400,error.getDefaultMessage()); + } + return ResultUtil.error(ResultCode.ERROR); + } + +} diff --git a/framework/src/main/java/cn/lili/common/exception/ServiceException.java b/framework/src/main/java/cn/lili/common/exception/ServiceException.java new file mode 100644 index 00000000..70e35d58 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/exception/ServiceException.java @@ -0,0 +1,30 @@ +package cn.lili.common.exception; + +import cn.lili.common.enums.ResultCode; +import lombok.Data; + +/** + * @author Chopper + */ +@Data +public class ServiceException extends RuntimeException { + + private String msg; + + private ResultCode resultCode; + + public ServiceException(String msg) { + super(msg); + this.msg = msg; + } + + public ServiceException() { + super("网络错误,请稍后重试!"); + this.msg = "网络错误,请稍后重试!"; + } + + public ServiceException(ResultCode resultCode) { + this.resultCode = resultCode; + } + +} diff --git a/framework/src/main/java/cn/lili/common/heath/DataSourceHealthConfig.java b/framework/src/main/java/cn/lili/common/heath/DataSourceHealthConfig.java new file mode 100644 index 00000000..2437cc99 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/heath/DataSourceHealthConfig.java @@ -0,0 +1,43 @@ +package cn.lili.common.heath; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration; +import org.springframework.boot.actuate.health.AbstractHealthIndicator; +import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator; +import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + +import javax.sql.DataSource; +import java.util.Map; + +/** + * 数据库检验工具 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/12/24 19:31 + */ + +@Configuration +public class DataSourceHealthConfig extends DataSourceHealthContributorAutoConfiguration { + + @Value("${spring.datasource.dbcp2.validation-query:select 1}") + private String defaultQuery; + + + public DataSourceHealthConfig(Map dataSources, ObjectProvider metadataProviders) { + super(dataSources, metadataProviders); + } + + @Override + protected AbstractHealthIndicator createIndicator(DataSource source) { + DataSourceHealthIndicator indicator = (DataSourceHealthIndicator) super.createIndicator(source); + if (!StringUtils.hasText(indicator.getQuery())) { + indicator.setQuery(defaultQuery); + } + return indicator; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/common/rocketmq/RocketmqSendCallback.java b/framework/src/main/java/cn/lili/common/rocketmq/RocketmqSendCallback.java new file mode 100644 index 00000000..088420b3 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/rocketmq/RocketmqSendCallback.java @@ -0,0 +1,23 @@ +package cn.lili.common.rocketmq; + +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.SendCallback; +import org.apache.rocketmq.client.producer.SendResult; + +/** + * @author paulG + * @since 2020/11/4 + **/ +@Slf4j +public class RocketmqSendCallback implements SendCallback { + + @Override + public void onSuccess(SendResult sendResult) { + log.info("async onSuccess SendResult={}", sendResult); + } + + @Override + public void onException(Throwable throwable) { + log.error("async onException Throwable", throwable); + } +} diff --git a/framework/src/main/java/cn/lili/common/rocketmq/RocketmqSendCallbackBuilder.java b/framework/src/main/java/cn/lili/common/rocketmq/RocketmqSendCallbackBuilder.java new file mode 100644 index 00000000..d29c25eb --- /dev/null +++ b/framework/src/main/java/cn/lili/common/rocketmq/RocketmqSendCallbackBuilder.java @@ -0,0 +1,14 @@ +package cn.lili.common.rocketmq; + +/** + * @author paulG + * @since 2020/11/4 + **/ +public class RocketmqSendCallbackBuilder { + + + public static RocketmqSendCallback commonCallback() { + return new RocketmqSendCallback(); + } + +} diff --git a/framework/src/main/java/cn/lili/common/rocketmq/tags/AfterSaleTagsEnum.java b/framework/src/main/java/cn/lili/common/rocketmq/tags/AfterSaleTagsEnum.java new file mode 100644 index 00000000..98322812 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/rocketmq/tags/AfterSaleTagsEnum.java @@ -0,0 +1,23 @@ +package cn.lili.common.rocketmq.tags; + +/** + * @author paulG + * @since 2020/12/9 + **/ +public enum AfterSaleTagsEnum { + + REFUND("售后退款"), + AFTER_SALE_STATUS_CHANGE("售后单状态改变"); + + private final String description; + + AfterSaleTagsEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/common/rocketmq/tags/GoodsTagsEnum.java b/framework/src/main/java/cn/lili/common/rocketmq/tags/GoodsTagsEnum.java new file mode 100644 index 00000000..0a7f3a39 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/rocketmq/tags/GoodsTagsEnum.java @@ -0,0 +1,29 @@ +package cn.lili.common.rocketmq.tags; + +/** + * @author paulG + * @since 2020/12/9 + **/ +public enum GoodsTagsEnum { + + GENERATOR_GOODS_INDEX("生成商品索引"), + GOODS_DELETE("删除商品"), + GOODS_AUDIT("审核商品"), + GOODS_COLLECTION("收藏商品"), + BUY_GOODS_COMPLETE("购买商品完成"), + SKU_DELETE("删除商品SKU"), + VIEW_GOODS("查看商品"), + GOODS_COMMENT_COMPLETE("商品评价"); + + private final String description; + + GoodsTagsEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/common/rocketmq/tags/MemberTagsEnum.java b/framework/src/main/java/cn/lili/common/rocketmq/tags/MemberTagsEnum.java new file mode 100644 index 00000000..1b26b23b --- /dev/null +++ b/framework/src/main/java/cn/lili/common/rocketmq/tags/MemberTagsEnum.java @@ -0,0 +1,25 @@ +package cn.lili.common.rocketmq.tags; + +/** + * @author paulG + * @since 2020/12/9 + **/ +public enum MemberTagsEnum { + + MEMBER_REGISTER("会员注册"), + MEMBER_SING("会员签到"), + MEMBER_WITHDRAWAL("会员提现"), + MEMBER_POINT_CHANGE("会员积分变动"); + + private final String description; + + MemberTagsEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/common/rocketmq/tags/MqOrderTagsEnum.java b/framework/src/main/java/cn/lili/common/rocketmq/tags/MqOrderTagsEnum.java new file mode 100644 index 00000000..baf6bdf6 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/rocketmq/tags/MqOrderTagsEnum.java @@ -0,0 +1,27 @@ +package cn.lili.common.rocketmq.tags; + +/** + * @author paulG + * @since 2020/12/9 + **/ +public enum MqOrderTagsEnum { + + + ORDER_CREATE("订单创建"), + STATUS_CHANGE("订单状态改变"); + + + + + private final String description; + + MqOrderTagsEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/common/rocketmq/tags/OtherTagsEnum.java b/framework/src/main/java/cn/lili/common/rocketmq/tags/OtherTagsEnum.java new file mode 100644 index 00000000..91e9dcf3 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/rocketmq/tags/OtherTagsEnum.java @@ -0,0 +1,23 @@ +package cn.lili.common.rocketmq.tags; + +/** + * @author paulG + * @since 2020/12/9 + **/ +public enum OtherTagsEnum { + + MESSAGE("站内消息提醒"), + SMS("短信消息提醒"); + + private final String description; + + OtherTagsEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/common/security/AuthUser.java b/framework/src/main/java/cn/lili/common/security/AuthUser.java new file mode 100644 index 00000000..71e86fbd --- /dev/null +++ b/framework/src/main/java/cn/lili/common/security/AuthUser.java @@ -0,0 +1,73 @@ +package cn.lili.common.security; + +import cn.lili.common.security.enums.UserEnums; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Chopper + */ +@Data +@AllArgsConstructor +public class AuthUser implements Serializable { + + /** + * 用户名 + */ + private String username; + + /** + * 昵称 + */ + private String nickName; + + /** + * id + */ + private String id; + + /** + * 长期有效(用于手机app登录场景或者信任场景等) + */ + private Boolean longTerm = false; + + /** + * @see UserEnums + * 角色 + */ + private UserEnums role; + + /** + * 如果角色是商家,则存在此店铺id字段 + * storeId + */ + private String storeId; + + /** + * 如果角色是商家,则存在此店铺名称字段 + * storeName + */ + private String storeName; + + /** + * 是否是超级管理员 + */ + private Boolean isSuper = false; + + public AuthUser(String username, String id, String nickName, UserEnums role) { + this.username = username; + this.id = id; + this.role = role; + this.nickName = nickName; + } + + public AuthUser(String username, String id, UserEnums manager, String nickName, Boolean isSuper) { + this.username = username; + this.id = id; + this.role = manager; + this.isSuper = isSuper; + this.nickName = nickName; + } +} diff --git a/framework/src/main/java/cn/lili/common/security/CustomAccessDeniedHandler.java b/framework/src/main/java/cn/lili/common/security/CustomAccessDeniedHandler.java new file mode 100644 index 00000000..254425a3 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/security/CustomAccessDeniedHandler.java @@ -0,0 +1,27 @@ +package cn.lili.common.security; + + +import cn.lili.common.utils.ResponseUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 自定义 拒绝访问响应 + * + * @author Chopper + */ +@Component +@Slf4j +public class CustomAccessDeniedHandler implements AccessDeniedHandler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) { + ResponseUtil.output(response, ResponseUtil.resultMap(false, 401, "抱歉,您没有访问权限")); + } + +} diff --git a/framework/src/main/java/cn/lili/common/security/SecurityBean.java b/framework/src/main/java/cn/lili/common/security/SecurityBean.java new file mode 100644 index 00000000..5d186160 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/security/SecurityBean.java @@ -0,0 +1,42 @@ +package cn.lili.common.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +/** + * SecurityBean + * + * @author Chopper + * @version v1.0 + * 2020-11-14 15:03 + */ +@Configuration +public class SecurityBean { + + @Bean + public BCryptPasswordEncoder passwordEncoder() { + BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); + return bCryptPasswordEncoder; + } + + /** + * 定义跨域配置 + * + * @return bean + */ + @Bean + CorsConfigurationSource corsConfigurationSource() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin(CorsConfiguration.ALL); + config.addAllowedHeader(CorsConfiguration.ALL); + config.addAllowedMethod(CorsConfiguration.ALL); + source.registerCorsConfiguration("/**", config); + return source; + } +} diff --git a/framework/src/main/java/cn/lili/common/security/context/AuthenticationHandler.java b/framework/src/main/java/cn/lili/common/security/context/AuthenticationHandler.java new file mode 100644 index 00000000..0a7b8c55 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/security/context/AuthenticationHandler.java @@ -0,0 +1,36 @@ +package cn.lili.common.security.context; + +import cn.lili.common.security.AuthUser; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +/** + * 获取用户信息 处理 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/14 20:35 + */ +@Component +public class AuthenticationHandler { + + /** + * 获取当前用户信息 + * + * @return + */ + public AuthUser getAuthUser() { + //获取spring security 权限信息,如果token有权限,在这里就会得到内容 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + return null; + } + Object object = authentication.getDetails(); + if (object instanceof AuthUser) { + return (AuthUser) authentication.getDetails(); + } + return null; + } +} diff --git a/framework/src/main/java/cn/lili/common/security/context/UserContext.java b/framework/src/main/java/cn/lili/common/security/context/UserContext.java new file mode 100644 index 00000000..8c21c485 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/security/context/UserContext.java @@ -0,0 +1,60 @@ +package cn.lili.common.security.context; + +import cn.lili.common.cache.Cache; +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.enums.SecurityEnum; +import cn.lili.common.token.SecretKeyUtil; +import com.google.gson.Gson; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; + +/** + * 用户上下文 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/14 20:27 + */ +public class UserContext { + + private static AuthenticationHandler authenticationHandler; + + public static void setHolder(AuthenticationHandler authenticationHandler) { + UserContext.authenticationHandler = authenticationHandler; + } + + + public static AuthUser getCurrentUser() { + return authenticationHandler.getAuthUser(); + } + + + /** + * 根据jwt获取token重的用户信息 + * + * @param cache 缓存 + * @param accessToken token + * @return + */ + public static AuthUser getAuthUser(Cache cache, String accessToken) { + try { + if (cache.keys("*" + accessToken).size() == 0) { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + //获取token的信息 + Claims claims + = Jwts.parser() + .setSigningKey(SecretKeyUtil.generalKeyByDecoders()) + .parseClaimsJws(accessToken).getBody(); + //获取存储在claims中的用户信息 + String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString(); + return new Gson().fromJson(json, AuthUser.class); + } catch (Exception e) { + return null; + } + } +} diff --git a/framework/src/main/java/cn/lili/common/security/context/UserContextInit.java b/framework/src/main/java/cn/lili/common/security/context/UserContextInit.java new file mode 100644 index 00000000..eb9ff50e --- /dev/null +++ b/framework/src/main/java/cn/lili/common/security/context/UserContextInit.java @@ -0,0 +1,37 @@ +package cn.lili.common.security.context; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +/** + * 给予用户上下文,初始化参数 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/14 20:30 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class UserContextInit implements ApplicationRunner { + + /** + * 用户信息holder,认证信息的获取者 + */ + private final AuthenticationHandler authenticationHandler; + + /** + * 在项目加载时指定认证信息获取者 + * 默认是由spring 安全上下文中获取 + * + * @param args + * @throws Exception + */ + @Override + public void run(ApplicationArguments args) { + UserContext.setHolder(authenticationHandler); + } +} diff --git a/framework/src/main/java/cn/lili/common/security/enums/SecurityEnum.java b/framework/src/main/java/cn/lili/common/security/enums/SecurityEnum.java new file mode 100644 index 00000000..4d27d941 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/security/enums/SecurityEnum.java @@ -0,0 +1,24 @@ +package cn.lili.common.security.enums; + +/** + * 安全相关常量 + * + * @author Chopper + */ +public enum SecurityEnum { + + /** + * 存在与header中的token参数头 名 + */ + HEADER_TOKEN("accessToken"), USER_CONTEXT("userContext"), JWT_SECRET("secret"); + + String value; + + SecurityEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/framework/src/main/java/cn/lili/common/security/enums/UserEnums.java b/framework/src/main/java/cn/lili/common/security/enums/UserEnums.java new file mode 100644 index 00000000..cb30323c --- /dev/null +++ b/framework/src/main/java/cn/lili/common/security/enums/UserEnums.java @@ -0,0 +1,28 @@ +package cn.lili.common.security.enums; + +/** + * token角色类型 + * + * @author Chopper + * @version v1.0 + * @Description: + * @since 2020/8/18 15:23 + */ +public enum UserEnums { + /** + * 角色 + */ + MEMBER("会员"), + STORE("商家"), + MANAGER("管理员"), + SYSTEM("系统"); + private final String role; + + UserEnums(String role) { + this.role = role; + } + + public String getRole() { + return role; + } +} diff --git a/framework/src/main/java/cn/lili/common/sms/AliSmsUtil.java b/framework/src/main/java/cn/lili/common/sms/AliSmsUtil.java new file mode 100644 index 00000000..d2a290f7 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/sms/AliSmsUtil.java @@ -0,0 +1,76 @@ +package cn.lili.common.sms; + +import cn.lili.modules.message.entity.dos.SmsSign; +import cn.lili.modules.message.entity.dos.SmsTemplate; + +import java.util.Map; + +/** + * @author Chopper + * @version v4.1 + * @Description: + * @since 2021/2/1 6:05 下午 + */ +public interface AliSmsUtil { + /** + * 申请短信签名 + * + * @param smsSign 短信签名 + */ + void addSmsSign(SmsSign smsSign) throws Exception; + + + /** + * 删除短信签名 + * + * @param signName 签名名称 + */ + void deleteSmsSign(String signName) throws Exception; + + /** + * 查询短信签名申请状态 + * + * @param signName 签名名称 + */ + Map querySmsSign(String signName) throws Exception; + + /** + * 修改未审核通过的短信签名,并重新提交审核。 + * + * @param smsSign 短信签名 + */ + void modifySmsSign(SmsSign smsSign) throws Exception; + + /** + * 修改未审核通过的短信模板,并重新提交审核。 + * + * @param smsTemplate 短信模板 + * @throws Exception + */ + void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception; + + /** + * 查看短信模板 + * + * @param templateCode 短信模板CODE + * @throws Exception + */ + Map querySmsTemplate(String templateCode) throws Exception; + + /** + * 申请短信模板 + * + * @param smsTemplate 短信模板 + * @return + * @throws Exception + */ + String addSmsTemplate(SmsTemplate smsTemplate) throws Exception; + + /** + * 删除短信模板 + * + * @param templateCode 短信模板CODE + * @throws Exception + */ + void deleteSmsTemplate(String templateCode) throws Exception; +} diff --git a/framework/src/main/java/cn/lili/common/sms/SmsUtil.java b/framework/src/main/java/cn/lili/common/sms/SmsUtil.java new file mode 100644 index 00000000..0732b9b3 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/sms/SmsUtil.java @@ -0,0 +1,57 @@ +package cn.lili.common.sms; + +import cn.lili.common.verification.enums.VerificationEnums; + +import java.util.List; +import java.util.Map; + +/** + * 短信接口 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/30 15:44 + */ +public interface SmsUtil { + + + /** + * 验证码发送 + * + * @param mobile 手机号 + * @param verificationEnums 验证码场景 + * @param uuid 用户标识uuid + */ + void sendSmsCode(String mobile, VerificationEnums verificationEnums, String uuid); + + /** + * 验证码验证 + * + * @param mobile 手机号 + * @param verificationEnums 验证码场景 + * @param uuid 用户标识uuid + * @param code 待验证code + */ + boolean verifyCode(String mobile, VerificationEnums verificationEnums, String uuid, String code); + + /** + * 短信发送 + * + * @param mobile 接收手机号 + * @param param 参数 + * @param templateCode 模版code + */ + void sendSmsCode(String signName, String mobile, Map param, String templateCode); + + /** + * 短信批量发送 + * + * @param mobile 接收手机号 + * @param signName 签名 + * @param templateCode 模版code + */ + void sendBatchSms(String signName, List mobile, String templateCode); + + +} diff --git a/framework/src/main/java/cn/lili/common/sms/impl/SmsUtilAliImplService.java b/framework/src/main/java/cn/lili/common/sms/impl/SmsUtilAliImplService.java new file mode 100644 index 00000000..19394748 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/sms/impl/SmsUtilAliImplService.java @@ -0,0 +1,343 @@ +package cn.lili.common.sms.impl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.sms.AliSmsUtil; +import cn.lili.common.sms.SmsUtil; +import cn.lili.common.utils.CommonUtil; +import cn.lili.common.verification.enums.VerificationEnums; +import cn.lili.modules.message.entity.dos.SmsSign; +import cn.lili.modules.message.entity.dos.SmsTemplate; +import cn.lili.modules.connect.util.Base64Utils; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.SmsSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.aliyun.dysmsapi20170525.models.*; +import com.aliyun.teaopenapi.models.Config; +import com.google.gson.Gson; +import com.xkcoding.http.util.StringUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 短信网管阿里云实现 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/30 15:44 + */ +@Component +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SmsUtilAliImplService implements SmsUtil, AliSmsUtil { + + private final Cache cache; + + private final SettingService settingService; + + private final MemberService memberService; + + @Override + public void sendSmsCode(String mobile, VerificationEnums verificationEnums, String uuid) { + + String code = CommonUtil.getRandomNum(); + code = "111111"; + + switch (verificationEnums) { + //如果某个模版需要自定义,则在此处进行调整 + case LOGIN: + case REGISTER: + case FIND_USER: { + + //准备发送短信参数 + Map params = new HashMap<>(); + params.put("code", code); + cache.put(cacheKey(verificationEnums, mobile, uuid), code, 300L); + //this.sendSmsCode("北京宏业汇成科技有限公司",mobile, params, verificationEnums.getSmsTemplate()); + break; + } + case UPDATE_PASSWORD: { + Member member = memberService.getById(UserContext.getCurrentUser().getId()); + if (member == null || StringUtil.isEmpty(member.getMobile())) { + return; + } + String memberMobile = member.getMobile(); + //准备发送短信参数 + Map params = new HashMap<>(); + params.put("code", code); + cache.put(cacheKey(verificationEnums, memberMobile, uuid), code, 300L); + //this.sendSmsCode("北京宏业汇成科技有限公司",mobile, params, verificationEnums.getSmsTemplate()); + break; + } + //如果不是有效的验证码手段,则此处不进行短信操作 + default: + return; + } + } + + @Override + public boolean verifyCode(String mobile, VerificationEnums verificationEnums, String uuid, String code) { + Object result = cache.get(cacheKey(verificationEnums, mobile, uuid)); + if (code.equals(result)) { + //校验之后,删除 + cache.remove(cacheKey(verificationEnums, mobile, uuid)); + return true; + } else { + return false; + } + + } + + @Override + public void sendSmsCode(String signName, String mobile, Map param, String templateCode) { + + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + SendSmsRequest sendSmsRequest = new SendSmsRequest() + .setSignName(signName) + .setPhoneNumbers(mobile) + .setTemplateCode(templateCode) + .setTemplateParam(JSONUtil.toJsonStr(param)); + try { + SendSmsResponse response = client.sendSms(sendSmsRequest); + System.out.println(response.getBody().getCode()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void sendBatchSms(String signName, List mobile, String templateCode) { + + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + + List sign = mobile; + + sign.replaceAll(e -> signName); + + //手机号拆成多个小组进行发送 + List> mobileList = new ArrayList<>(); + + //签名名称多个小组 + List> signNameList = new ArrayList<>(); + + //循环分组 + for (int i = 0; i < (mobile.size() / 100 + (mobile.size() % 100 == 0 ? 0 : 1)); i++) { + int endPoint = Math.min((100 + (i * 100)), mobile.size()); + mobileList.add(mobile.subList((i * 100), endPoint)); + signNameList.add(sign.subList((i * 100), endPoint)); + } + + //发送短信 + for (int i = 0; i < mobileList.size(); i++) { + SendBatchSmsRequest sendBatchSmsRequest = new SendBatchSmsRequest() + .setPhoneNumberJson(JSONUtil.toJsonStr(mobileList.get(i))) + .setSignNameJson(JSONUtil.toJsonStr(signNameList.get(i))) + .setTemplateCode(templateCode); + try { + client.sendBatchSms(sendBatchSmsRequest); + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + + @Override + public void addSmsSign(SmsSign smsSign) throws Exception { + //设置参数添加短信签名 + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + + AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList0 = new AddSmsSignRequest.AddSmsSignRequestSignFileList() + .setFileContents(Base64Utils.encode(smsSign.getBusinessLicense())) + .setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); + AddSmsSignRequest.AddSmsSignRequestSignFileList signFileList1 = new AddSmsSignRequest.AddSmsSignRequestSignFileList() + .setFileContents(Base64Utils.encode(smsSign.getLicense())) + .setFileSuffix(smsSign.getLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); + AddSmsSignRequest addSmsSignRequest = new AddSmsSignRequest() + .setSignName(smsSign.getSignName()) + .setSignSource(smsSign.getSignSource()) + .setRemark(smsSign.getRemark()) + .setSignFileList(java.util.Arrays.asList( + signFileList0, + signFileList1 + )); + AddSmsSignResponse response = client.addSmsSign(addSmsSignRequest); + if (!response.getBody().getCode().equals("OK")) { + throw new ServiceException(response.getBody().getMessage()); + } + } + + @Override + public void deleteSmsSign(String signName) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + DeleteSmsSignRequest deleteSmsSignRequest = new DeleteSmsSignRequest() + .setSignName(signName); + + DeleteSmsSignResponse response = client.deleteSmsSign(deleteSmsSignRequest); + if (!response.getBody().getCode().equals("OK")) { + throw new ServiceException(response.getBody().getMessage()); + } + + } + + @Override + public Map querySmsSign(String signName) throws Exception { + //设置参数查看短信签名 + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + QuerySmsSignRequest querySmsSignRequest = new QuerySmsSignRequest().setSignName(signName); + + QuerySmsSignResponse response = client.querySmsSign(querySmsSignRequest); + if (!response.getBody().getCode().equals("OK")) { + throw new ServiceException(response.getBody().getMessage()); + } + Map map = new HashMap<>(); + map.put("SignStatus", response.getBody().getSignStatus()); + map.put("Reason", response.getBody().getReason()); + return map; + } + + @Override + public void modifySmsSign(SmsSign smsSign) throws Exception { + //设置参数添加短信签名 + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + + ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList0 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList() + .setFileContents(Base64Utils.encode(smsSign.getBusinessLicense())) + .setFileSuffix(smsSign.getBusinessLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); + ModifySmsSignRequest.ModifySmsSignRequestSignFileList signFileList1 = new ModifySmsSignRequest.ModifySmsSignRequestSignFileList() + .setFileContents(Base64Utils.encode(smsSign.getLicense())) + .setFileSuffix(smsSign.getLicense().substring(smsSign.getBusinessLicense().lastIndexOf(".") + 1)); + ModifySmsSignRequest modifySmsSign = new ModifySmsSignRequest() + .setSignName(smsSign.getSignName()) + .setSignSource(smsSign.getSignSource()) + .setRemark(smsSign.getRemark()) + .setSignFileList(java.util.Arrays.asList( + signFileList0, + signFileList1 + )); + ModifySmsSignResponse response = client.modifySmsSign(modifySmsSign); + if (!response.getBody().getCode().equals("OK")) { + throw new ServiceException(response.getBody().getMessage()); + } + } + + @Override + public void modifySmsTemplate(SmsTemplate smsTemplate) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + ModifySmsTemplateRequest modifySmsTemplateRequest = new ModifySmsTemplateRequest() + .setTemplateType(smsTemplate.getTemplateType()) + .setTemplateName(smsTemplate.getTemplateName()) + .setTemplateContent(smsTemplate.getTemplateContent()) + .setRemark(smsTemplate.getRemark()) + .setTemplateCode(smsTemplate.getTemplateCode()); + + ModifySmsTemplateResponse response = client.modifySmsTemplate(modifySmsTemplateRequest); + if (!response.getBody().getCode().equals("OK")) { + throw new ServiceException(response.getBody().getMessage()); + } + } + + @Override + public Map querySmsTemplate(String templateCode) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + QuerySmsTemplateRequest querySmsTemplateRequest = new QuerySmsTemplateRequest() + .setTemplateCode(templateCode); + QuerySmsTemplateResponse response = client.querySmsTemplate(querySmsTemplateRequest); + + if (!response.getBody().getCode().equals("OK")) { + throw new ServiceException(response.getBody().getMessage()); + } + Map map = new HashMap<>(); + map.put("TemplateStatus", response.getBody().getTemplateStatus()); + map.put("Reason", response.getBody().getReason()); + map.put("TemplateCode", response.getBody().getTemplateCode()); + return map; + } + + @Override + public String addSmsTemplate(SmsTemplate smsTemplate) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + AddSmsTemplateRequest addSmsTemplateRequest = new AddSmsTemplateRequest() + .setTemplateType(1) + .setTemplateName(smsTemplate.getTemplateName()) + .setTemplateContent(smsTemplate.getTemplateContent()) + .setRemark(smsTemplate.getRemark()); + + AddSmsTemplateResponse response = client.addSmsTemplate(addSmsTemplateRequest); + if (!response.getBody().getCode().equals("OK")) { + throw new ServiceException(response.getBody().getMessage()); + } + return response.getBody().getTemplateCode(); + } + + @Override + public void deleteSmsTemplate(String templateCode) throws Exception { + com.aliyun.dysmsapi20170525.Client client = this.createClient(); + DeleteSmsTemplateRequest deleteSmsTemplateRequest = new DeleteSmsTemplateRequest() + .setTemplateCode(templateCode); + + DeleteSmsTemplateResponse response = client.deleteSmsTemplate(deleteSmsTemplateRequest); + if (!response.getBody().getCode().equals("OK")) { + throw new ServiceException(response.getBody().getMessage()); + } + } + + + /** + * 使用AK&SK初始化账号Client + * + * @return Client + * @throws Exception + */ + public com.aliyun.dysmsapi20170525.Client createClient() { + try { + Setting setting = settingService.getById(SettingEnum.SMS_SETTING.name()); + if (StrUtil.isBlank(setting.getSettingValue())) { + throw new ServiceException("您还未配置阿里云短信"); + } + SmsSetting smsSetting = new Gson().fromJson(setting.getSettingValue(), SmsSetting.class); + + Config config = new Config(); + // 您的AccessKey ID + //config.accessKeyId = smsSetting.getAccessKeyId(); + config.accessKeyId = "LTAI4G4deX59EyjpEULaJdsU"; + // 您的AccessKey Secret + //config.accessKeySecret = smsSetting.getAccessSecret(); + config.accessKeySecret = "BlRBpl7WBman6GYYwLKMiKqMTXFhWf"; + // 访问的域名 + config.endpoint = "dysmsapi.aliyuncs.com"; + return new com.aliyun.dysmsapi20170525.Client(config); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 生成缓存key + * + * @param verificationEnums 验证场景 + * @param mobile 手机号码 + * @param uuid 用户标识 uuid + * @return + */ + static String cacheKey(VerificationEnums verificationEnums, String mobile, String uuid) { + return CachePrefix.SMS_CODE.getPrefix() + verificationEnums.name() + mobile; + } +} diff --git a/framework/src/main/java/cn/lili/common/test/BaseTest.java b/framework/src/main/java/cn/lili/common/test/BaseTest.java new file mode 100644 index 00000000..a9ed9b69 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/test/BaseTest.java @@ -0,0 +1,36 @@ +package cn.lili.common.test; + +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListener; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; + +/** + * BaseTest + * + * @author Chopper + * @version v1.0 + * @since + * 2020-06-13 12:17 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +@Transactional(rollbackFor = Exception.class) +@Rollback() +@ContextConfiguration +@Configuration +@ComponentScan("cn.lili") +public class BaseTest implements TestExecutionListener { + @Override + public void beforeTestClass(TestContext testContext) throws Exception { + //设置环境变量 解决es冲突 + System.setProperty("es.set.netty.runtime.available.processors", "false"); + } + +} diff --git a/framework/src/main/java/cn/lili/common/token/PermissionEnum.java b/framework/src/main/java/cn/lili/common/token/PermissionEnum.java new file mode 100644 index 00000000..be6e1d28 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/token/PermissionEnum.java @@ -0,0 +1,19 @@ +package cn.lili.common.token; + +/** + * 权限枚举值 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/25 09:21 + */ + +public enum PermissionEnum { + + /** + * 超级权限,查看权限 + */ + SUPER, QUERY + +} diff --git a/framework/src/main/java/cn/lili/common/token/SecretKeyUtil.java b/framework/src/main/java/cn/lili/common/token/SecretKeyUtil.java new file mode 100644 index 00000000..d5e4d9a8 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/token/SecretKeyUtil.java @@ -0,0 +1,27 @@ +package cn.lili.common.token; + +import com.google.api.client.repackaged.org.apache.commons.codec.binary.Base64; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; + +import javax.crypto.SecretKey; + +/** + * SignWithUtil + * + * @author Chopper + * @version v1.0 + * 2020-11-18 17:30 + */ +public class SecretKeyUtil { + public static SecretKey generalKey() { + byte[] encodedKey = Base64.decodeBase64("cuAihCz53DZRjZwbsGcZJ2Ai6At+T142uphtJMsk7iQ=");//自定义 + javax.crypto.SecretKey key = Keys.hmacShaKeyFor(encodedKey); + return key; + } + + public static SecretKey generalKeyByDecoders() { + return Keys.hmacShaKeyFor(Decoders.BASE64.decode("cuAihCz53DZRjZwbsGcZJ2Ai6At+T142uphtJMsk7iQ=")); + + } +} diff --git a/framework/src/main/java/cn/lili/common/token/Token.java b/framework/src/main/java/cn/lili/common/token/Token.java new file mode 100644 index 00000000..94be6fb3 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/token/Token.java @@ -0,0 +1,24 @@ +package cn.lili.common.token; + +import lombok.Data; + +/** + * Token 实体类 + * + * @author Chopper + * @version v1.0 + * 2020-11-13 10:02 + */ +@Data +public class Token { + /** + * 访问token + */ + private String accessToken; + + /** + * 刷新token + */ + private String refreshToken; + +} diff --git a/framework/src/main/java/cn/lili/common/token/TokenUtil.java b/framework/src/main/java/cn/lili/common/token/TokenUtil.java new file mode 100644 index 00000000..a0a2830c --- /dev/null +++ b/framework/src/main/java/cn/lili/common/token/TokenUtil.java @@ -0,0 +1,138 @@ +package cn.lili.common.token; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.enums.SecurityEnum; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.config.properties.JWTTokenProperties; +import com.google.gson.Gson; +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.SignatureException; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.concurrent.TimeUnit; + +/** + * TokenUtil + * + * @author Chopper + * @version v1.0 + * 2020-11-12 18:44 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class TokenUtil { + + private final JWTTokenProperties tokenProperties; + + private final Cache cache; + + /** + * 构建token + * + * @param username 主体 + * @param claim 私有声明 + * @param longTerm 长时间特殊token 如:移动端,微信小程序等 + * @return + */ + public Token createToken(String username, Object claim, boolean longTerm, UserEnums userEnums) { + Token token = new Token(); + //访问token + String accessToken = createToken(username, claim, tokenProperties.getTokenExpireTime()); + + cache.put(CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + accessToken, 1, + tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES); + //刷新token生成策略:如果是长时间有效的token(用于app),则默认15天有效期刷新token。如果是普通用户登录,则刷新token为普通token2倍数 + Long expireTime = longTerm ? 15 * 24 * 60L : tokenProperties.getTokenExpireTime() * 2; + String refreshToken = createToken(username, claim, expireTime); + + cache.put(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + refreshToken, 1, expireTime, TimeUnit.MINUTES); + + token.setAccessToken(accessToken); + token.setRefreshToken(refreshToken); + return token; + } + + /** + * 刷新token + * + * @param oldRefreshToken 刷新token + * @return token + */ + public Token refreshToken(String oldRefreshToken, UserEnums userEnums) { + + Claims claims; + try { + claims = Jwts.parser() + .setSigningKey(SecretKeyUtil.generalKeyByDecoders()) + .parseClaimsJws(oldRefreshToken).getBody(); + } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e) { + //token 过期 认证失败等 + throw new ServiceException(ResultCode.USER_AUTH_EXPIRED); + } + + //获取存储在claims中的用户信息 + String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString(); + AuthUser authUser = new Gson().fromJson(json, AuthUser.class); + + + String username = authUser.getUsername(); + //获取是否长期有效的token + boolean longTerm = authUser.getLongTerm(); + + + //如果缓存中有刷新token && + if (cache.hasKey(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + oldRefreshToken)) { + Token token = new Token(); + //访问token + String accessToken = createToken(username, authUser, tokenProperties.getTokenExpireTime()); + cache.put(CachePrefix.ACCESS_TOKEN.getPrefix(userEnums) + accessToken, 1, tokenProperties.getTokenExpireTime(), TimeUnit.MINUTES); + + //如果是信任登录设备,则刷新token长度继续延长 + Long expirationTime = tokenProperties.getTokenExpireTime() * 2; + if (longTerm) { + expirationTime = 60 * 24 * 15L; + } + + //刷新token生成策略:如果是长时间有效的token(用于app),则默认15天有效期刷新token。如果是普通用户登录,则刷新token为普通token2倍数 + String refreshToken = createToken(username, authUser, expirationTime); + + cache.put(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + refreshToken, 1, expirationTime, TimeUnit.MINUTES); + token.setAccessToken(accessToken); + token.setRefreshToken(refreshToken); + cache.remove(CachePrefix.REFRESH_TOKEN.getPrefix(userEnums) + oldRefreshToken); + return token; + } else { + throw new ServiceException(ResultCode.USER_AUTH_EXPIRED); + } + + } + + /** + * 生成token + * + * @param username 主体 + * @param claim 私有神明内容 + * @param expirationTime 过期时间(分钟) + * @return + */ + private String createToken(String username, Object claim, Long expirationTime) { + // JWT 生成 + return Jwts.builder() + // jwt 私有声明 + .claim(SecurityEnum.USER_CONTEXT.getValue(), new Gson().toJson(claim)) + // JWT的主体 + .setSubject(username) + // 失效时间 当前时间+过期分钟 + .setExpiration(new Date(System.currentTimeMillis() + expirationTime * 60 * 1000)) + // 签名算法和密钥 + .signWith(SecretKeyUtil.generalKey()) + .compact(); + } +} diff --git a/framework/src/main/java/cn/lili/common/token/base/AbstractTokenGenerate.java b/framework/src/main/java/cn/lili/common/token/base/AbstractTokenGenerate.java new file mode 100644 index 00000000..ff22fd0c --- /dev/null +++ b/framework/src/main/java/cn/lili/common/token/base/AbstractTokenGenerate.java @@ -0,0 +1,38 @@ +package cn.lili.common.token.base; + +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.token.Token; + +/** + * AbstractToken + * 抽象token,定义生成token类 + * + * @author Chopper + * @version v1.0 + * 2020-11-13 10:13 + */ +public abstract class AbstractTokenGenerate { + + /** + * 生成token + * + * @param username 用户名 + * @param longTerm 是否长时间有效 + * @return + */ + public abstract Token createToken(String username, Boolean longTerm); + + /** + * 刷新token + * + * @param refreshToken 刷新token + * @return token + */ + public abstract Token refreshToken(String refreshToken); + + /** + * 默认role + */ + public UserEnums role = UserEnums.MANAGER; + +} diff --git a/framework/src/main/java/cn/lili/common/token/base/generate/ManagerTokenGenerate.java b/framework/src/main/java/cn/lili/common/token/base/generate/ManagerTokenGenerate.java new file mode 100644 index 00000000..509cc135 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/token/base/generate/ManagerTokenGenerate.java @@ -0,0 +1,132 @@ +package cn.lili.common.token.base.generate; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.token.PermissionEnum; +import cn.lili.common.token.Token; +import cn.lili.common.token.TokenUtil; +import cn.lili.common.token.base.AbstractTokenGenerate; +import cn.lili.modules.permission.entity.dos.AdminUser; +import cn.lili.modules.permission.entity.vo.UserMenuVO; +import cn.lili.modules.permission.service.AdminUserService; +import cn.lili.modules.permission.service.RoleMenuService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 管理员token生成 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/16 10:51 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ManagerTokenGenerate extends AbstractTokenGenerate { + + private AdminUserService adminUserService; + + private final TokenUtil tokenUtil; + + private final RoleMenuService roleMenuService; + + private final Cache cache; + + @Autowired + public void setAdminUserService(AdminUserService adminUserService) { + this.adminUserService = adminUserService; + } + + @Override + public Token createToken(String username, Boolean longTerm) { + // 生成token + AdminUser adminUser = adminUserService.findByUsername(username); + AuthUser user = new AuthUser(adminUser.getUsername(), adminUser.getId(), UserEnums.MANAGER, adminUser.getNickName(), adminUser.getIsSuper()); + + + List userMenuVOList = roleMenuService.findAllMenu(user.getId()); + //缓存权限列表 + cache.put(CachePrefix.PERMISSION_LIST.getPrefix(UserEnums.MANAGER) + user.getId(), this.permissionList(userMenuVOList)); + + return tokenUtil.createToken(username, user, longTerm, UserEnums.MANAGER); + } + + @Override + public Token refreshToken(String refreshToken) { + return tokenUtil.refreshToken(refreshToken, UserEnums.MANAGER); + } + + /** + * 获取用户权限 + * + * @param userMenuVOList + * @return + */ + private Map> permissionList(List userMenuVOList) { + Map> permission = new HashMap<>(); + if (userMenuVOList == null || userMenuVOList.size() == 0) { + return permission; + } + List superPermissions = new ArrayList<>(); + List queryPermissions = new ArrayList<>(); + initPermission(superPermissions, queryPermissions); + + //循环权限菜单 + userMenuVOList.forEach(menu -> { + //循环菜单,赋予用户权限 + if (menu.getPath() != null) { + //获取路径集合 + String[] paths = menu.getPath().split("\\|"); + //for循环路径集合 + for (String path : paths) { + //如果是超级权限 则计入超级权限 + if (menu.getIsSupper()) { + //如果已有超级权限,则这里就不做权限的累加 + if (!superPermissions.contains(path)) { + superPermissions.add(path); + } + } + //否则计入浏览权限 + else { + //如果已有超级权限,或者已有普通查看权限,则这里就不做权限的累加 + if (!superPermissions.contains(path) && !queryPermissions.contains(path)) { + queryPermissions.add(path); + } + } + } + } + + //去除无效的权限 + superPermissions.forEach(queryPermissions::remove); + }); + permission.put(PermissionEnum.SUPER.name(), superPermissions); + permission.put(PermissionEnum.QUERY.name(), queryPermissions); + return permission; + } + + /** + * 初始赋予的权限,查看权限包含首页流量统计权限, + * 超级权限包含个人信息维护,密码修改权限 + * + * @param superPermissions 超级权限 + * @param queryPermissions 查询权限 + */ + void initPermission(List superPermissions, List queryPermissions) { + //用户信息维护 + superPermissions.add("/manager/user/info"); + superPermissions.add("/manager/user/edit"); + superPermissions.add("/manager/user/editPassword*"); + //统计查看 + queryPermissions.add("/manager/statistics*"); + } + +} diff --git a/framework/src/main/java/cn/lili/common/token/base/generate/MemberTokenGenerate.java b/framework/src/main/java/cn/lili/common/token/base/generate/MemberTokenGenerate.java new file mode 100644 index 00000000..91bf6e50 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/token/base/generate/MemberTokenGenerate.java @@ -0,0 +1,74 @@ +package cn.lili.common.token.base.generate; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.token.Token; +import cn.lili.common.token.TokenUtil; +import cn.lili.common.token.base.AbstractTokenGenerate; +import cn.lili.config.context.ThreadContextHolder; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; + +/** + * 会员token生成 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/16 10:50 + */ + +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberTokenGenerate extends AbstractTokenGenerate { + + + private MemberService memberService; + + private final TokenUtil tokenUtil; + + @Override + public Token createToken(String username, Boolean longTerm) { + + Member member = memberService.findByUsername(username); + + //获取客户端类型 + String clientType = ThreadContextHolder.getHttpRequest().getHeader("clientType"); + ClientTypeEnum clientTypeEnum; + try { + //如果客户端为空,则缺省值为PC,pc第三方登录时不会传递此参数 + if (clientType == null) { + clientTypeEnum = ClientTypeEnum.PC; + } else { + clientTypeEnum = ClientTypeEnum.valueOf(clientType); + } + } catch (IllegalArgumentException e) { + clientTypeEnum = ClientTypeEnum.UNKNOWN; + } + //记录最后登录时间,客户端类型 + member.setLastLoginDate(new Date()); + member.setClientEnum(clientTypeEnum.name()); + memberService.updateById(member); + + AuthUser authUser = new AuthUser(member.getUsername(), member.getId(),member.getNickName(), UserEnums.MEMBER); + // 登陆成功生成token + return tokenUtil.createToken(username, authUser, longTerm, UserEnums.MEMBER); + } + + @Override + public Token refreshToken(String refreshToken) { + return tokenUtil.refreshToken(refreshToken, UserEnums.MEMBER); + } + + + @Autowired + public void setMemberService(MemberService memberService) { + this.memberService = memberService; + } +} diff --git a/framework/src/main/java/cn/lili/common/token/base/generate/StoreTokenGenerate.java b/framework/src/main/java/cn/lili/common/token/base/generate/StoreTokenGenerate.java new file mode 100644 index 00000000..b31b0871 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/token/base/generate/StoreTokenGenerate.java @@ -0,0 +1,68 @@ +package cn.lili.common.token.base.generate; + +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.token.Token; +import cn.lili.common.token.TokenUtil; +import cn.lili.common.token.base.AbstractTokenGenerate; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 商家token生成 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/16 10:51 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreTokenGenerate extends AbstractTokenGenerate { + + private MemberService memberService; + + private StoreService storeService; + + private final TokenUtil tokenUtil; + + @Autowired + public void setMemberService(MemberService memberService) { + this.memberService = memberService; + } + + @Autowired + public void setStoreService(StoreService storeService) { + this.storeService = storeService; + } + + @Override + public Token createToken(String username, Boolean longTerm) { + // 生成token + Member member = memberService.findByUsername(username); + if (member.getHaveStore().equals(SwitchEnum.CLOSE.name())) { + throw new ServiceException("该会员未开通店铺"); + } + AuthUser user = new AuthUser(member.getUsername(), member.getId(),member.getNickName(), UserEnums.STORE); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Store::getMemberId, member.getId()); + Store store = storeService.getOne(queryWrapper); + user.setStoreId(store.getId()); + user.setStoreName(store.getStoreName()); + return tokenUtil.createToken(username, user, longTerm, UserEnums.STORE); + } + + @Override + public Token refreshToken(String refreshToken) { + return tokenUtil.refreshToken(refreshToken, UserEnums.STORE); + } + +} diff --git a/framework/src/main/java/cn/lili/common/trigger/RocketmqTimerTrigger.java b/framework/src/main/java/cn/lili/common/trigger/RocketmqTimerTrigger.java new file mode 100644 index 00000000..ed35e667 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/trigger/RocketmqTimerTrigger.java @@ -0,0 +1,83 @@ +package cn.lili.common.trigger; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.trigger.delay.PromotionDelayQueue; +import cn.lili.common.trigger.interfaces.TimeTrigger; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import cn.lili.common.trigger.util.TimeTriggerUtil; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.StringUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.stereotype.Component; + +/** + * @author paulG + * @since 2020/11/5 + **/ +@Component +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RocketmqTimerTrigger implements TimeTrigger { + + private final RocketMQTemplate rocketMQTemplate; + + private final Cache cache; + + private PromotionDelayQueue promotionDelayQueue; + + @Autowired + public void setPromotionDelayQueue(PromotionDelayQueue promotionDelayQueue) { + this.promotionDelayQueue = promotionDelayQueue; + } + + @Override + public void add(String executorName, Object param, Long triggerTime, String uniqueKey, String topic) { + + + TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(executorName, triggerTime, param, uniqueKey, topic); + Message message = MessageBuilder.withPayload(timeTriggerMsg).build(); + + this.rocketMQTemplate.asyncSend(topic, message, RocketmqSendCallbackBuilder.commonCallback()); + } + + @Override + public void add(TimeTriggerMsg timeTriggerMsg) { + this.add(timeTriggerMsg.getTriggerExecutor(), timeTriggerMsg.getParam(), timeTriggerMsg.getTriggerTime(), timeTriggerMsg.getUniqueKey(), timeTriggerMsg.getTopic()); + } + + @Override + public void addDelay(TimeTriggerMsg timeTriggerMsg, int delayTime) { + String uniqueKey = timeTriggerMsg.getUniqueKey(); + if (StringUtils.isEmpty(uniqueKey)) { + uniqueKey = StringUtils.getRandStr(10); + } + String generateKey = TimeTriggerUtil.generateKey(timeTriggerMsg.getTriggerExecutor(), timeTriggerMsg.getTriggerTime(), uniqueKey); + this.cache.put(generateKey, 1); + if (Boolean.TRUE.equals(promotionDelayQueue.addJobId(JSONUtil.toJsonStr(timeTriggerMsg), delayTime))) { + log.info("add Redis key {} --------------------------", generateKey); + log.info("定时执行在【" + DateUtil.toString(timeTriggerMsg.getTriggerTime(), "yyyy-MM-dd HH:mm:ss") + "】,消费【" + timeTriggerMsg.getParam().toString() + "】"); + } else { + log.info("延时任务添加失败!"); + } + } + + @Override + public void edit(String executorName, Object param, Long oldTriggerTime, Long triggerTime, String uniqueKey, int delayTime, String topic) { + this.delete(executorName, oldTriggerTime, uniqueKey, topic); + this.addDelay(new TimeTriggerMsg(executorName, triggerTime, param, uniqueKey, topic), delayTime); + } + + @Override + public void delete(String executorName, Long triggerTime, String uniqueKey, String topic) { + String generateKey = TimeTriggerUtil.generateKey(executorName, triggerTime, uniqueKey); + log.info("delete redis key {} -----------------------", generateKey); + this.cache.remove(generateKey); + } +} diff --git a/framework/src/main/java/cn/lili/common/trigger/delay/PromotionDelayQueue.java b/framework/src/main/java/cn/lili/common/trigger/delay/PromotionDelayQueue.java new file mode 100644 index 00000000..724af151 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/trigger/delay/PromotionDelayQueue.java @@ -0,0 +1,35 @@ +package cn.lili.common.trigger.delay; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.delayqueue.AbstractDelayQueueMachineFactory; +import cn.lili.common.trigger.interfaces.TimeTrigger; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 促销延迟队列 + * + * @author paulG + * @version v4.1 + * @date 2020/11/17 7:19 下午 + * @description + * @since 1 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PromotionDelayQueue extends AbstractDelayQueueMachineFactory { + + private final TimeTrigger timeTrigger; + + @Override + public void invoke(String jobId) { + timeTrigger.add(JSONUtil.toBean(jobId, TimeTriggerMsg.class)); + } + + @Override + public String setDelayQueueName() { + return "promotion_delay"; + } +} diff --git a/framework/src/main/java/cn/lili/common/trigger/interfaces/TimeTrigger.java b/framework/src/main/java/cn/lili/common/trigger/interfaces/TimeTrigger.java new file mode 100644 index 00000000..b0099950 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/trigger/interfaces/TimeTrigger.java @@ -0,0 +1,64 @@ +package cn.lili.common.trigger.interfaces; + +import cn.lili.common.trigger.model.TimeTriggerMsg; + +/** + * 延时执行接口 + * + * @author Chopper + */ +public interface TimeTrigger { + + /** + * 添加延时任务 + * + * @param executorName 执行器beanId + * @param param 执行参数 + * @param triggerTime 执行时间 时间戳 秒为单位 + * @param uniqueKey 如果是一个 需要有 修改/取消 延时任务功能的延时任务,
+ * 请填写此参数,作为后续删除,修改做为唯一凭证
+ * 建议参数为:COUPON_{ACTIVITY_ID} 例如 coupon_123
+ * 业务内全局唯一 + * @param topic rocketmq topic + */ + void add(String executorName, Object param, Long triggerTime, String uniqueKey, String topic); + + /** + * 添加延时任务 + * + * @param timeTriggerMsg 延时任务信息 + */ + void add(TimeTriggerMsg timeTriggerMsg); + + + /** + * 添加延时任务 + * + * @param timeTriggerMsg 延时任务信息 + * @param delayTime 延时时间(秒) + */ + void addDelay(TimeTriggerMsg timeTriggerMsg, int delayTime); + + /** + * 修改延时任务 + * + * @param executorName 执行器beanId + * @param param 执行参数 + * @param triggerTime 执行时间 时间戳 秒为单位 + * @param oldTriggerTime 旧的任务执行时间 + * @param uniqueKey 添加任务时的唯一凭证 + * @param delayTime 延时时间(秒) + * @param topic rocketmq topic + */ + void edit(String executorName, Object param, Long oldTriggerTime, Long triggerTime, String uniqueKey, int delayTime, String topic); + + /** + * 删除延时任务 + * + * @param executorName 执行器 + * @param triggerTime 执行时间 + * @param uniqueKey 添加任务时的唯一凭证 + * @param topic rocketmq topic + */ + void delete(String executorName, Long triggerTime, String uniqueKey, String topic); +} diff --git a/framework/src/main/java/cn/lili/common/trigger/interfaces/TimeTriggerExecutor.java b/framework/src/main/java/cn/lili/common/trigger/interfaces/TimeTriggerExecutor.java new file mode 100644 index 00000000..93531920 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/trigger/interfaces/TimeTriggerExecutor.java @@ -0,0 +1,17 @@ +package cn.lili.common.trigger.interfaces; + +/** + * 延时任务执行器接口 + * @author Chopper + * + */ +public interface TimeTriggerExecutor { + + + /** + * 执行任务 + * @param object 任务参数 + */ + void execute(Object object); + +} diff --git a/framework/src/main/java/cn/lili/common/trigger/model/TimeExecuteConstant.java b/framework/src/main/java/cn/lili/common/trigger/model/TimeExecuteConstant.java new file mode 100644 index 00000000..6438de52 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/trigger/model/TimeExecuteConstant.java @@ -0,0 +1,24 @@ +package cn.lili.common.trigger.model; + +/** + * @author paulG + * @since 2020/8/20 + **/ +public abstract class TimeExecuteConstant { + + /** + * 促销延迟加载执行器 + */ + public static final String PROMOTION_EXECUTOR = "promotionTimeTriggerExecutor"; + + /** + * 拼团延迟加载执行器 + */ + public static final String PINTUAN_EXECUTOR = "pintuanTimeTriggerExecutor"; + + /** + * 拼团延迟加载执行器 + */ + public static final String FULL_DISCOUNT_EXECUTOR = "fullDiscountTimeTriggerExecutor"; + +} diff --git a/framework/src/main/java/cn/lili/common/trigger/model/TimeTriggerMsg.java b/framework/src/main/java/cn/lili/common/trigger/model/TimeTriggerMsg.java new file mode 100644 index 00000000..09ed7b00 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/trigger/model/TimeTriggerMsg.java @@ -0,0 +1,50 @@ +package cn.lili.common.trigger.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 延时任务消息 + * + * @author Chopper + * @version v1.0 + * @since + * 2019-02-12 下午5:46 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TimeTriggerMsg implements Serializable { + + + private static final long serialVersionUID = 8897917127201859535L; + + /** + * 执行器beanId + */ + private String triggerExecutor; + + /** + * 执行器 执行时间 + */ + private Long triggerTime; + + /** + * 执行器参数 + */ + private Object param; + + /** + * 唯一KEY + */ + private String uniqueKey; + + /** + * 信息队列主题 + */ + private String topic; + +} diff --git a/framework/src/main/java/cn/lili/common/trigger/util/TimeTriggerUtil.java b/framework/src/main/java/cn/lili/common/trigger/util/TimeTriggerUtil.java new file mode 100644 index 00000000..7475c10d --- /dev/null +++ b/framework/src/main/java/cn/lili/common/trigger/util/TimeTriggerUtil.java @@ -0,0 +1,28 @@ +package cn.lili.common.trigger.util; + +/** + * 延时任务mq实现内容,提供加密算法以及任务前缀参数 + * + * @author Chopper + */ +public class TimeTriggerUtil { + + /** + * 前缀 + */ + private static final String PREFIX = "{rocketmq_trigger}_"; + + /** + * 生成延时任务标识key + * + * @param executorName 执行器beanId + * @param triggerTime 执行时间 + * @param uniqueKey 自定义表示 + * @return 延时任务标识key + */ + public static String generateKey(String executorName, Long triggerTime, String uniqueKey) { + return PREFIX + (executorName + triggerTime + uniqueKey).hashCode(); + } + + +} diff --git a/framework/src/main/java/cn/lili/common/utils/Base64DecodeMultipartFile.java b/framework/src/main/java/cn/lili/common/utils/Base64DecodeMultipartFile.java new file mode 100644 index 00000000..62c7fde0 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/Base64DecodeMultipartFile.java @@ -0,0 +1,117 @@ +package cn.lili.common.utils; + +import org.springframework.web.multipart.MultipartFile; + +import java.util.Base64; +import java.util.Base64.Decoder; + +import java.io.*; + +/** + * base64转为multipartFile工具类 + * + * @author Chopper + */ +public class Base64DecodeMultipartFile implements MultipartFile { + + private final byte[] imgContent; + private final String header; + + public Base64DecodeMultipartFile(byte[] imgContent, String header) { + this.imgContent = imgContent; + this.header = header.split(";")[0]; + } + + @Override + public String getName() { + return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1]; + } + + @Override + public String getOriginalFilename() { + return System.currentTimeMillis() + (int) Math.random() * 10000 + "." + header.split("/")[1]; + } + + @Override + public String getContentType() { + return header.split(":")[1]; + } + + @Override + public boolean isEmpty() { + return imgContent == null || imgContent.length == 0; + } + + @Override + public long getSize() { + return imgContent.length; + } + + @Override + public byte[] getBytes() throws IOException { + return imgContent; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(imgContent); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + new FileOutputStream(dest).write(imgContent); + } + + + public static MultipartFile base64Convert(String base64) { + + String[] baseStrs = base64.split(","); + Decoder decoder = Base64.getDecoder(); + byte[] b = decoder.decode(baseStrs[1]); + + for (int i = 0; i < b.length; ++i) { + if (b[i] < 0) { + b[i] += 256; + } + } + return new Base64DecodeMultipartFile(b, baseStrs[0]); + } + + + public static InputStream base64ToInputStream(String base64) { + ByteArrayInputStream stream = null; + try { + byte[] bytes = Base64.getDecoder().decode(base64); + stream = new ByteArrayInputStream(bytes); + } catch (Exception e) { + e.printStackTrace(); + } + return stream; + } + + public static String inputStreamToStream(InputStream in) { + byte[] data = null; + // 读取图片字节数组 + try { + ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); + byte[] buff = new byte[100]; + int rc = 0; + while ((rc = in.read(buff, 0, 100)) > 0) { + swapStream.write(buff, 0, rc); + } + data = swapStream.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return Base64.getEncoder().encodeToString(data); + } +} + diff --git a/framework/src/main/java/cn/lili/common/utils/BeanUtil.java b/framework/src/main/java/cn/lili/common/utils/BeanUtil.java new file mode 100644 index 00000000..b525fa64 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/BeanUtil.java @@ -0,0 +1,59 @@ +package cn.lili.common.utils; + +import org.springframework.beans.BeanUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * 对象属性复制 + * @author Chopper + */ +public class BeanUtil { + + /** + * 复制属性 + * @param objectFrom + * @param objectTo + */ + public static void copyProperties(Object objectFrom,Object objectTo){ + BeanUtils.copyProperties(objectFrom, objectTo); + } + + + /** + * 获取属性名数组 + */ + public static String[] getFiledName(Object o) { + Field[] fields = o.getClass().getDeclaredFields(); + Field[] superFields = o.getClass().getSuperclass().getDeclaredFields(); + String[] fieldNames = new String[fields.length + superFields.length]; + int index = 0; + for (int i = 0; i < fields.length; i++) { + fieldNames[index] = fields[i].getName(); + index++; + } + for (int i = 0; i < superFields.length; i++) { + if (superFields[i].getName().equals("id")) { + continue; + } + fieldNames[index] = superFields[i].getName(); + index++; + } + return fieldNames; + } + + /* 根据属性名获取属性值 + * */ + public static Object getFieldValueByName(String fieldName, Object o) { + try { + String firstLetter = fieldName.substring(0, 1).toUpperCase(); + String getter = "get" + firstLetter + fieldName.substring(1); + Method method = o.getClass().getMethod(getter, new Class[]{}); + Object value = method.invoke(o, new Object[]{}); + return value; + } catch (Exception e) { + return null; + } + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/CheckMobileUtil.java b/framework/src/main/java/cn/lili/common/utils/CheckMobileUtil.java new file mode 100644 index 00000000..7bec68a4 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/CheckMobileUtil.java @@ -0,0 +1,53 @@ +package cn.lili.common.utils; + +import javax.servlet.http.HttpServletRequest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * CheckMobileUtil + * + * @author Chopper + * @version v1.0 + * 2021-02-04 14:56 + */ +public class CheckMobileUtil { + + + // \b 是单词边界(连着的两个(字母字符 与 非字母字符) 之间的逻辑上的间隔), + // 字符串在编译时会被转码一次,所以是 "\\b" + // \B 是单词内部逻辑间隔(连着的两个字母字符之间的逻辑上的间隔) + static String phoneReg = "\\b(ip(hone|od)|android|opera m(ob|in)i" + + "|windows (phone|ce)|blackberry" + + "|s(ymbian|eries60|amsung)|p(laybook|alm|rofile/midp" + + "|laystation portable)|nokia|fennec|htc[-_]" + + "|mobile|up.browser|[1-4][0-9]{2}x[1-4][0-9]{2})\\b"; + static String tableReg = "\\b(ipad|tablet|(Nexus 7)|up.browser" + + "|[1-4][0-9]{2}x[1-4][0-9]{2})\\b"; + + //移动设备正则匹配:手机端、平板 + static Pattern phonePat = Pattern.compile(phoneReg, Pattern.CASE_INSENSITIVE); + static Pattern tablePat = Pattern.compile(tableReg, Pattern.CASE_INSENSITIVE); + + /** + * 检测是否是移动设备访问 + * + * @param request 浏览器标识 获取方式: + * @return true:移动设备接入,false:pc端接入 + * @Title: check + */ + public static boolean check(HttpServletRequest request) { + String userAgent = request.getHeader("USER-AGENT").toLowerCase(); + if (null == userAgent) { + userAgent = ""; + } + // 匹配 + Matcher matcherPhone = phonePat.matcher(userAgent); + Matcher matcherTable = tablePat.matcher(userAgent); + if (matcherPhone.find() || matcherTable.find()) { + return true; + } else { + return false; + } + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/CommonUtil.java b/framework/src/main/java/cn/lili/common/utils/CommonUtil.java new file mode 100644 index 00000000..e6c8e0e8 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/CommonUtil.java @@ -0,0 +1,51 @@ +package cn.lili.common.utils; + +import java.util.Random; +import java.util.UUID; + +/** + * 通用工具 + * @author Chopper + */ +public class CommonUtil { + + /** + * 以UUID重命名 + * @param fileName + * @return + */ + public static String rename(String fileName) { + String extName = fileName.substring(fileName.lastIndexOf(".")); + return UUID.randomUUID().toString().replace("-", "") + extName; + } + + + /** + * 随机6位数生成 + */ + public static String getRandomNum() { + + Random random = new Random(); + int num = random.nextInt(999999); + //不足六位前面补0 + String str = String.format("%06d", num); + return str; + } + + /** + * 批量递归删除时 判断target是否在ids中 避免重复删除 + * @param target + * @param ids + * @return + */ + public static Boolean judgeIds(String target, String[] ids){ + Boolean flag = false; + for(String id : ids){ + if(id.equals(target)){ + flag = true; + break; + } + } + return flag; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/CookieUtil.java b/framework/src/main/java/cn/lili/common/utils/CookieUtil.java new file mode 100644 index 00000000..a430ebfb --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/CookieUtil.java @@ -0,0 +1,90 @@ +package cn.lili.common.utils; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * CookieUtil + * + * @author Chopper + * @version v1.0 + * 2020-12-14 09:32 + */ +public class CookieUtil { + + /** + * 新增cookie + * + * @param key key值 + * @param value 对应值 + * @param response 响应 + */ + public static void addCookie(String key, String value, HttpServletResponse response) { + try { + Cookie c = new Cookie(key, value); + c.setPath("/"); + response.addCookie(c); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 新增cookie + * + * @param key key值 + * @param value 对应值 + * @param maxAge cookie 有效时间 + * @param response 响应 + */ + public static void addCookie(String key, String value, Integer maxAge, HttpServletResponse response) { + try { + Cookie c = new Cookie(key, value); + c.setMaxAge(maxAge); + c.setPath("/"); + response.addCookie(c); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 删除cookie + * + * @param key key值 + * @param response 响应 + */ + public static void delCookie(String key, HttpServletResponse response) { + try { + Cookie c = new Cookie(key, ""); + c.setMaxAge(0); + response.addCookie(c); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 获取cookie + * + * @param key key值 + * @param request 请求 + * @return cookie value + */ + public static String getCookie(String key, HttpServletRequest request) { + try { + if (request.getCookies() == null) { + return null; + } + for (int i = 0; i < request.getCookies().length; i++) { + if (request.getCookies()[i].getName().equals(key)) { + return request.getCookies()[i].getValue(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/CurrencyUtil.java b/framework/src/main/java/cn/lili/common/utils/CurrencyUtil.java new file mode 100644 index 00000000..d2e21cb5 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/CurrencyUtil.java @@ -0,0 +1,134 @@ +package cn.lili.common.utils; + +import java.math.BigDecimal; + +/** + * 金额计算工具 + */ +public final class CurrencyUtil { + /** + * 默认除法运算精度 + */ + private static final int DEF_DIV_SCALE = 2; + + /** + * 这个类不能实例化 + */ + private CurrencyUtil() { + } + + /** + * 提供精确的加法运算。 + * + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static Double add(double v1, double v2) { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static Double mul(double v1, double v2) { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时, 精确到小数点以后10位,以后的数字四舍五入。 + * + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。 当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。 + * + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) { + if (scale < 0) { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + //如果被除数等于0,则返回0 + if (v2 == 0) { + return 0; + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) { + if (scale < 0) { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + BigDecimal one = new BigDecimal("1"); + return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + /** + * 金额转分 + * + * @param money + * @return + */ + public static Integer fen(Double money) { + double price = mul(money, 100); + return (int) price; + } + + /** + * 金额转分 + * + * @param money + * @return + */ + public static double reversalFen(Double money) { + double price = div(money, 100); + return price; + } + + public static void main(String[] args) { + System.out.println(fen(23.4324)); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/common/utils/DateUtil.java b/framework/src/main/java/cn/lili/common/utils/DateUtil.java new file mode 100644 index 00000000..c6ed8b0b --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/DateUtil.java @@ -0,0 +1,387 @@ +package cn.lili.common.utils; + +import java.text.SimpleDateFormat; +import java.time.*; +import java.util.*; + +/** + * 日期相关的操作 + * + * @author Chopper + */ +public class DateUtil { + + public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + public static final String STANDARD_DATE_FORMAT = "yyyy-MM-dd"; + + public static final String STANDARD_DATE_NO_UNDERLINE_FORMAT = "yyyyMMdd"; + + public static final String FULL_DATE = "yyyyMMddHHmmss"; + + + /** + * 当天的开始时间 + * + * @return + */ + public static Date startOfTodDayTime() { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTime(); + } + + /** + * 当天的开始时间 + * + * @return + */ + public static Date startOfTodDayTime(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTime(); + } + + /** + * 当天的开始时间 + * + * @return + */ + public static long startOfTodDay() { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + Date date = calendar.getTime(); + return date.getTime() / 1000; + } + + public static void main(String[] args) { + + } + + /** + * 当天的结束时间 + * + * @return + */ + public static Date endOfDate() { + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MILLISECOND, 999); + return calendar.getTime(); + } + + /** + * 当天的结束时间 + * + * @return + */ + public static Date endOfDate(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MILLISECOND, 999); + return calendar.getTime(); + } + + /** + * 某天的年月日 + * + * @param dayUntilNow 距今多少天以前 + * @return 年月日map key为 year month day + */ + public static Map getYearMonthAndDay(int dayUntilNow) { + + Map map = new HashMap(3); + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + calendar.add(Calendar.DATE, -dayUntilNow); + map.put("year", calendar.get(Calendar.YEAR)); + map.put("month", calendar.get(Calendar.MONTH) + 1); + map.put("day", calendar.get(Calendar.DAY_OF_MONTH)); + return map; + } + + /** + * 将一个字符串转换成日期格式 + * + * @param date 字符串日期 + * @param pattern 日期格式 + * @return + */ + public static Date toDate(String date, String pattern) { + if ("".equals("" + date)) { + return null; + } + if (pattern == null) { + pattern = STANDARD_DATE_FORMAT; + } + SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.ENGLISH); + Date newDate = new Date(); + try { + newDate = sdf.parse(date); + + } catch (Exception ex) { + ex.printStackTrace(); + } + return newDate; + } + + /** + * 获取上个月的开始结束时间 + * + * @return + */ + public static Long[] getLastMonth() { + // 取得系统当前时间 + Calendar cal = Calendar.getInstance(); + int year = cal.get(Calendar.YEAR); + int month = cal.get(Calendar.MONTH) + 1; + + // 取得系统当前时间所在月第一天时间对象 + cal.set(Calendar.DAY_OF_MONTH, 1); + + // 日期减一,取得上月最后一天时间对象 + cal.add(Calendar.DAY_OF_MONTH, -1); + + // 输出上月最后一天日期 + int day = cal.get(Calendar.DAY_OF_MONTH); + + String months = ""; + String days = ""; + + if (month > 1) { + month--; + } else { + year--; + month = 12; + } + if (!(String.valueOf(month).length() > 1)) { + months = "0" + month; + } else { + months = String.valueOf(month); + } + if (!(String.valueOf(day).length() > 1)) { + days = "0" + day; + } else { + days = String.valueOf(day); + } + String firstDay = "" + year + "-" + months + "-01"; + String lastDay = "" + year + "-" + months + "-" + days + " 23:59:59"; + + Long[] lastMonth = new Long[2]; + lastMonth[0] = DateUtil.getDateline(firstDay); + lastMonth[1] = DateUtil.getDateline(lastDay, STANDARD_FORMAT); + + return lastMonth; + } + + /** + * 把日期转换成字符串型 + * + * @param date 日期 + * @return + */ + public static String toString(Date date) { + return toString(date,STANDARD_FORMAT); + } + /** + * 把日期转换成字符串型 + * + * @param date 日期 + * @param pattern 类型 + * @return + */ + public static String toString(Date date, String pattern) { + if (date == null) { + return ""; + } + if (pattern == null) { + pattern = STANDARD_DATE_FORMAT; + } + String dateString = ""; + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + try { + dateString = sdf.format(date); + } catch (Exception ex) { + ex.printStackTrace(); + } + return dateString; + } + + /** + * 时间戳转换成时间类型 + * + * @param time 时间戳 + * @param pattern 格式 + * @return + */ + public static String toString(Long time, String pattern) { + if (time > 0) { + if (time.toString().length() == 10) { + time = time * 1000; + } + Date date = new Date(time); + String str = DateUtil.toString(date, pattern); + return str; + } + return ""; + } + + /** + * 判断当前时间是否在某个时间范围 + * + * @param start 开始时间,以秒为单位的时间戳 + * @param end 结束时间,以秒为单位的时间戳 + * @return 是否在范围内 + */ + public static boolean inRangeOf(long start, long end) { + long now = getDateline(); + return start <= now && end >= now; + } + + /** + * 获取指定日期的时间戳 + * + * @param date 指定日期 + * @return 时间戳 + */ + public static long getDateline(String date) { + return toDate(date, STANDARD_DATE_FORMAT).getTime() / 1000; + } + + /** + * 获取当前时间的时间戳 + * + * @return 时间戳 + */ + public static long getDateline() { + return System.currentTimeMillis() / 1000; + } + + /** + * 获取当前时间格式化字符串 + * + * @return 时间戳 + */ + public static String getCurrentDateStr(String format) { + return toString(new Date(), format); + } + + /** + * 获取当前时间格式化字符串 + * + * @return 格式化的时间 + */ + public static String getCurrentDateStr() { + return toString(new Date(), FULL_DATE); + } + + /** + * 根据日期格式及日期获取时间戳 + * + * @param date 日期 + * @param pattern 日期格式 + * @return 时间戳 + */ + public static long getDateline(String date, String pattern) { + return toDate(date, pattern).getTime() / 1000; + } + + /** + * 获取几个月之前的日期时间戳 + * + * @param beforeMonth 几个月之前 + * @return + */ + public static long getBeforeMonthDateline(int beforeMonth) { + SimpleDateFormat format = new SimpleDateFormat(STANDARD_FORMAT); + Calendar c = Calendar.getInstance(); + + //过去一月 + c.setTime(new Date()); + c.add(Calendar.MONTH, (0 - beforeMonth)); + Date m = c.getTime(); + String mon = format.format(m); + return getDateline(mon, STANDARD_FORMAT); + } + + /** + * 获取当前天的结束时间 + * + * @return 当前天的结束时间 + */ + public static Date getCurrentDayEndTime() { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + cal.set(Calendar.DATE, cal.get(Calendar.DATE) + 1); + cal.set(Calendar.SECOND, cal.get(Calendar.SECOND) - 1); + return cal.getTime(); + } + + /** + * 获取延时时间(秒) + * + * @param startTime 开始时间 + * @return 延时时间(秒) + */ + public static Integer getDelayTime(Long startTime) { + int time = Math.toIntExact((startTime - System.currentTimeMillis()) / 1000); + // 如果时间为负数则改为一秒后执行 + if (time <= 0) { + time = 1; + } + return time; + } + + /** + * 获取某年某月开始时间 + * + * @param year 年 + * @param month 月 + * @return 开始时间 + */ + public static Date getBeginTime(int year, int month) { + YearMonth yearMonth = YearMonth.of(year, month); + LocalDate localDate = yearMonth.atDay(1); + LocalDateTime startOfDay = localDate.atStartOfDay(); + ZonedDateTime zonedDateTime = startOfDay.atZone(ZoneId.of("Asia/Shanghai")); + + return Date.from(zonedDateTime.toInstant()); + + } + + /** + * 获取某年某月结束时间 + * + * @param year 年 + * @param month 月 + * @return 结束时间 + */ + public static Date getEndTime(int year, int month) { + YearMonth yearMonth = YearMonth.of(year, month); + LocalDate endOfMonth = yearMonth.atEndOfMonth(); + LocalDateTime localDateTime = endOfMonth.atTime(23, 59, 59, 999); + ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai")); + return Date.from(zonedDateTime.toInstant()); + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/HibernateProxyTypeAdapter.java b/framework/src/main/java/cn/lili/common/utils/HibernateProxyTypeAdapter.java new file mode 100644 index 00000000..8d3976ba --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/HibernateProxyTypeAdapter.java @@ -0,0 +1,56 @@ +package cn.lili.common.utils; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import org.hibernate.Hibernate; +import org.hibernate.proxy.HibernateProxy; + +import java.io.IOException; + +/** + * 代理对象实例化 + * @author Chopper + */ +public class HibernateProxyTypeAdapter extends TypeAdapter { + + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { + + @Override + @SuppressWarnings("unchecked") + public TypeAdapter create(Gson gson, TypeToken type) { + return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter) new HibernateProxyTypeAdapter(gson) : null); + } + }; + private final Gson context; + + private HibernateProxyTypeAdapter(Gson context) { + this.context = context; + } + + @Override + public HibernateProxy read(JsonReader in) throws IOException { + throw new UnsupportedOperationException("Not supported"); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public void write(JsonWriter out, HibernateProxy value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + // Retrieve the original (not proxy) class + Class baseType = Hibernate.getClass(value); + // Get the TypeAdapter of the original class, to delegate the serialization + TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType)); + // Get a filled instance of the original class + Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer() + .getImplementation(); + // Serialize the value + delegate.write(out, unproxiedValue); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/common/utils/HttpClientUtils.java b/framework/src/main/java/cn/lili/common/utils/HttpClientUtils.java new file mode 100644 index 00000000..0042eb45 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/HttpClientUtils.java @@ -0,0 +1,232 @@ +package cn.lili.common.utils; + +import org.apache.http.*; +import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.LayeredConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; + +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLHandshakeException; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class HttpClientUtils { + + // org.apache.http.impl.client.CloseableHttpClient + private static CloseableHttpClient httpClient = null; + + // 这里就直接默认固定了,因为以下三个参数在新建的method中仍然可以重新配置并被覆盖. + static final int connectionRequestTimeout = 30000;// ms毫秒,从池中获取链接超时时间 + static final int connectTimeout = 60000;// ms毫秒,建立链接超时时间 + static final int socketTimeout = 60000;// ms毫秒,读取超时时间 + + // 总配置,主要涉及是以下两个参数,如果要作调整没有用到properties会比较后麻烦,但鉴于一经粘贴,随处可用的特点,就不再做依赖性配置化处理了. + // 而且这个参数同一家公司基本不会变动. + static final int maxTotal = 500;// 最大总并发,很重要的参数 + static final int maxPerRoute = 100;// 每路并发,很重要的参数 + + // 正常情况这里应该配成MAP或LIST + // 细化配置参数,用来对每路参数做精细化处理,可以管控各ip的流量,比如默认配置请求baidu:80端口最大100个并发链接, + static final String detailHostName = "http://www.baidu.com";// 每个细化配置之ip(不重要,在特殊场景很有用) + // 每个细化配置之port(不重要,在特殊场景很有用) + static final int detailPort = 80; + // 每个细化配置之最大并发数(不重要,在特殊场景很有用) + static final int detailMaxPerRoute = 100; + + private static CloseableHttpClient getHttpClient() { + if (null == httpClient) { + synchronized (HttpClientUtils.class) { + if (null == httpClient) { + httpClient = init(); + } + } + } + return httpClient; + } + + /** + * 链接池初始化 这里最重要的一点理解就是. 让CloseableHttpClient 一直活在池的世界里, 但是HttpPost却一直用完就消掉. + * 这样可以让链接一直保持着. + */ + private static CloseableHttpClient init() { + CloseableHttpClient newHotpoint; + + // 设置连接池 + ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory(); + LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory(); + Registry registry = RegistryBuilder.create().register("http", plainsf).register("https", sslsf).build(); + PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry); + // 将最大连接数增加 + cm.setMaxTotal(maxTotal); + // 将每个路由基础的连接增加 + cm.setDefaultMaxPerRoute(maxPerRoute); + + // 细化配置开始,其实这里用Map或List的for循环来配置每个链接,在特殊场景很有用. + // 将每个路由基础的连接做特殊化配置,一般用不着 + HttpHost httpHost = new HttpHost(detailHostName, detailPort); + // 将目标主机的最大连接数增加 + cm.setMaxPerRoute(new HttpRoute(httpHost), detailMaxPerRoute); + // 细化配置结束 + + // 请求重试处理 + HttpRequestRetryHandler httpRequestRetryHandler = (exception, executionCount, context) -> { + if (executionCount >= 2) {// 如果已经重试了2次,就放弃 + return false; + } + if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接,那么就重试 + return true; + } + if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常 + return false; + } + if (exception instanceof InterruptedIOException) {// 超时 + return false; + } + if (exception instanceof UnknownHostException) {// 目标服务器不可达 + return false; + } + if (exception instanceof SSLException) {// SSL握手异常 + return false; + } + + HttpClientContext clientContext = HttpClientContext.adapt(context); + HttpRequest request = clientContext.getRequest(); + // 如果请求是幂等的,就再次尝试 + return !(request instanceof HttpEntityEnclosingRequest); + }; + + // 配置请求的超时设置 + RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout).setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build(); + newHotpoint = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig).setRetryHandler(httpRequestRetryHandler).build(); + return newHotpoint; + } + + public static String doGet(String url, Map param) { + + // httpClient + CloseableHttpClient httpClient = getHttpClient(); + + String resultString = ""; + CloseableHttpResponse response = null; + try { + // 创建uri + URIBuilder builder = new URIBuilder(url); + if (param != null) { + for (String key : param.keySet()) { + builder.addParameter(key, param.get(key)); + } + } + URI uri = builder.build(); + + // 创建http GET请求 + HttpGet httpGet = new HttpGet(uri); + + // 执行请求 + response = httpClient.execute(httpGet); + // 判断返回状态是否为200 + if (response.getStatusLine().getStatusCode() == 200) { + resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); + } else { + System.out.println(response.getStatusLine().getStatusCode()); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (response != null) { + response.close(); + } + httpClient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return resultString; + } + + public static String doPost(String url, Map param) { + // 创建HttpClient对象 + CloseableHttpClient httpClient = getHttpClient(); + CloseableHttpResponse response = null; + String resultString = ""; + try { + // 创建Http Post请求 + HttpPost httpPost = new HttpPost(url); + // 创建参数列表 + if (param != null) { + List paramList = new ArrayList<>(); + for (String key : param.keySet()) { + paramList.add(new BasicNameValuePair(key, param.get(key))); + } + // 模拟表单 + UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, "utf-8"); + httpPost.setEntity(entity); + } + // 执行http请求 + response = httpClient.execute(httpPost); + resultString = EntityUtils.toString(response.getEntity(), "utf-8"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + response.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return resultString; + } + + + public static String doPostJson(String url, String json) { + // 创建HttpClient对象 + CloseableHttpClient httpClient = getHttpClient(); + CloseableHttpResponse response = null; + String resultString = ""; + try { + // 创建Http Post请求 + HttpPost httpPost = new HttpPost(url); + // 创建请求内容 + StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); + httpPost.setEntity(entity); + // 执行http请求 + response = httpClient.execute(httpPost); + resultString = EntityUtils.toString(response.getEntity(), "utf-8"); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + response.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return resultString; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/common/utils/IpHelper.java b/framework/src/main/java/cn/lili/common/utils/IpHelper.java new file mode 100644 index 00000000..793fc28a --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/IpHelper.java @@ -0,0 +1,75 @@ +package cn.lili.common.utils; + + +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.http.HttpUtil; +import cn.lili.modules.connect.util.IpUtils; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; + + +/** + * ip工具 + * + * @author Chopper + */ +@Slf4j +@Component +public class IpHelper { + + //qq lbs 地区查询key + @Value("${lili.lbs.key}") + private String key; + //qq lbs 地区查询key + @Value("${lili.lbs.sk}") + private String sk; + + private static final String api = "https://apis.map.qq.com"; + + + /** + * 获取IP返回地理信息 + * + * @param + * @return + */ + public String getIpCity(HttpServletRequest request) { + + String url = "/ws/location/v1/ip?key=" + key + "&ip=" + IpUtils.getIpAddress(request); + String sign = SecureUtil.md5(url + sk); + url = api + url + "&sign=" + sign; + String result = "未知"; + try { + String json = HttpUtil.get(url, 3000); + JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject(); + String status = jsonObject.get("status").getAsString(); + if ("0".equals(status)) { + JsonObject address = jsonObject.get("result").getAsJsonObject().get("ad_info").getAsJsonObject(); + String nation = address.get("nation").getAsString(); + String province = address.get("province").getAsString(); + String city = address.get("city").getAsString(); + String district = address.get("district").getAsString(); + if (StrUtil.isNotBlank(nation) && StrUtil.isBlank(province)) { + result = nation; + } else { + result = province; + if (StrUtil.isNotBlank(city)) { + result += " " + city; + } + if (StrUtil.isNotBlank(district)) { + result += " " + district; + } + } + } + } catch (Exception e) { + log.info("获取IP地理信息失败"); + } + return result; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/JasyptUtil.java b/framework/src/main/java/cn/lili/common/utils/JasyptUtil.java new file mode 100644 index 00000000..d1b7ced3 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/JasyptUtil.java @@ -0,0 +1,59 @@ +package cn.lili.common.utils; + +import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; +import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; + + +/** + * 加密解密 + * @author Chopper + */ +public class JasyptUtil { + + /** + * Jasypt生成加密结果 + * @param password 配置文件中设定的加密密码 jasypt.encryptor.password + * @param value 待加密值 + * @return + */ + public static String encyptPwd(String password,String value){ + PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); + encryptor.setConfig(cryptor(password)); + String result = encryptor.encrypt(value); + return result; + } + /** + * 解密 + * @param password 配置文件中设定的加密密码 jasypt.encryptor.password + * @param value 待解密密文 + * @return + */ + public static String decyptPwd(String password,String value){ + PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); + encryptor.setConfig(cryptor(password)); + encryptor.decrypt(value); + String result = encryptor.decrypt(value); + return result; + } + + public static SimpleStringPBEConfig cryptor(String password){ + SimpleStringPBEConfig config = new SimpleStringPBEConfig(); + config.setPassword(password); + config.setAlgorithm("PBEWITHHMACSHA512ANDAES_256"); + config.setKeyObtentionIterations("1000"); + config.setPoolSize(1); + config.setProviderName("SunJCE"); + config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); + config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator"); + config.setStringOutputType("base64"); + return config; + } + + public static void main(String[] args){ + + //加密 若修改了第一个参数加密password记得在配置文件同步修改 + System.out.println(encyptPwd("jasypt.encryptor.password","123456")); + //解密 + System.out.println(decyptPwd("jasypt.encryptor.password","PYVnAYh+j5C3jkMV1d+myj6JzDaUk7pcfTWUaYsvQdEVkuvIVf7Y0mOU9XkffxT8")); + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/ObjectUtil.java b/framework/src/main/java/cn/lili/common/utils/ObjectUtil.java new file mode 100644 index 00000000..c025f524 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/ObjectUtil.java @@ -0,0 +1,57 @@ +package cn.lili.common.utils; + +import cn.hutool.core.util.StrUtil; +import com.google.gson.Gson; +import org.springframework.cglib.beans.BeanMap; + +import java.util.HashMap; +import java.util.Map; + +/** + * 对象转换工具 + * @author Chopper + */ +public class ObjectUtil { + + public static String mapToString(Map paramMap){ + + if (paramMap == null) { + return ""; + } + Map params = new HashMap<>(16); + for (Map.Entry param : paramMap.entrySet()) { + + String key = param.getKey(); + String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : ""); + String obj = StrUtil.endWithIgnoreCase(param.getKey(), "password") ? "******" : paramValue; + params.put(key,obj); + } + return new Gson().toJson(params); + } + + public static String mapToStringAll(Map paramMap){ + + if (paramMap == null) { + return ""; + } + Map params = new HashMap<>(16); + for (Map.Entry param : paramMap.entrySet()) { + + String key = param.getKey(); + String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : ""); + params.put(key, paramValue); + } + return new Gson().toJson(params); + } + + public static Map beanToMap(T bean) { + Map map = new HashMap<>(16); + if (bean != null) { + BeanMap beanMap = BeanMap.create(bean); + for (Object key : beanMap.keySet()) { + map.put(key+"", beanMap.get(key)); + } + } + return map; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/OperationalJudgment.java b/framework/src/main/java/cn/lili/common/utils/OperationalJudgment.java new file mode 100644 index 00000000..bd8d02a8 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/OperationalJudgment.java @@ -0,0 +1,58 @@ +package cn.lili.common.utils; + +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; + +/** + * 全局统一判定是否可操作某属性 + * + * @author Chopper + * @version v1.0 + * 2020-08-20 18:07 + */ +public class OperationalJudgment { + + /** + * 需要判定的对象必须包含属性 memberId,storeId 代表判定的角色 + * + * @param object 判定的对象 + * @param + * @return + */ + public static t judgment(t object) { + return judgment(object, "memberId", "storeId"); + } + + /** + * 需要判定的对象必须包含属性 memberId,storeId 代表判定的角色 + * + * @param object + * @param buyerIdField + * @param storeIdField + * @param + * @return 返回判定本身,防止多次查询对象 + */ + public static t judgment(t object, String buyerIdField, String storeIdField) { + AuthUser tokenUser = UserContext.getCurrentUser(); + switch (tokenUser.getRole()) { + case MANAGER: + return object; + case MEMBER: + if (tokenUser.getId().equals(BeanUtil.getFieldValueByName(buyerIdField, object))) { + return object; + } else { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + case STORE: + if (tokenUser.getStoreId().equals(BeanUtil.getFieldValueByName(storeIdField, object))) { + return object; + } else { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + } + return object; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/PageUtil.java b/framework/src/main/java/cn/lili/common/utils/PageUtil.java new file mode 100644 index 00000000..84f87983 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/PageUtil.java @@ -0,0 +1,151 @@ +package cn.lili.common.utils; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.SearchVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 分页工具 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/26 15:23 + */ +public class PageUtil { + + + /** + * Mybatis-Plus分页封装 + * + * @param page + * @return + */ + public static Page initPage(PageVO page) { + + Page p; + int pageNumber = page.getPageNumber(); + int pageSize = page.getPageSize(); + String sort = page.getSort(); + String order = page.getOrder(); + + if (pageNumber < 1) { + pageNumber = 1; + } + if (pageSize < 1) { + pageSize = 10; + } + if (pageSize > 100) { + pageSize = 100; + } + if (StrUtil.isNotBlank(sort)) { + Boolean isAsc = false; + if (StrUtil.isBlank(order)) { + isAsc = false; + } else { + if ("desc".equals(order.toLowerCase())) { + isAsc = false; + } else if ("asc".equals(order.toLowerCase())) { + isAsc = true; + } + } + p = new Page<>(pageNumber, pageSize); + if (isAsc) { + p.addOrder(OrderItem.asc(sort)); + } else { + p.addOrder(OrderItem.desc(sort)); + } + + } else { + p = new Page<>(pageNumber, pageSize); + } + return p; + } + + /** + * 生成条件搜索 全对象对比 equals + * 如果需要like 需要另行处理 + * + * @param object + * @return + */ + public static QueryWrapper initWrapper(Object object) { + return initWrapper(object, null); + } + + /** + * 生成条件搜索 全对象对比 + * + * @param object + * @param searchVo + * @return + */ + public static QueryWrapper initWrapper(Object object, SearchVO searchVo) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + // 创建时间区间判定 + if (searchVo != null && StrUtil.isNotBlank(searchVo.getStartDate()) && StrUtil.isNotBlank(searchVo.getEndDate())) { + Date start = cn.hutool.core.date.DateUtil.parse(searchVo.getStartDate()); + Date end = cn.hutool.core.date.DateUtil.parse(searchVo.getEndDate()); + queryWrapper.between("create_time", start, DateUtil.endOfDay(end)); + } + if (object != null) { + String[] fieldNames = BeanUtil.getFiledName(object); + //遍历所有属性 + for (int j = 0; j < fieldNames.length; j++) { + //获取属性的名字 + String key = fieldNames[j]; + //获取值 + Object value = BeanUtil.getFieldValueByName(key, object); + //如果值不为空才做查询处理 + if (value != null && !"".equals(value)) { + //字段数据库中,驼峰转下划线 + queryWrapper.eq(StringUtils.camel2Underline(key), value); + } + } + } + return queryWrapper; + } + + + /** + * List 手动分页 + * + * @param page + * @param list + * @return + */ + public static List listToPage(PageVO page, List list) { + + int pageNumber = page.getPageNumber() - 1; + int pageSize = page.getPageSize(); + + if (pageNumber < 0) { + pageNumber = 0; + } + if (pageSize < 1) { + pageSize = 10; + } + if (pageSize > 100) { + pageSize = 100; + } + + int fromIndex = pageNumber * pageSize; + int toIndex = pageNumber * pageSize + pageSize; + + if (fromIndex > list.size()) { + return new ArrayList<>(); + } else if (toIndex >= list.size()) { + return list.subList(fromIndex, list.size()); + } else { + return list.subList(fromIndex, toIndex); + } + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/PasswordUtil.java b/framework/src/main/java/cn/lili/common/utils/PasswordUtil.java new file mode 100644 index 00000000..ac534b72 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/PasswordUtil.java @@ -0,0 +1,157 @@ +package cn.lili.common.utils; + +import java.security.Key; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +public class PasswordUtil { + + /** + * JAVA6支持以下任意一种算法 + * PBEWITHMD5ANDDES + * PBEWITHMD5ANDTRIPLEDES + * PBEWITHSHAANDDESEDE + * PBEWITHSHA1ANDRC2_40 + * PBKDF2WITHHMACSHA1 + * */ + + /** + * 定义使用的算法为:PBEWITHMD5andDES算法 + */ + public static final String ALGORITHM = "PBEWithMD5AndDES"; + + /** + * 定义迭代次数为1000次 + */ + private static final int ITERATIONCOUNT = 1000; + + /** + * 获取加密算法中使用的盐值,解密中使用的盐值必须与加密中使用的相同才能完成操作. + * 盐长度必须为8字节 + * + * @return byte[] 盐值 + */ + public static byte[] getSalt() throws Exception { + //实例化安全随机数 + SecureRandom random = new SecureRandom(); + //产出盐 + return random.generateSeed(8); + } + + /** + * 根据PBE密码生成一把密钥 + * + * @param password 生成密钥时所使用的密码 + * @return Key PBE算法密钥 + */ + private static Key getPBEKey(String password) throws Exception { + // 实例化使用的算法 + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM); + // 设置PBE密钥参数 + PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray()); + // 生成密钥 + SecretKey secretKey = keyFactory.generateSecret(keySpec); + + return secretKey; + } + + /** + * 加密明文字符串 + * + * @param plaintext 待加密的明文字符串 + * @param password 生成密钥时所使用的密码 + * @param salt 盐值 + * @return 加密后的密文字符串 + * @throws Exception + */ + public static String encrypt(String plaintext, String password, byte[] salt) throws Exception { + + Key key = getPBEKey(password); + + PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, ITERATIONCOUNT); + + Cipher cipher = Cipher.getInstance(ALGORITHM); + + cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); + + byte encipheredData[] = cipher.doFinal(plaintext.getBytes()); + + return bytesToHexString(encipheredData); + } + + /** + * 解密密文字符串 + * + * @param ciphertext 待解密的密文字符串 + * @param password 生成密钥时所使用的密码(如需解密,该参数需要与加密时使用的一致) + * @param salt 盐值(如需解密,该参数需要与加密时使用的一致) + * @return 解密后的明文字符串 + * @throws Exception + */ + public static String decrypt(String ciphertext, String password, byte[] salt) throws Exception { + + Key key = getPBEKey(password); + + PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, ITERATIONCOUNT); + + Cipher cipher = Cipher.getInstance(ALGORITHM); + + cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); + + byte[] passDec = cipher.doFinal(hexStringToBytes(ciphertext)); + + return new String(passDec); + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param src 字节数组 + * @return + */ + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(""); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + /** + * 将十六进制字符串转换为字节数组 + * + * @param hexString 十六进制字符串 + * @return + */ + public static byte[] hexStringToBytes(String hexString) { + if (hexString == null || hexString.equals("")) { + return null; + } + hexString = hexString.toUpperCase(); + int length = hexString.length() / 2; + char[] hexChars = hexString.toCharArray(); + byte[] d = new byte[length]; + for (int i = 0; i < length; i++) { + int pos = i * 2; + d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); + } + return d; + } + + private static byte charToByte(char c) { + return (byte) "0123456789ABCDEF".indexOf(c); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/common/utils/RedisUtil.java b/framework/src/main/java/cn/lili/common/utils/RedisUtil.java new file mode 100644 index 00000000..26266c28 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/RedisUtil.java @@ -0,0 +1,646 @@ +package cn.lili.common.utils; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.DefaultTypedTuple; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Redis封装工具类 + * + * @author paulG + * @since 2020/11/7 + **/ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RedisUtil { + + private final RedisTemplate redisTemplate; + + //=============================common============================ + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) + * @return + */ + public boolean expire(String key, long time) { + try { + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据key 获取过期时间 + * + * @param key 键 不能为null + * @return 时间(秒) 返回0代表为永久有效 + */ + public long getExpire(String key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 判断key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除缓存 + * + * @param key 可以传一个值 或多个 + */ + @SuppressWarnings("unchecked") + public void del(String... key) { + if (key != null && key.length > 0) { + if (key.length == 1) { + redisTemplate.delete(key[0]); + } else { + redisTemplate.delete(CollectionUtils.arrayToList(key)); + } + } + } + + //============================String============================= + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public T get(String key, Class clazz) { + Object o = key == null ? null : redisTemplate.opsForValue().get(key); + return (T) o; + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + * @return true成功 false失败 + */ + public boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + * @return true成功 false 失败 + */ + public boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 递增 + * + * @param key 键 + * @return + */ + public long incr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递增因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + + /** + * 递减 + * + * @param key 键 + * @return + */ + public long decr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递减因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, -delta); + } + + //================================Map================================= + + /** + * HashGet + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return 值 + */ + public Object hget(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 获取hashKey对应的所有键值 + * + * @param key 键 + * @return 对应的多个键值 + */ + public Map hmget(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + * @return true 成功 false 失败 + */ + public boolean hmset(String key, Map map) { + try { + redisTemplate.opsForHash().putAll(key, map); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * HashSet 并设置时间 + * + * @param key 键 + * @param map 对应多个键值 + * @param time 时间(秒) + * @return true成功 false失败 + */ + public boolean hmset(String key, Map map, long time) { + try { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(key, item, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value, long time) { + try { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除hash表中的值 + * + * @param key 键 不能为null + * @param item 项 可以使多个 不能为null + */ + public void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 判断hash表中是否有该项的值 + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return true 存在 false不存在 + */ + public boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * hash递增 如果不存在,就会创建一个 并把新增后的值返回 + * + * @param key 键 + * @param item 项 + * @param by 要增加几(大于0) + * @return + */ + public double hincr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, by); + } + + /** + * hash递减 + * + * @param key 键 + * @param item 项 + * @param by 要减少记(小于0) + * @return + */ + public double hdecr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, -by); + } + + //============================set============================= + + /** + * 根据key获取Set中的所有值 + * + * @param key 键 + * @return + */ + public Set sGet(String key) { + try { + return redisTemplate.opsForSet().members(key); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 根据value从一个set中查询,是否存在 + * + * @param key 键 + * @param value 值 + * @return true 存在 false不存在 + */ + public boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(key, value); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(key, values); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSetAndTime(String key, long time, Object... values) { + try { + Long count = redisTemplate.opsForSet().add(key, values); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 获取set缓存的长度 + * + * @param key 键 + * @return + */ + public long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 移除值为value的 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 移除的个数 + */ + public long setRemove(String key, Object... values) { + try { + Long count = redisTemplate.opsForSet().remove(key, values); + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + //===============================listByParentId================================= + + /** + * 获取list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束 0 到 -1代表所有值 + * @return + */ + public List lGet(String key, long start, long end) { + try { + return redisTemplate.opsForList().range(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 获取list缓存的长度 + * + * @param key 键 + * @return + */ + public long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 通过索引 获取list中的值 + * + * @param key 键 + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 + * @return + */ + public Object lGetIndex(String key, long index) { + try { + return redisTemplate.opsForList().index(key, index); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, Object value, long time) { + try { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, List value) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lPush(String key, List value) { + try { + redisTemplate.opsForList().leftPushAll(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, List value, long time) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据索引修改list中的某条数据 + * + * @param key 键 + * @param index 索引 + * @param value 值 + * @return + */ + public boolean lUpdateIndex(String key, long index, Object value) { + try { + redisTemplate.opsForList().set(key, index, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 移除N个值为value + * + * @param key 键 + * @param count 移除多少个 + * @param value 值 + * @return 移除的个数 + */ + public long lRemove(String key, long count, Object value) { + try { + Long remove = redisTemplate.opsForList().remove(key, count, value); + return remove; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + //===============================ZSet================================= + + /** + * 向Zset里添加成员 + * + * @param key + * @param score + * @param value + * @return + */ + public boolean zadd(String key, long score, String value) { + Boolean result = redisTemplate.opsForZSet().add(key, value, score); + return result; + + } + + + /** + * 获取 某key 下 某一分值区间的队列 + * + * @param key + * @param from + * @param to + * @return + */ + public Set zrangeByScoreWithScores(String key, int from, long to) { + Set set = redisTemplate.opsForZSet().rangeByScoreWithScores(key, from, to); + return set; + } + + /** + * 移除 Zset队列值 + * + * @param key + * @param value + * @return + */ + public Long zremove(String key, String... value) { + return redisTemplate.opsForZSet().remove(key, value); + + } + +} diff --git a/framework/src/main/java/cn/lili/common/utils/RegularUtil.java b/framework/src/main/java/cn/lili/common/utils/RegularUtil.java new file mode 100644 index 00000000..1915c4db --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/RegularUtil.java @@ -0,0 +1,42 @@ +package cn.lili.common.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 用户名验证工具类 + * @author Chopper + */ +public class RegularUtil { + + + /** + * 手机号 + */ + private static final Pattern mobile = Pattern.compile("^1[3|4|5|8][0-9]\\d{8}$"); + + /** + * 邮箱 + */ + private static final Pattern email = Pattern.compile("^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*\\.[a-zA-Z0-9]{2,6}$"); + + public static boolean Mobile(String v){ + + Matcher m = mobile.matcher(v); + if(m.matches()){ + return true; + } + return false; + } + + public static boolean Email(String v){ + + Matcher m = email.matcher(v); + if(m.matches()){ + return true; + } + return false; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/ResponseUtil.java b/framework/src/main/java/cn/lili/common/utils/ResponseUtil.java new file mode 100644 index 00000000..25febba0 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/ResponseUtil.java @@ -0,0 +1,125 @@ +package cn.lili.common.utils; + +import com.google.gson.Gson; +import lombok.extern.slf4j.Slf4j; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * response 输出响应工具 + * + * @author Chopper + */ +@Slf4j +public class ResponseUtil { + + static final String ENCODING = "UTF-8"; + static final String CONTENT_TYPE = "application/json;charset=UTF-8"; + + /** + * 输出前端内容以及状态指定 + * + * @param response + * @param status + * @param content + */ + public static void output(HttpServletResponse response, Integer status, String content) { + ServletOutputStream servletOutputStream = null; + try { + response.setCharacterEncoding(ENCODING); + response.setContentType(CONTENT_TYPE); + response.setStatus(status); + servletOutputStream = response.getOutputStream(); + servletOutputStream.write(content.getBytes()); + } catch (Exception e) { + log.error("response output error:", e); + } finally { + if (servletOutputStream != null) { + try { + servletOutputStream.flush(); + servletOutputStream.close(); + } catch (IOException e) { + log.error("response output IO close error:", e); + } + } + } + } + + + /** + * response 输出JSON + * + * @param response + * @param status response 状态 + * @param resultMap + */ + public static void output(HttpServletResponse response, Integer status, Map resultMap) { + response.setStatus(status); + output(response, resultMap); + } + + /** + * response 输出JSON + * + * @param response + * @param resultMap + */ + public static void output(HttpServletResponse response, Map resultMap) { + ServletOutputStream servletOutputStream = null; + try { + response.setCharacterEncoding(ENCODING); + response.setContentType(CONTENT_TYPE); + servletOutputStream = response.getOutputStream(); + servletOutputStream.write(new Gson().toJson(resultMap).getBytes()); + } catch (Exception e) { + log.error("response output error:", e); + } finally { + if (servletOutputStream != null) { + try { + servletOutputStream.flush(); + servletOutputStream.close(); + } catch (IOException e) { + log.error("response output IO close error:", e); + } + } + } + } + + /** + * 构造响应 + * + * @param flag + * @param code + * @param msg + * @return + */ + public static Map resultMap(boolean flag, Integer code, String msg) { + return resultMap(flag, code, msg, null); + } + + /** + * 构造响应 + * + * @param flag + * @param code + * @param msg + * @param data + * @return + */ + public static Map resultMap(boolean flag, Integer code, String msg, Object data) { + + Map resultMap = new HashMap(16); + resultMap.put("success", flag); + resultMap.put("message", msg); + resultMap.put("code", code); + resultMap.put("timestamp", System.currentTimeMillis()); + if (data != null) { + resultMap.put("result", data); + } + return resultMap; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/ResultUtil.java b/framework/src/main/java/cn/lili/common/utils/ResultUtil.java new file mode 100644 index 00000000..f08b0b37 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/ResultUtil.java @@ -0,0 +1,114 @@ +package cn.lili.common.utils; + + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.vo.ResultMessage; + +/** + * @author lili + */ +public class ResultUtil { + + /** + * 抽象类,存放结果 + */ + private final ResultMessage resultMessage; + /** + * 正常响应 + */ + private static final Integer SUCCESS = 200; + /** + * 业务异常 + */ + private static final Integer ERROR = 400; + + + /** + * 构造话方法,给响应结果默认值 + */ + public ResultUtil() { + resultMessage = new ResultMessage<>(); + resultMessage.setSuccess(true); + resultMessage.setMessage("success"); + resultMessage.setCode(SUCCESS); + } + + /** + * 返回数据 + * + * @param t + * @return + */ + public ResultMessage setData(T t) { + this.resultMessage.setResult(t); + return this.resultMessage; + } + + /** + * 服务器异常 追加状态码 + * + * @param resultCode 返回码 + */ + public ResultMessage setErrorMsg(ResultCode resultCode) { + this.resultMessage.setSuccess(false); + this.resultMessage.setMessage(resultCode.message()); + this.resultMessage.setCode(resultCode.code()); + return this.resultMessage; + } + + /** + * 服务器异常 追加状态码 + * @param code 状态码 + * @param msg 返回消息 + */ + public ResultMessage setErrorMsg(Integer code, String msg) { + this.resultMessage.setSuccess(false); + this.resultMessage.setMessage(msg); + this.resultMessage.setCode(code); + return this.resultMessage; + } + + + /** + * 返回成功消息 + * + * @param resultCode 返回码 + * @return 返回成功消息 + */ + public ResultMessage setSuccessMsg(ResultCode resultCode) { + this.resultMessage.setSuccess(true); + this.resultMessage.setMessage(resultCode.message()); + this.resultMessage.setCode(resultCode.code()); + return this.resultMessage; + + } + + //抽象静态方法,返回结果集 + public static ResultMessage data(T t) { + return new ResultUtil().setData(t); + } + + /** + * 返回成功 + * @param resultCode 返回状态码 + */ + public static ResultMessage success(ResultCode resultCode) { + return new ResultUtil().setSuccessMsg(resultCode); + } + + /** + * 返回失败 + * @param resultCode 返回状态码 + */ + public static ResultMessage error(ResultCode resultCode) { + return new ResultUtil().setErrorMsg(resultCode); + } + /** + * 返回失败 + * @param code 状态码 + * @param msg 返回消息 + */ + public static ResultMessage error(Integer code, String msg) { + return new ResultUtil().setErrorMsg(code, msg); + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/SnowFlake.java b/framework/src/main/java/cn/lili/common/utils/SnowFlake.java new file mode 100644 index 00000000..d7a788cc --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/SnowFlake.java @@ -0,0 +1,41 @@ +package cn.lili.common.utils; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; + +import java.util.Date; + +/** + * 雪花分布式id获取 + * + * @author Chopper + */ +public class SnowFlake { + + /** + * 机器id + */ + private static long workerId = 0L; + /** + * 机房id + */ + private static long datacenterId = 0L; + + private static Snowflake snowflake = IdUtil.createSnowflake(workerId, datacenterId); + + public static long getId() { + return snowflake.nextId(); + } + + /** + * 生成字符,带有前缀 + * @param prefix + * @return + */ + public static String createStr(String prefix) { + return prefix + DateUtil.toString(new Date(), "yyyyMMdd") + SnowFlake.getId(); + } + public static String getIdStr() { + return snowflake.nextId() + ""; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/SpelUtil.java b/framework/src/main/java/cn/lili/common/utils/SpelUtil.java new file mode 100644 index 00000000..ee76c848 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/SpelUtil.java @@ -0,0 +1,76 @@ +package cn.lili.common.utils; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +/** + * SpelUtil + * + * @author Chopper + * @version v1.0 + * 2021-01-11 10:45 + */ +public class SpelUtil { + + + // spel表达式解析器 + private static SpelExpressionParser spelExpressionParser = new SpelExpressionParser(); + + // 参数名发现器 + private static DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); + + /** + * 转换 jspl参数 + * + * @param joinPoint + * @param spel + * @return + */ + public static String compileParams(JoinPoint joinPoint, String spel) { // Spel表达式解析日志信息 + // 获得方法参数名数组 + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + + String[] parameterNames = parameterNameDiscoverer.getParameterNames(signature.getMethod()); + if (parameterNames != null && parameterNames.length > 0) { + EvaluationContext context = new StandardEvaluationContext(); + + //获取方法参数值 + Object[] args = joinPoint.getArgs(); + for (int i = 0; i < args.length; i++) { + context.setVariable(parameterNames[i], args[i]); // 替换spel里的变量值为实际值, 比如 #user --> user对象 + } + return spelExpressionParser.parseExpression(spel).getValue(context).toString(); + } + return ""; + } + + /** + * 转换 jspl参数 + * + * @param joinPoint + * @param spel + * @return + */ + public static String compileParams(JoinPoint joinPoint, Object rvt, String spel) { // Spel表达式解析日志信息 + // 获得方法参数名数组 + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + + String[] parameterNames = parameterNameDiscoverer.getParameterNames(signature.getMethod()); + if (parameterNames != null && parameterNames.length > 0) { + EvaluationContext context = new StandardEvaluationContext(); + + //获取方法参数值 + Object[] args = joinPoint.getArgs(); + for (int i = 0; i < args.length; i++) { + context.setVariable(parameterNames[i], args[i]); // 替换spel里的变量值为实际值, 比如 #user --> user对象 + } + context.setVariable("rvt", rvt); + return spelExpressionParser.parseExpression(spel).getValue(context).toString(); + } + return ""; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/SpringContextUtil.java b/framework/src/main/java/cn/lili/common/utils/SpringContextUtil.java new file mode 100644 index 00000000..b8a63b4d --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/SpringContextUtil.java @@ -0,0 +1,62 @@ +package cn.lili.common.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * @author Chopper + */ +@Component +public class SpringContextUtil implements ApplicationContextAware { + + /** + * 上下文对象实例 + */ + private static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext appContext) throws BeansException { + applicationContext = appContext; + } + + /** + * 获取applicationContext + * @return spring applicationContext + */ + public static ApplicationContext getApplicationContext() { + return applicationContext; + } + + /** + * 通过name获取 Bean. + * @param name bean的名字 + * @return bean实例 + */ + public static Object getBean(String name){ + return getApplicationContext().getBean(name); + } + + /** + * 通过class获取Bean. + * @param clazz bean的类型 + * @param bean的类型 + * @return bean实例 + */ + public static T getBean(Class clazz){ + return getApplicationContext().getBean(clazz); + } + + /** + * 通过name,以及Clazz返回指定的Bean + * @param name bean的名字 + * @param clazz bean的类型 + * @param bean的类型 + * @return bean实例 + */ + public static T getBean(String name,Class clazz){ + return getApplicationContext().getBean(name, clazz); + } + +} diff --git a/framework/src/main/java/cn/lili/common/utils/StringUtils.java b/framework/src/main/java/cn/lili/common/utils/StringUtils.java new file mode 100644 index 00000000..cbb4547f --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/StringUtils.java @@ -0,0 +1,255 @@ +package cn.lili.common.utils; + +import cn.hutool.core.util.StrUtil; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +/** + * 字串工具类 + * + * @author pikachu + */ +public class StringUtils extends StrUtil { + + /** + * MD5加密方法 + * + * @param str String + * @return String + */ + public static String md5(String str) { + MessageDigest messageDigest = null; + try { + messageDigest = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + ex.printStackTrace(); + return null; + } + byte[] resultByte = messageDigest.digest(str.getBytes()); + StringBuffer result = new StringBuffer(); + for (int i = 0; i < resultByte.length; ++i) { + int v = 0xFF & resultByte[i]; + if (v < 16) { + result.append("0"); + } + result.append(Integer.toHexString(v)); + } + return result.toString(); + } + + /** + * 将object转为数字 + * + * @param obj 需要转object的对象 + * @param checked 如果为true格式不正确抛出异常 + * @return + */ + public static int toInt(Object obj, boolean checked) { + int value = 0; + if (obj == null) { + return 0; + } + try { + value = Integer.parseInt(obj.toString()); + } catch (Exception ex) { + if (checked) { + throw new RuntimeException("整型数字格式不正确"); + } else { + return 0; + } + } + return value; + } + + /** + * 将一个字串转为long,如果无空,则返回默认值 + * + * @param str 要转换的数字字串 + * @param defaultValue 默认值 + * @return + */ + public static Long toLong(String str, Long defaultValue) { + Long value = defaultValue; + if (str == null || "".equals(str)) { + return defaultValue; + } + try { + value = Long.parseLong(str); + } catch (Exception ex) { + return defaultValue; + } + return value; + } + + /** + * 将一个object转为double 如果object 为 null 则返回0; + * + * @param obj 需要转成Double的对象 + * @param checked 如果为true格式不正确抛出异常 + * @return + */ + public static Double toDouble(Object obj, boolean checked) { + Double value = 0d; + if (obj == null) { + if (checked) { + throw new RuntimeException("数字格式不正确"); + } else { + return 0D; + } + } + try { + value = Double.valueOf(obj.toString()); + } catch (Exception ex) { + if (checked) { + throw new RuntimeException("数字格式不正确"); + } else { + return 0D; + } + } + return value; + } + + /** + * 将一个字串转为Double,如果无空,则返回默认值 + * + * @param str 要转换的数字字串 + * @param defaultValue 默认值 + * @return + */ + public static Double toDouble(String str, Double defaultValue) { + Double value = defaultValue; + if (str == null || "".equals(str)) { + return 0d; + } + try { + value = Double.valueOf(str); + } catch (Exception ex) { + ex.printStackTrace(); + value = defaultValue; + } + return value; + } + + /** + * 获取随机数 + * + * @param n 随机次数 + * @return + */ + public static String getRandStr(int n) { + Random random = new Random(); + String sRand = ""; + for (int i = 0; i < n; i++) { + String rand = String.valueOf(random.nextInt(10)); + sRand += rand; + } + return sRand; + } + + + /** + * 判断一个数组是否为空,并且长度大于0 + * + * @param list + * @return true 不空/false 空 + */ + public static boolean isNotEmpty(List list) { + + return list != null && list.size() > 0; + } + + /** + * 切个字符串,如果超出长度则切割 + * + * @param var + * @param size + * @return + */ + public static String subStringLength(String var, Integer size) { + if (var.length() > size) { + return var.substring(0, (size - 4)).toString() + "..."; + } + return var; + } + + /** + * 对象转map + * + * @param obj + * @return + * @throws Exception + */ + public static Map objectToMap(Object obj) throws Exception { + if (obj == null) { + return null; + } + Map map = new HashMap(); + + BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor property : propertyDescriptors) { + String key = property.getName(); + if (key.compareToIgnoreCase("class") == 0) { + continue; + } + Method getter = property.getReadMethod(); + Object value = getter != null ? getter.invoke(obj) : null; + map.put(key, value); + } + + return map; + } + + + /** + * 驼峰法转下划线 + */ + public static String camel2Underline(String str) { + + if (StrUtil.isBlank(str)) { + return ""; + } + if (str.length() == 1) { + return str.toLowerCase(); + } + StringBuffer sb = new StringBuffer(); + for (int i = 1; i < str.length(); i++) { + if (Character.isUpperCase(str.charAt(i))) { + sb.append("_" + Character.toLowerCase(str.charAt(i))); + } else { + sb.append(str.charAt(i)); + } + } + return (str.charAt(0) + sb.toString()).toLowerCase(); + } + + /** + * 如果给定字符串{@code str}中不包含{@code appendStr},则在{@code str}后追加{@code appendStr}; + * 如果已包含{@code appendStr},则在{@code str}后追加{@code otherwise} + * + * @param str 给定的字符串 + * @param appendStr 需要追加的内容 + * @param otherwise 当{@code appendStr}不满足时追加到{@code str}后的内容 + * @return 追加后的字符串 + */ + public static String appendIfNotContain(String str, String appendStr, String otherwise) { + if (isEmpty(str) || isEmpty(appendStr)) { + return str; + } + if (str.contains(appendStr)) { + return str.concat(otherwise); + } + return str.concat(appendStr); + } +} + + diff --git a/framework/src/main/java/cn/lili/common/utils/ThreadPoolUtil.java b/framework/src/main/java/cn/lili/common/utils/ThreadPoolUtil.java new file mode 100644 index 00000000..6867dea8 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/ThreadPoolUtil.java @@ -0,0 +1,77 @@ +package cn.lili.common.utils; + +import java.util.concurrent.*; + +/** + * @author Chopper + */ +public class ThreadPoolUtil { + + /** + * 核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量 + */ + private static final int SIZE_CORE_POOL = 5; + /** + * 线程池维护线程的最大数量 + */ + private static final int SIZE_MAX_POOL = 10; + /** + * 线程池维护线程所允许的空闲时间 + */ + private static final long ALIVE_TIME = 2000; + /** + * 线程缓冲队列 + */ + private static final BlockingQueue bqueue = new ArrayBlockingQueue(100); + private static final ThreadPoolExecutor pool = new ThreadPoolExecutor(SIZE_CORE_POOL, SIZE_MAX_POOL, ALIVE_TIME, TimeUnit.MILLISECONDS, bqueue, new ThreadPoolExecutor.CallerRunsPolicy()); + public static ThreadPoolExecutor threadPool; + + static { + pool.prestartAllCoreThreads(); + } + + /** + * 无返回值直接执行, 管他娘的 + * + * @param runnable + */ + public static void execute(Runnable runnable) { + getThreadPool().execute(runnable); + } + + /** + * 返回值直接执行, 管他娘的 + * + * @param callable + */ + public static Future submit(Callable callable) { + return getThreadPool().submit(callable); + } + + /** + * dcs获取线程池 + * + * @return 线程池对象 + */ + public static ThreadPoolExecutor getThreadPool() { + if (threadPool != null) { + return threadPool; + } else { + synchronized (ThreadPoolUtil.class) { + if (threadPool == null) { + threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool(); + return threadPool; + } + return threadPool; + } + } + } + + public static ThreadPoolExecutor getPool() { + return pool; + } + + public static void main(String[] args) { + System.out.println(pool.getPoolSize()); + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/UrlBuilder.java b/framework/src/main/java/cn/lili/common/utils/UrlBuilder.java new file mode 100644 index 00000000..42b80004 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/UrlBuilder.java @@ -0,0 +1,102 @@ +package cn.lili.common.utils; + +import com.xkcoding.http.util.MapUtil; +import lombok.Setter; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + *

+ * 构造URL + *

+ * + * @author yangkai.shen (https://xkcoding.com) + * @since 1.9.0 + */ +@Setter +public class UrlBuilder { + + private final Map params = new LinkedHashMap<>(7); + private String baseUrl; + + private UrlBuilder() { + + } + + /** + * @param baseUrl 基础路径 + * @return the new {@code UrlBuilder} + */ + public static UrlBuilder fromBaseUrl(String baseUrl) { + UrlBuilder builder = new UrlBuilder(); + builder.setBaseUrl(baseUrl); + return builder; + } + + /** + * 只读的参数Map + * + * @return unmodifiable Map + * @since 1.15.0 + */ + public Map getReadOnlyParams() { + return Collections.unmodifiableMap(params); + } + + /** + * 添加参数 + * + * @param key 参数名称 + * @param value 参数值 + * @return this UrlBuilder + */ + public UrlBuilder queryParam(String key, Object value) { + if (StringUtils.isEmpty(key)) { + throw new RuntimeException("参数名不能为空"); + } + String valueAsString = (value != null ? value.toString() : null); + this.params.put(key, valueAsString); + + return this; + } + + /** + * 添加参数 + * + * @param value 参数值 + * @return this UrlBuilder + */ + public UrlBuilder pathAppend(String value) { + if (StringUtils.isEmpty(value)) { + throw new RuntimeException("参数不能为空"); + } + this.setBaseUrl(this.baseUrl += value); + return this; + } + + /** + * 构造url + * + * @return url + */ + public String build() { + return this.build(false); + } + + /** + * 构造url + * + * @param encode 转码 + * @return url + */ + public String build(boolean encode) { + if (MapUtil.isEmpty(this.params)) { + return this.baseUrl; + } + String baseUrl = StringUtils.appendIfNotContain(this.baseUrl, "?", "&"); + String paramString = MapUtil.parseMapToString(this.params, encode); + return baseUrl + paramString; + } +} diff --git a/framework/src/main/java/cn/lili/common/utils/UserAgentUtils.java b/framework/src/main/java/cn/lili/common/utils/UserAgentUtils.java new file mode 100644 index 00000000..125889f4 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/utils/UserAgentUtils.java @@ -0,0 +1,83 @@ +package cn.lili.common.utils; + + +import eu.bitwalker.useragentutils.DeviceType; +import eu.bitwalker.useragentutils.UserAgent; + +import javax.servlet.http.HttpServletRequest; + +/** + * 客户端类型统计 + * + * @author Chopper + * @version v1.0 + * 2021-02-26 10:53 + */ +public class UserAgentUtils { + + /** + * 获取设备类型 + * + * @param request + * @return + */ + public static DeviceType getDeviceType(HttpServletRequest request) { + return getUserAgent(request).getOperatingSystem().getDeviceType(); + } + + /** + * 是否是PC + * + * @param request + * @return + */ + public static boolean isComputer(HttpServletRequest request) { + return DeviceType.COMPUTER.equals(getDeviceType(request)); + } + + /** + * 是否是手机 + * + * @param request + * @return + */ + public static boolean isMobile(HttpServletRequest request) { + return DeviceType.MOBILE.equals(getDeviceType(request)); + } + + /** + * 是否是平板 + * + * @param request + * @return + */ + public static boolean isTablet(HttpServletRequest request) { + return DeviceType.TABLET.equals(getDeviceType(request)); + } + + /** + * 是否是手机和平板 + * + * @param request + * @return + */ + public static boolean isMobileOrTablet(HttpServletRequest request) { + DeviceType deviceType = getDeviceType(request); + return DeviceType.MOBILE.equals(deviceType) || DeviceType.TABLET.equals(deviceType); + } + + /** + * 获取用户代理对象 + * + * @param request + * @return + */ + public static UserAgent getUserAgent(HttpServletRequest request) { + return UserAgent.parseUserAgentString(request.getHeader("User-Agent")); + } + + +} + + + diff --git a/framework/src/main/java/cn/lili/common/validation/Mobile.java b/framework/src/main/java/cn/lili/common/validation/Mobile.java new file mode 100644 index 00000000..374b37f0 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/validation/Mobile.java @@ -0,0 +1,32 @@ +package cn.lili.common.validation; + +import cn.lili.common.validation.impl.MobileValidator; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 手机号码校验注解 + * + * @author Bulbasaur + */ +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) +@Retention(RUNTIME) +@Documented +@Constraint(validatedBy = {MobileValidator.class}) +public @interface Mobile { + + String regexp() default "1[3|4|5|7|8]\\d{9}"; + + String message() default "手机号码格式不正确"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/framework/src/main/java/cn/lili/common/validation/impl/MobileValidator.java b/framework/src/main/java/cn/lili/common/validation/impl/MobileValidator.java new file mode 100644 index 00000000..add99847 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/validation/impl/MobileValidator.java @@ -0,0 +1,25 @@ +package cn.lili.common.validation.impl; + +import cn.lili.common.validation.Mobile; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class MobileValidator implements ConstraintValidator { + + private static Pattern pattern = Pattern.compile("^0?(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])[0-9]{8}$"); + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + Matcher m = pattern.matcher(value); + return m.matches(); + } + + @Override + public void initialize(Mobile constraintAnnotation) { + + } +} diff --git a/framework/src/main/java/cn/lili/common/verification/ImageUtil.java b/framework/src/main/java/cn/lili/common/verification/ImageUtil.java new file mode 100644 index 00000000..deba251f --- /dev/null +++ b/framework/src/main/java/cn/lili/common/verification/ImageUtil.java @@ -0,0 +1,136 @@ +package cn.lili.common.verification; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; + +/** + * ImageUtil + * + * @author Chopper + * @version v1.0 + * 2020-11-17 14:50 + */ +public class ImageUtil { + + /** + * 添加水印 + * + * @param oriImage + * @param text + * @throws IOException + */ + public static void addWatermark(BufferedImage oriImage, String text) { + Graphics2D graphics2D = oriImage.createGraphics(); + // 设置水印文字颜色 + graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + // 设置水印文字Font + graphics2D.setColor(Color.black); + // 设置水印文字透明度 + graphics2D.setFont(new Font("宋体", Font.BOLD, 30)); + // 第一参数->设置的内容,后面两个参数->文字在图片上的坐标位置(x,y) + graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.4f)); + graphics2D.drawString(text, 10, 40); + graphics2D.dispose(); + } + + + /** + * @param oriImage 原图 + * @param templateImage 模板图 + * @param newImage 新抠出的小图 + * @param x 随机扣取坐标X + * @param y 随机扣取坐标y + */ + public static void cutByTemplate(BufferedImage oriImage, BufferedImage templateImage, BufferedImage newImage, + int x, int y) { + // 临时数组遍历用于高斯模糊存周边像素值 + int[][] matrix = new int[3][3]; + int[] values = new int[9]; + + int xLength = templateImage.getWidth(); + int yLength = templateImage.getHeight(); + // 模板图像宽度 + for (int i = 0; i < xLength; i++) { + // 模板图片高度 + for (int j = 0; j < yLength; j++) { + // 如果模板图像当前像素点不是透明色 copy源文件信息到目标图片中 + int rgb = templateImage.getRGB(i, j); + if (rgb < 0) { + newImage.setRGB(i, j, oriImage.getRGB(x + i, y + j)); + + // 抠图区域高斯模糊 + readPixel(oriImage, x + i, y + j, values); + fillMatrix(matrix, values); + oriImage.setRGB(x + i, y + j, avgMatrix(matrix)); + } + + // 防止数组越界判断 + if (i == (xLength - 1) || j == (yLength - 1)) { + continue; + } + int rightRgb = templateImage.getRGB(i + 1, j); + int downRgb = templateImage.getRGB(i, j + 1); + // 描边处理,,取带像素和无像素的界点,判断该点是不是临界轮廓点,如果是设置该坐标像素是白色 + if ((rgb >= 0 && rightRgb < 0) || (rgb < 0 && rightRgb >= 0) || (rgb >= 0 && downRgb < 0) + || (rgb < 0 && downRgb >= 0)) { + + newImage.setRGB(i, j, Color.GRAY.getRGB()); + +// oriImage.setRGB(x + i, y + j, Color.white.getRGB()); + } + } + } + } + + public static void readPixel(BufferedImage img, int x, int y, int[] pixels) { + int xStart = x - 1; + int yStart = y - 1; + int current = 0; + for (int i = xStart; i < 3 + xStart; i++) + for (int j = yStart; j < 3 + yStart; j++) { + int tx = i; + if (tx < 0) { + tx = -tx; + + } else if (tx >= img.getWidth()) { + tx = x; + } + int ty = j; + if (ty < 0) { + ty = -ty; + } else if (ty >= img.getHeight()) { + ty = y; + } + pixels[current++] = img.getRGB(tx, ty); + + } + } + + public static void fillMatrix(int[][] matrix, int[] values) { + int filled = 0; + for (int[] x : matrix) { + for (int j = 0; j < x.length; j++) { + x[j] = values[filled++]; + } + } + } + + public static int avgMatrix(int[][] matrix) { + int r = 0; + int g = 0; + int b = 0; + for (int[] x : matrix) { + for (int j = 0; j < x.length; j++) { + if (j == 1) { + continue; + } + Color c = new Color(x[j]); + r += c.getRed(); + g += c.getGreen(); + b += c.getBlue(); + } + } + return new Color(r / 8, g / 8, b / 8).getRGB(); + } +} diff --git a/framework/src/main/java/cn/lili/common/verification/SliderImageUtil.java b/framework/src/main/java/cn/lili/common/verification/SliderImageUtil.java new file mode 100644 index 00000000..9dc75798 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/verification/SliderImageUtil.java @@ -0,0 +1,105 @@ +package cn.lili.common.verification; + +import cn.lili.common.utils.Base64DecodeMultipartFile; +import cn.lili.common.vo.SerializableStream; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.util.Base64Utils; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +/** + * 验证码工具 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/17 14:34 + */ +@Component +@Slf4j +public class SliderImageUtil { + + private static final int BOLD = 5; + + private static final String IMG_FILE_TYPE = "jpg"; + + private static final String TEMP_IMG_FILE_TYPE = "png"; + + /** + * 根据模板切图 + * + * @param sliderFile + * @param originalFile + * @return + * @throws Exception sliderFile, originalFile + */ + public static Map pictureTemplatesCut(SerializableStream sliderFile, SerializableStream originalFile) throws Exception { + + Random random = new Random(); + Map pictureMap = new HashMap<>(); + // 拼图 + BufferedImage sliderImage = ImageIO.read(Base64DecodeMultipartFile.base64ToInputStream(sliderFile.getBase64())); + int sliderWidth = sliderImage.getWidth(); + int sliderHeight = sliderImage.getHeight(); + + // 原图 + BufferedImage originalImage = ImageIO.read(Base64DecodeMultipartFile.base64ToInputStream(originalFile.getBase64())); + int originalWidth = originalImage.getWidth(); + int originalHeight = originalImage.getHeight(); + + // 随机生成抠图坐标X,Y + // X轴距离右端targetWidth Y轴距离底部targetHeight以上 + int randomX = random.nextInt(originalWidth - 3 * sliderWidth) + 2 * sliderWidth; + int randomY = random.nextInt(originalHeight - sliderHeight); + log.info("原图大小{} x {},随机生成的坐标 X,Y 为({},{})", originalWidth, originalHeight, randomX, randomY); + + // 新建一个和模板一样大小的图像,TYPE_4BYTE_ABGR表示具有8位RGBA颜色分量的图像,正常取imageTemplate.getType() + BufferedImage newImage = new BufferedImage(sliderWidth, sliderHeight, sliderImage.getType()); + // 得到画笔对象 + Graphics2D graphics = newImage.createGraphics(); + // 如果需要生成RGB格式,需要做如下配置,Transparency 设置透明 + newImage = graphics.getDeviceConfiguration().createCompatibleImage(sliderWidth, sliderHeight, + Transparency.TRANSLUCENT); + + // 新建的图像根据模板颜色赋值,源图生成遮罩 + ImageUtil.cutByTemplate(originalImage, sliderImage, newImage, randomX, randomY); + + // 设置“抗锯齿”的属性 + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics.setStroke(new BasicStroke(BOLD, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL)); + graphics.drawImage(newImage, 0, 0, null); + graphics.dispose(); + + //添加水印 + ImageUtil.addWatermark(originalImage, "请滑动拼图"); + ByteArrayOutputStream newImageOs = new ByteArrayOutputStream();// 新建流。 + ImageIO.write(newImage, TEMP_IMG_FILE_TYPE, newImageOs);// 利用ImageIO类提供的write方法,将bi以png图片的数据模式写入流。 + byte[] newImagery = newImageOs.toByteArray(); + + ByteArrayOutputStream oriImagesOs = new ByteArrayOutputStream();// 新建流。 + ImageIO.write(originalImage, IMG_FILE_TYPE, oriImagesOs);// 利用ImageIO类提供的write方法,将bi以jpg图片的数据模式写入流。 + byte[] oriImageByte = oriImagesOs.toByteArray(); + + pictureMap.put("slidingImage", "data:image/png;base64," + Base64Utils.encodeToString(newImagery)); + pictureMap.put("backImage", "data:image/png;base64," + Base64Utils.encodeToString(oriImageByte)); +// x轴 + pictureMap.put("randomX", randomX); +// y轴 + pictureMap.put("randomY", randomY); + + pictureMap.put("originalHeight", originalHeight); + pictureMap.put("originalWidth", originalWidth); + pictureMap.put("sliderHeight", sliderHeight); + pictureMap.put("sliderWidth", sliderWidth); + return pictureMap; + } + + +} diff --git a/framework/src/main/java/cn/lili/common/verification/VerificationSDK.java b/framework/src/main/java/cn/lili/common/verification/VerificationSDK.java new file mode 100644 index 00000000..16591687 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/verification/VerificationSDK.java @@ -0,0 +1,36 @@ +package cn.lili.common.verification; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 验证码sdk + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/17 15:43 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class VerificationSDK { + + private final Cache cache; + + /** + * 生成一个token 用于获取校验中心的验证码逻辑 + * + * @return + */ + public boolean checked(String verificationKey, String uuid) { + //生成校验KEY,在验证码服务做记录 + String key = CachePrefix.VERIFICATION_KEY.getPrefix() + verificationKey; + cache.get(key); + return true; + } + + +} diff --git a/framework/src/main/java/cn/lili/common/verification/aop/VerificationInterceptor.java b/framework/src/main/java/cn/lili/common/verification/aop/VerificationInterceptor.java new file mode 100644 index 00000000..df20b738 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/verification/aop/VerificationInterceptor.java @@ -0,0 +1,46 @@ +package cn.lili.common.verification.aop; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.verification.aop.annotation.Verification; +import cn.lili.common.verification.enums.VerificationEnums; +import cn.lili.common.verification.service.VerificationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import java.lang.reflect.Method; + +/** + * 验证码验证拦截 + * + * @author Chopper + */ +@Aspect +@Configuration +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class VerificationInterceptor { + + + private final VerificationService verificationService; + + @Before("@annotation(cn.lili.common.verification.aop.annotation.Verification)") + public void interceptor(JoinPoint pjp) { + MethodSignature signature = (MethodSignature) pjp.getSignature(); + Method method = signature.getMethod(); + Verification verificationAnnotation = method.getAnnotation(Verification.class); + VerificationEnums verificationEnums = verificationAnnotation.type(); + String uuid = verificationAnnotation.uuid(); + boolean result = verificationService.check(uuid, verificationEnums); + if (result) { + return; + } + throw new ServiceException(ResultCode.VERIFICATION_ERROR); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/common/verification/aop/annotation/Verification.java b/framework/src/main/java/cn/lili/common/verification/aop/annotation/Verification.java new file mode 100644 index 00000000..74af5437 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/verification/aop/annotation/Verification.java @@ -0,0 +1,32 @@ +package cn.lili.common.verification.aop.annotation; + + +import cn.lili.common.verification.enums.VerificationEnums; + +import java.lang.annotation.*; + +/** + * 限流注解 + * + * @author Chopper + * @since 2018-02-05 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface Verification { + /** + * uuid + * + * @return String + */ + String uuid(); + + /** + * 验证类型 + * + * @return + */ + VerificationEnums type(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/common/verification/enums/VerificationEnums.java b/framework/src/main/java/cn/lili/common/verification/enums/VerificationEnums.java new file mode 100644 index 00000000..4cb72339 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/verification/enums/VerificationEnums.java @@ -0,0 +1,26 @@ +package cn.lili.common.verification.enums; + +/** + * VerificationEnums + * + * @author Chopper + * @version v1.0 + * 2020-12-22 19:11 + */ +public enum VerificationEnums { + + /** + * 登录 + * 注册 + * 找回用户 + * 修改密码 + * 支付钱包密码 + */ + LOGIN, + REGISTER, + FIND_USER, + UPDATE_PASSWORD, + WALLET_PASSWORD; +} + + diff --git a/framework/src/main/java/cn/lili/common/verification/service/VerificationService.java b/framework/src/main/java/cn/lili/common/verification/service/VerificationService.java new file mode 100644 index 00000000..6c54ced0 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/verification/service/VerificationService.java @@ -0,0 +1,37 @@ +package cn.lili.common.verification.service; + +import cn.lili.common.verification.enums.VerificationEnums; + +import java.io.IOException; +import java.util.Map; + +/** + * 验证码模块 + */ +public interface VerificationService { + /** + * 获取校验对象 + * + * @param verificationEnums 校验枚举 + * @param uuid uuid + * @return 校验对象 + */ + Map createVerification(VerificationEnums verificationEnums, String uuid) throws IOException; + + /** + * 预校验 + * + * @param xPos 位移距离 + * @param uuid 用户唯一表示 + * @param verificationEnums 校验枚举 + * @return + */ + boolean preCheck(Integer xPos, String uuid, VerificationEnums verificationEnums); + + /** + * @param uuid 用户唯一表示 + * @param verificationEnums 校验枚举 + * @return + */ + boolean check(String uuid, VerificationEnums verificationEnums); +} diff --git a/framework/src/main/java/cn/lili/common/verification/service/impl/VerificationServiceImpl.java b/framework/src/main/java/cn/lili/common/verification/service/impl/VerificationServiceImpl.java new file mode 100644 index 00000000..469bbaa5 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/verification/service/impl/VerificationServiceImpl.java @@ -0,0 +1,178 @@ +package cn.lili.common.verification.service.impl; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.verification.SliderImageUtil; +import cn.lili.common.verification.enums.VerificationEnums; +import cn.lili.common.verification.service.VerificationService; +import cn.lili.common.vo.SerializableStream; +import cn.lili.modules.base.entity.dos.VerificationSource; +import cn.lili.modules.base.entity.vo.VerificationVO; +import cn.lili.modules.base.service.VerificationSourceService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Random; + +/** + * 认证处理类 + * + * @author Chopper + * @version v1.0 + * 2020-11-17 14:59 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class VerificationServiceImpl implements VerificationService { + + + private final VerificationSourceService verificationSourceService; + + private final Cache cache; + + /** + * 创建校验 + * + * @return 验证码参数 + */ + @Override + public Map createVerification(VerificationEnums verificationEnums, String uuid) throws IOException { + + if (uuid == null) { + throw new ServiceException("非法请求,请重新刷新页面操作"); + } + + //获取验证码配置 + VerificationVO verificationVO = verificationSourceService.getVerificationCache(); + + List verificationResources = verificationVO.getVerificationResources(); + List verificationSlider = verificationVO.getVerificationSlider(); + + + Random random = new Random(); + // 随机选择需要切的图下标 + int resourceNum = random.nextInt(verificationResources.size()); + // 随机选择剪切模版下标 + int sliderNum = random.nextInt(verificationSlider.size()); + + // 随机选择需要切的图片地址 + String originalResource = verificationResources.get(resourceNum).getResource(); + // 随机选择剪切模版图片地址 + String sliderResource = verificationSlider.get(sliderNum).getResource(); + + try { + //获取缓存中的资源 + SerializableStream originalFile = getInputStream(originalResource); + SerializableStream sliderFile = getInputStream(sliderResource); + Map resultMap = SliderImageUtil.pictureTemplatesCut(sliderFile, originalFile); + // 生成验证参数 120可以验证 无需手动清除,120秒有效时间自动清除 + cache.put(cacheKey(verificationEnums, uuid), resultMap.get("randomX"), 120L); + resultMap.put("key", cacheKey(verificationEnums, uuid)); + // 移除横坐标移动距离 + resultMap.remove("randomX"); + return resultMap; + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 根据网络地址,获取源文件 + * 这里简单说一下,这里是将不可序列化的inputstream序列化对象,存入redis缓存 + * @param originalResource + * @return + */ + private SerializableStream getInputStream(String originalResource) throws Exception { + + Object object = cache.get(CachePrefix.VERIFICATION_IMAGE.getPrefix() + originalResource); + if (object != null) { + return (SerializableStream) object; + } + if (StringUtils.isNotEmpty(originalResource)) { + URL url = new URL(originalResource); + InputStream inputStream = url.openStream(); + SerializableStream serializableStream = new SerializableStream(inputStream); + cache.put(CachePrefix.VERIFICATION_IMAGE.getPrefix() + originalResource, serializableStream); + return serializableStream; + } + return null; + } + + /** + * 预校验图片 用于前端回显 + * + * @param xPos X轴移动距离 + * @param verificationEnums 验证key + * @return 验证是否成功 + */ + @Override + public boolean preCheck(Integer xPos, String uuid, VerificationEnums verificationEnums) { + Integer randomX = (Integer) cache.get(cacheKey(verificationEnums, uuid)); + if (randomX == null) { + return false; + } + log.debug("{}{}", randomX, xPos); + //验证结果 + boolean result = Math.abs(randomX - xPos) < 3; + if (result) { + //验证成功,则记录验证结果 验证有效时间,120秒 + cache.put(cacheResult(verificationEnums, uuid), true, 120L); + return result; + } + return false; + } + + /** + * 验证码校验 + * + * @param uuid 用户标识 + * @param verificationEnums 验证key + * @return 验证是否成功 + */ + @Override + public boolean check(String uuid, VerificationEnums verificationEnums) { + Object object = cache.get(cacheResult(verificationEnums, uuid)); + if (object == null) { + return false; + } else { + cache.remove(cacheResult(verificationEnums, uuid)); + return true; + } + } + + /** + * 生成缓存key 记录缓存需要验证的内容 + * + * @param verificationEnums 验证码枚举 + * @param uuid 用户uuid + * @return 缓存key + */ + public static String cacheKey(VerificationEnums verificationEnums, String uuid) { + return CachePrefix.VERIFICATION_KEY.getPrefix() + verificationEnums.name() + uuid; + } + + /** + * 生成缓存key 记录缓存验证的结果 + * + * @param verificationEnums 验证码枚举 + * @param uuid 用户uuid + * @return 缓存key + */ + public static String cacheResult(VerificationEnums verificationEnums, String uuid) { + return CachePrefix.VERIFICATION_RESULT.getPrefix() + verificationEnums.name() + uuid; + } + +} diff --git a/framework/src/main/java/cn/lili/common/vo/PageVO.java b/framework/src/main/java/cn/lili/common/vo/PageVO.java new file mode 100644 index 00000000..cc379fae --- /dev/null +++ b/framework/src/main/java/cn/lili/common/vo/PageVO.java @@ -0,0 +1,55 @@ +package cn.lili.common.vo; + +import cn.lili.common.utils.StringUtils; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 查询参数 + * + * @author Chopper + * @date 2020/11/26 14:43 + */ +@Data +public class PageVO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "页号") + private Integer pageNumber = 1; + + @ApiModelProperty(value = "页面大小") + private Integer pageSize = 10; + + @ApiModelProperty(value = "排序字段") + private String sort; + + @ApiModelProperty(value = "排序方式 asc/desc") + private String order; + + @ApiModelProperty(value = "需要驼峰转换蛇形", notes = "一般不做处理,如果数据库中就是蛇形,则这块需要处理。") + private Boolean notConvert; + + public Integer getMongoPageNumber() { + int i = pageNumber - 1; + if (i < 0) { + return pageNumber; + } else { + return i; + } + } + + public String getSort() { + if (!StringUtils.isEmpty(sort)) { + if (notConvert == null || Boolean.FALSE.equals(notConvert)) { + return StringUtils.camel2Underline(sort); + } else { + return sort; + } + } + return sort; + } + +} diff --git a/framework/src/main/java/cn/lili/common/vo/ResultMessage.java b/framework/src/main/java/cn/lili/common/vo/ResultMessage.java new file mode 100644 index 00000000..6990ab9f --- /dev/null +++ b/framework/src/main/java/cn/lili/common/vo/ResultMessage.java @@ -0,0 +1,43 @@ +package cn.lili.common.vo; + + +import lombok.Data; + +import java.io.Serializable; + +/** + * 前后端交互VO + * + * @author Chopper + * @date 2020/11/26 14:40 + */ +@Data +public class ResultMessage implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 成功标志 + */ + private boolean success; + + /** + * 消息 + */ + private String message; + + /** + * 返回代码 + */ + private Integer code; + + /** + * 时间戳 + */ + private long timestamp = System.currentTimeMillis(); + + /** + * 结果对象 + */ + private T result; +} diff --git a/framework/src/main/java/cn/lili/common/vo/SearchVO.java b/framework/src/main/java/cn/lili/common/vo/SearchVO.java new file mode 100644 index 00000000..9664098c --- /dev/null +++ b/framework/src/main/java/cn/lili/common/vo/SearchVO.java @@ -0,0 +1,51 @@ +package cn.lili.common.vo; + +import cn.lili.common.utils.DateUtil; +import com.alipay.api.internal.util.StringUtils; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; + +/** + * 日期搜索参数 + * + * @author Chopper + * @date 2020/11/26 14:43 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SearchVO implements Serializable { + + @ApiModelProperty(value = "起始日期") + private String startDate; + + @ApiModelProperty(value = "结束日期") + private String endDate; + + public Date getConvertStartDate() { + if (StringUtils.isEmpty(startDate)) { + return null; + } + Date date = DateUtil.toDate(startDate, DateUtil.STANDARD_DATE_FORMAT); + return date; + } + + public Date getConvertEndDate() { + if (StringUtils.isEmpty(endDate)) { + return null; + } + //结束时间等于结束日期+1天 -1秒, + Date date = DateUtil.toDate(endDate, DateUtil.STANDARD_DATE_FORMAT); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) + 1); + calendar.set(Calendar.SECOND, -1); + return calendar.getTime(); + } +} diff --git a/framework/src/main/java/cn/lili/common/vo/SerializableStream.java b/framework/src/main/java/cn/lili/common/vo/SerializableStream.java new file mode 100644 index 00000000..ddeb0cc9 --- /dev/null +++ b/framework/src/main/java/cn/lili/common/vo/SerializableStream.java @@ -0,0 +1,25 @@ +package cn.lili.common.vo; + +import cn.lili.common.utils.Base64DecodeMultipartFile; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.InputStream; + +/** + * 序列化的input stream + * + * @author Chopper + * @date 2021-03-25 16:32 + */ +@Data +@NoArgsConstructor +public class SerializableStream { + private String base64; + + public SerializableStream(InputStream inputStream) { + this.base64 = Base64DecodeMultipartFile.inputStreamToStream(inputStream); + } + +} + diff --git a/framework/src/main/java/cn/lili/config/cache/FastJsonRedisSerializer.java b/framework/src/main/java/cn/lili/config/cache/FastJsonRedisSerializer.java new file mode 100644 index 00000000..a7f741d3 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/cache/FastJsonRedisSerializer.java @@ -0,0 +1,38 @@ +package cn.lili.config.cache; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; + +import java.nio.charset.Charset; + +/* + *要实现对象的缓存,定义自己的序列化和反序列化器。使用阿里的fastjson来实现的比较多。 + */ +public class FastJsonRedisSerializer implements RedisSerializer { + private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + private Class clazz; + + public FastJsonRedisSerializer(Class clazz) { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException { + if (null == t) { + return new byte[0]; + } + return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException { + if (null == bytes || bytes.length <= 0) { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + return (T) JSON.parseObject(str, clazz); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/config/cache/RedisConfig.java b/framework/src/main/java/cn/lili/config/cache/RedisConfig.java new file mode 100644 index 00000000..ffcd587e --- /dev/null +++ b/framework/src/main/java/cn/lili/config/cache/RedisConfig.java @@ -0,0 +1,150 @@ +package cn.lili.config.cache; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.parser.ParserConfig; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.interceptor.CacheErrorHandler; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +/** + * 自定义redis配置 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2021/3/20 09:37 + */ + +@Slf4j +@Configuration +@ConditionalOnClass(RedisOperations.class) +@EnableConfigurationProperties(RedisProperties.class) +public class RedisConfig extends CachingConfigurerSupport { + + + @Value("${lili.cache.timeout:7200}") + private Integer timeout; + + + @Bean + @Primary//当有多个管理器的时候,必须使用该注解在一个管理器上注释:表示该管理器为默认的管理器 + public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { + //初始化一个RedisCacheWriter + RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); + //序列化方式2 + FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); + RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer); + RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair); + //设置过期时间 + defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofSeconds(timeout)); + RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig); + + //设置白名单---非常重要******** + /* + 使用fastjson的时候:序列化时将class信息写入,反解析的时候, + fastjson默认情况下会开启autoType的检查,相当于一个白名单检查, + 如果序列化信息中的类路径不在autoType中, + 反解析就会报com.alibaba.fastjson.JSONException: autoType is not support的异常 + 可参考 https://blog.csdn.net/u012240455/article/details/80538540 + */ + ParserConfig.getGlobalInstance().addAccept("cn.lili."); + + return cacheManager; + } + + @Bean(name = "redisTemplate") + @ConditionalOnMissingBean(name = "redisTemplate") + public RedisTemplate redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + //使用fastjson序列化 + FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); + // value值的序列化采用fastJsonRedisSerializer + template.setValueSerializer(fastJsonRedisSerializer); + template.setHashValueSerializer(fastJsonRedisSerializer); + // key的序列化采用StringRedisSerializer + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setConnectionFactory(lettuceConnectionFactory); + return template; + } + + /** + * 自定义缓存key生成策略,默认将使用该策略 + */ + @Bean + @Override + public KeyGenerator keyGenerator() { + return (target, method, params) -> { + Map container = new HashMap<>(3); + Class targetClassClass = target.getClass(); + // 类地址 + container.put("class", targetClassClass.toGenericString()); + // 方法名称 + container.put("methodName", method.getName()); + // 包名称 + container.put("package", targetClassClass.getPackage()); + // 参数列表 + for (int i = 0; i < params.length; i++) { + container.put(String.valueOf(i), params[i]); + } + // 转为JSON字符串 + String jsonString = JSON.toJSONString(container); + // 做SHA256 Hash计算,得到一个SHA256摘要作为Key + return DigestUtils.sha256Hex(jsonString); + }; + } + + @Bean + @Override + public CacheErrorHandler errorHandler() { + // 异常处理,当Redis发生异常时,打印日志,但是程序正常走 + log.info("初始化 -> [{}]", "Redis CacheErrorHandler"); + return new CacheErrorHandler() { + @Override + public void handleCacheGetError(RuntimeException e, Cache cache, Object key) { + log.error("Redis occur handleCacheGetError:key -> [{}]", key, e); + } + + @Override + public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) { + log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e); + } + + @Override + public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) { + log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e); + } + + @Override + public void handleCacheClearError(RuntimeException e, Cache cache) { + log.error("Redis occur handleCacheClearError:", e); + } + }; + } + +} diff --git a/framework/src/main/java/cn/lili/config/context/ContextConfiguration.java b/framework/src/main/java/cn/lili/config/context/ContextConfiguration.java new file mode 100644 index 00000000..ed5d42ee --- /dev/null +++ b/framework/src/main/java/cn/lili/config/context/ContextConfiguration.java @@ -0,0 +1,19 @@ +package cn.lili.config.context; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 过滤路径 + * @author Chopper + */ +@Configuration +public class ContextConfiguration implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new ThreadContextHolderInterceptorAdapter()).addPathPatterns("/**"); + } + +} diff --git a/framework/src/main/java/cn/lili/config/context/ThreadContextHolder.java b/framework/src/main/java/cn/lili/config/context/ThreadContextHolder.java new file mode 100644 index 00000000..60bcc797 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/context/ThreadContextHolder.java @@ -0,0 +1,40 @@ +package cn.lili.config.context; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 用户上下文 + * @author paulG + * @since 2020/10/16 + **/ +public class ThreadContextHolder { + + private static final ThreadLocal REQUEST_THREAD_LOCAL_HOLDER = new ThreadLocal<>(); + private static final ThreadLocal RESPONSE_THREAD_LOCAL_HOLDER = new ThreadLocal<>(); + + public static void remove() { + REQUEST_THREAD_LOCAL_HOLDER.remove(); + RESPONSE_THREAD_LOCAL_HOLDER.remove(); + } + + public static HttpServletResponse getHttpResponse() { + + return RESPONSE_THREAD_LOCAL_HOLDER.get(); + } + + public static void setHttpResponse(HttpServletResponse response) { + RESPONSE_THREAD_LOCAL_HOLDER.set(response); + } + + public static HttpServletRequest getHttpRequest() { + return REQUEST_THREAD_LOCAL_HOLDER.get(); + } + + public static void setHttpRequest(HttpServletRequest request) { + + REQUEST_THREAD_LOCAL_HOLDER.set(request); + } + + +} diff --git a/framework/src/main/java/cn/lili/config/context/ThreadContextHolderInterceptorAdapter.java b/framework/src/main/java/cn/lili/config/context/ThreadContextHolderInterceptorAdapter.java new file mode 100644 index 00000000..a8cb28e1 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/context/ThreadContextHolderInterceptorAdapter.java @@ -0,0 +1,56 @@ +package cn.lili.config.context; + +import org.springframework.lang.Nullable; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +/** + * request response 填充 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/12/9 10:44 + */ +public class ThreadContextHolderInterceptorAdapter extends HandlerInterceptorAdapter { + + + /** + * 拦截request和response并放到上下文中 + * + * @param request + * @param response + * @param handler + * @return + * @throws Exception + */ + @Override + public boolean preHandle(HttpServletRequest request, + HttpServletResponse response, Object handler) throws Exception { + + ThreadContextHolder.setHttpResponse(response); + ThreadContextHolder.setHttpRequest(request); + + return super.preHandle(request, response, handler); + } + + + /** + * 从上下文中移除 request 和response + * + * @param request + * @param response + * @param handler + * @param ex + * @throws Exception + */ + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { + ThreadContextHolder.remove(); + + super.afterCompletion(request, response, handler, ex); + } +} diff --git a/framework/src/main/java/cn/lili/config/elasticsearch/ElasticsearchConfig.java b/framework/src/main/java/cn/lili/config/elasticsearch/ElasticsearchConfig.java new file mode 100644 index 00000000..86212804 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/elasticsearch/ElasticsearchConfig.java @@ -0,0 +1,103 @@ +package cn.lili.config.elasticsearch; + +import lombok.RequiredArgsConstructor; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.conn.ConnectionKeepAliveStrategy; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.reactor.IOReactorConfig; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.elasticsearch.client.RestHighLevelClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration; + +import javax.annotation.Nonnull; +import javax.annotation.PreDestroy; +import java.io.IOException; +import java.util.List; + +/** + * elasticsearch 配置 + * + * @author paulG + * @since 2020/10/13 + **/ +@Configuration +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class ElasticsearchConfig extends AbstractElasticsearchConfiguration { + + private final ElasticsearchProperties elasticsearchProperties; + + private RestHighLevelClient client; + + @Override + @Bean + @Nonnull + public RestHighLevelClient elasticsearchClient() { + RestClientBuilder restBuilder = RestClient + .builder(this.getHttpHosts()); + restBuilder.setHttpClientConfigCallback(httpClientBuilder -> + httpClientBuilder + .setKeepAliveStrategy(getConnectionKeepAliveStrategy()) + .setMaxConnPerRoute(10). + setDefaultIOReactorConfig(IOReactorConfig.custom().setIoThreadCount(1).build())); + String username = elasticsearchProperties.getAccount().getUsername(); + String password = elasticsearchProperties.getAccount().getPassword(); + if (username != null && password != null) { + final CredentialsProvider credential = new BasicCredentialsProvider(); + credential.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password)); + restBuilder.setHttpClientConfigCallback(httpClientBuilder -> + httpClientBuilder + .setDefaultCredentialsProvider(credential) + .setKeepAliveStrategy(getConnectionKeepAliveStrategy()) + .setMaxConnPerRoute(10) + .setDefaultIOReactorConfig(IOReactorConfig.custom().setIoThreadCount(1).build())); + } + + restBuilder.setRequestConfigCallback(requestConfigBuilder -> + requestConfigBuilder.setConnectTimeout(1000) // time until a connection with the server is established. + .setSocketTimeout(12 * 1000) // time of inactivity to wait for packets[data] to receive. + .setConnectionRequestTimeout(2 * 1000)); // time to fetch a connection from the connection pool 0 for infinite. + + client = new RestHighLevelClient(restBuilder); + return client; + } + + private HttpHost[] getHttpHosts() { + List clusterNodes = elasticsearchProperties.getClusterNodes(); + HttpHost[] httpHosts = new HttpHost[clusterNodes.size()]; + for (int i = 0; i < clusterNodes.size(); i++) { + String[] node = clusterNodes.get(i).split(":"); + httpHosts[i] = new HttpHost(node[0], Integer.parseInt(node[1]), elasticsearchProperties.getSchema()); + } + return httpHosts; + } + + private ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() { + return (response, context) -> 2 * 60 * 1000; + } + + /* + * it gets called when bean instance is getting removed from the context if + * scope is not a prototype + */ + /* + * If there is a method named shutdown or close then spring container will try + * to automatically configure them as callback methods when bean is being + * destroyed + */ + @PreDestroy + public void clientClose() { + try { + this.client.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/framework/src/main/java/cn/lili/config/elasticsearch/ElasticsearchProperties.java b/framework/src/main/java/cn/lili/config/elasticsearch/ElasticsearchProperties.java new file mode 100644 index 00000000..b6e66fbf --- /dev/null +++ b/framework/src/main/java/cn/lili/config/elasticsearch/ElasticsearchProperties.java @@ -0,0 +1,119 @@ +package cn.lili.config.elasticsearch; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; + +/** + * @author paulG + * @since 2020/10/13 + **/ +@Data +@Builder +@Component +@NoArgsConstructor +@AllArgsConstructor +@ConfigurationProperties(prefix = "lili.data.elasticsearch") +public class ElasticsearchProperties { + + /** + * 请求协议 + */ + private String schema = "https"; + + /** + * 集群名称 + */ + private String clusterName = "elasticsearch"; + + /** + * 集群节点 + */ + @NotNull(message = "集群节点不允许为空") + private List clusterNodes = new ArrayList<>(); + + /** + * 索引前缀 + */ + private String indexPrefix; + + /** + * 连接超时时间(毫秒) + */ + private Integer connectTimeout = 1000; + + /** + * socket 超时时间 + */ + private Integer socketTimeout = 30000; + + /** + * 连接请求超时时间 + */ + private Integer connectionRequestTimeout = 500; + + /** + * 每个路由的最大连接数量 + */ + private Integer maxConnectPerRoute = 10; + + /** + * 最大连接总数量 + */ + private Integer maxConnectTotal = 30; + + /** + * 索引配置信息 + */ + private Index index = new Index(); + + /** + * 认证账户 + */ + private Account account = new Account(); + + /** + * 索引配置信息 + */ + @Data + public static class Index { + + /** + * 分片数量 + */ + private Integer numberOfShards = 3; + + /** + * 副本数量 + */ + private Integer numberOfReplicas = 2; + + } + + /** + * 认证账户 + */ + @Data + public static class Account { + + /** + * 认证用户 + */ + private String username; + + /** + * 认证密码 + */ + private String password; + + } + + +} diff --git a/framework/src/main/java/cn/lili/config/interceptor/RequestInterceptorAdapter.java b/framework/src/main/java/cn/lili/config/interceptor/RequestInterceptorAdapter.java new file mode 100644 index 00000000..17e7f37a --- /dev/null +++ b/framework/src/main/java/cn/lili/config/interceptor/RequestInterceptorAdapter.java @@ -0,0 +1,45 @@ +package cn.lili.config.interceptor; + +import cn.lili.config.context.ThreadContextHolder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 写入request/response + * + * @author Chopper + * @version v1.0 + * @since 2020-06-13 13:38 + */ +@Slf4j +@Component +public class RequestInterceptorAdapter extends HandlerInterceptorAdapter { + + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, + Object handler) { + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, + Object handler, ModelAndView modelAndView) throws Exception { + + ThreadContextHolder.setHttpResponse(response); + ThreadContextHolder.setHttpRequest(request); + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, + Object handler, Exception ex) throws Exception { + ThreadContextHolder.remove(); + + super.afterCompletion(request, response, handler, ex); + } +} diff --git a/framework/src/main/java/cn/lili/config/interceptor/UrlConfiguration.java b/framework/src/main/java/cn/lili/config/interceptor/UrlConfiguration.java new file mode 100644 index 00000000..5de9cb8e --- /dev/null +++ b/framework/src/main/java/cn/lili/config/interceptor/UrlConfiguration.java @@ -0,0 +1,44 @@ +package cn.lili.config.interceptor; + +import cn.lili.config.properties.IgnoredUrlsProperties; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * 过滤路径 + * + * @author Chopper + */ +@Configuration +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class UrlConfiguration implements WebMvcConfigurer { + + private final IgnoredUrlsProperties ignoredUrlsProperties; + + private final RequestInterceptorAdapter requestInterceptorAdapter; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 注册拦截器 + InterceptorRegistration ir = registry.addInterceptor(requestInterceptorAdapter); + // 配置拦截的路径 + ir.addPathPatterns("/**"); + // 配置不拦截的路径 + ir.excludePathPatterns(ignoredUrlsProperties.getUrls()); + } + + //开放资源 这里配置swagger可以在前端访问 + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + // 解决 SWAGGER 404报错 + registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); + } + +} diff --git a/framework/src/main/java/cn/lili/config/mongo/MongoConfig.java b/framework/src/main/java/cn/lili/config/mongo/MongoConfig.java new file mode 100644 index 00000000..d2fffd74 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/mongo/MongoConfig.java @@ -0,0 +1,50 @@ +package cn.lili.config.mongo; + +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; +import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; + +import java.util.Collections; + +/** + * @author paulG + * @since 2020/10/22 + **/ +@Configuration +@EnableMongoRepositories +public class MongoConfig extends AbstractMongoClientConfiguration { + + @Value("${spring.data.mongodb.database}") + private String databaseName; + + @Value("${spring.data.mongodb.host}") + private String host; + + @Value("${spring.data.mongodb.port}") + private Integer port; + + @Value("${spring.data.mongodb.username}") + private String username; + + @Value("${spring.data.mongodb.password}") + private String password; + + @Value("${spring.data.mongodb.authentication-database}") + private String authenticationDatabase; + + @Override + protected String getDatabaseName() { + return databaseName; + } + + @Override + protected void configureClientSettings(MongoClientSettings.Builder builder) { + builder.credential(MongoCredential.createCredential(username, authenticationDatabase, password.toCharArray())) + .applyToClusterSettings(settings -> settings.hosts(Collections.singletonList(new ServerAddress(host, port)))); + } + +} diff --git a/framework/src/main/java/cn/lili/config/properties/ApiProperties.java b/framework/src/main/java/cn/lili/config/properties/ApiProperties.java new file mode 100644 index 00000000..806c891d --- /dev/null +++ b/framework/src/main/java/cn/lili/config/properties/ApiProperties.java @@ -0,0 +1,36 @@ +package cn.lili.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * API地址配置 + * @author Chopper + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "lili.api") +public class ApiProperties { + + + /** + * 买家api + */ + private String buyer; + + /** + * 管理端域名 + */ + private String store; + + /** + * 管理端域名 + */ + private String manager; + + /** + * 管理端域名 + */ + private String common; +} diff --git a/framework/src/main/java/cn/lili/config/properties/DomainProperties.java b/framework/src/main/java/cn/lili/config/properties/DomainProperties.java new file mode 100644 index 00000000..7a56f007 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/properties/DomainProperties.java @@ -0,0 +1,37 @@ +package cn.lili.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 域名配置 + * @author Chopper + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "lili.domain") +public class DomainProperties { + + /** + * 买家PC端域名 + */ + private String pc; + + /** + * 买家WAP端域名 + */ + private String wap; + + /** + * Store域名 + */ + private String store; + + /** + * 管理端域名 + */ + private String admin; + + +} diff --git a/framework/src/main/java/cn/lili/config/properties/IgnoredUrlsProperties.java b/framework/src/main/java/cn/lili/config/properties/IgnoredUrlsProperties.java new file mode 100644 index 00000000..097fdee6 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/properties/IgnoredUrlsProperties.java @@ -0,0 +1,22 @@ +package cn.lili.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * 忽略授权设定 + * @author Chopper + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "ignored") +public class IgnoredUrlsProperties { + + private List urls = new ArrayList<>(); + + private List limitUrls = new ArrayList<>(); +} diff --git a/framework/src/main/java/cn/lili/config/properties/JWTTokenProperties.java b/framework/src/main/java/cn/lili/config/properties/JWTTokenProperties.java new file mode 100644 index 00000000..b542694c --- /dev/null +++ b/framework/src/main/java/cn/lili/config/properties/JWTTokenProperties.java @@ -0,0 +1,22 @@ +package cn.lili.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * token过期配置 + * + * @author Chopper + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "lili.jwt-setting") +public class JWTTokenProperties { + + + /** + * token默认过期时间 + */ + private long tokenExpireTime = 60; +} diff --git a/framework/src/main/java/cn/lili/config/properties/StatisticsProperties.java b/framework/src/main/java/cn/lili/config/properties/StatisticsProperties.java new file mode 100644 index 00000000..a2e69777 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/properties/StatisticsProperties.java @@ -0,0 +1,43 @@ +package cn.lili.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 在线人数统计 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2021/2/21 10:19 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "lili.statistics") +public class StatisticsProperties { + + /** + * 在线人数统计 X 小时 + */ + private Integer onlineMember = 48; + + /** + * 当前在线人数 刷新时间间隔 + */ + private Integer currentOnlineUpdate = 600; + + public Integer getOnlineMember() { + if (onlineMember == null) { + return 48; + } + return onlineMember; + } + + public Integer getCurrentOnlineUpdate() { + if (currentOnlineUpdate == null) { + return 600; + } + return currentOnlineUpdate; + } +} diff --git a/framework/src/main/java/cn/lili/config/properties/SystemSetting.java b/framework/src/main/java/cn/lili/config/properties/SystemSetting.java new file mode 100644 index 00000000..0e658f47 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/properties/SystemSetting.java @@ -0,0 +1,21 @@ +package cn.lili.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 系统设置 + * @author Chopper + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "lili.system") +public class SystemSetting { + + + /** + * 是否是演示站点 + */ + private Boolean isDemoSite = false; +} diff --git a/framework/src/main/java/cn/lili/config/properties/ThreadProperties.java b/framework/src/main/java/cn/lili/config/properties/ThreadProperties.java new file mode 100644 index 00000000..90dced01 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/properties/ThreadProperties.java @@ -0,0 +1,43 @@ +package cn.lili.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 线程配置 + * @author Chopper + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "lili.thread") +public class ThreadProperties { + + + /** + * 核心线程数 + */ + private Integer corePoolSize = 10; + + /** + * 最大线程数 + */ + private Integer maxPoolSize = 50; + + /** + * 队列最大长度 + */ + private Integer queueCapacity = Integer.MAX_VALUE; + + /** + * 允许超时关闭 + */ + private Boolean allowCoreThreadTimeOut = false; + + /** + * 保持存活时间 + */ + private Integer KeepAliveSeconds = 60; + + +} diff --git a/framework/src/main/java/cn/lili/config/rocketmq/RocketmqCustomProperties.java b/framework/src/main/java/cn/lili/config/rocketmq/RocketmqCustomProperties.java new file mode 100644 index 00000000..9f17158f --- /dev/null +++ b/framework/src/main/java/cn/lili/config/rocketmq/RocketmqCustomProperties.java @@ -0,0 +1,64 @@ +package cn.lili.config.rocketmq; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author paulG + * @since 2020/10/30 + **/ +@Component +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@ConfigurationProperties(prefix = "lili.data.rocketmq") +public class RocketmqCustomProperties { + + private String promotionTopic; + + private String promotionGroup; + + private String orderTopic; + + private String orderGroup; + + private String msgExtTopic; + + private String msgExtGroup; + + private String goodsTopic; + + private String goodsGroup; + + private String topicUser; + + private String memberTopic; + + private String memberGroup; + + private String otherTopic; + + private String otherGroup; + + private String noticeTopic; + + private String noticeGroup; + + private String noticeSendTopic; + + private String noticeSendGroup; + + private String storeTopic; + + private String storeGroup; + + private String afterSaleTopic; + + private String afterSaleGroup; + +} diff --git a/framework/src/main/java/cn/lili/config/sharding/CreateTimeShardingDatabaseAlgorithm.java b/framework/src/main/java/cn/lili/config/sharding/CreateTimeShardingDatabaseAlgorithm.java new file mode 100644 index 00000000..d25be592 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/sharding/CreateTimeShardingDatabaseAlgorithm.java @@ -0,0 +1,44 @@ +package cn.lili.config.sharding; + +import cn.lili.common.utils.DateUtil; +import com.google.common.collect.Range; +import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; +import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; +import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm; +import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * 按创建时间年份分库 + * + * @author Chopper + */ +public class CreateTimeShardingDatabaseAlgorithm implements PreciseShardingAlgorithm, RangeShardingAlgorithm { + + + @Override + public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) { + Long create_time = preciseShardingValue.getValue(); + String value = DateUtil.toString(create_time, "yyyy"); + //data2019,data2020 + return "data" + value; + } + + @Override + public Collection doSharding(Collection collection, RangeShardingValue rangeShardingValue) { + Collection collect = new ArrayList<>(); + Range valueRange = rangeShardingValue.getValueRange(); + + //开始年份结束年份 + String start = DateUtil.toString(valueRange.lowerEndpoint().longValue(), "yyyy"); + String end = DateUtil.toString(valueRange.upperEndpoint().longValue(), "yyyy"); + //循环增加区间的查询条件 + for (Integer i = Integer.valueOf(start); i <= Integer.valueOf(end); i++) { + collect.add("data" + i); + } + return collect; + } +} + diff --git a/framework/src/main/java/cn/lili/config/sharding/CreateTimeShardingTableAlgorithm.java b/framework/src/main/java/cn/lili/config/sharding/CreateTimeShardingTableAlgorithm.java new file mode 100644 index 00000000..e640e877 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/sharding/CreateTimeShardingTableAlgorithm.java @@ -0,0 +1,81 @@ +package cn.lili.config.sharding; + +import cn.lili.common.utils.DateUtil; +import com.google.common.collect.Range; +import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; +import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; +import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm; +import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * 按创建时间月份分表 + * + * @author Chopper + */ +public class CreateTimeShardingTableAlgorithm implements PreciseShardingAlgorithm, RangeShardingAlgorithm { + + + @Override + public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) { + Long create_time = preciseShardingValue.getValue(); + String monthValue = DateUtil.toString(create_time, "MM"); + String yearValue = DateUtil.toString(create_time, "yyyy"); + Integer month = Integer.valueOf(monthValue); + Integer year = Integer.valueOf(yearValue); + //li_order_1,li_order_2~ + return "li_order_" + year + "_" + month; + } + + @Override + public Collection doSharding(Collection collection, RangeShardingValue rangeShardingValue) { + Collection collect = new ArrayList<>(); + Range valueRange = rangeShardingValue.getValueRange(); + + Integer startMonth = Integer.parseInt(DateUtil.toString(valueRange.lowerEndpoint().longValue(), "MM")); + Integer endMonth = Integer.parseInt(DateUtil.toString(valueRange.upperEndpoint().longValue(), "MM")); + Integer startYear = Integer.parseInt(DateUtil.toString(valueRange.lowerEndpoint().longValue(), "yyyy")); + Integer endYear = Integer.parseInt(DateUtil.toString(valueRange.upperEndpoint().longValue(), "yyyy")); + + //如果是同一年查询 + //2020-1~2020-2 + if (startYear.equals(endYear)) { + for (Integer i = startYear; i <= endYear; i++) { + for (Integer j = startMonth; j <= endMonth; j++) { + collect.add("li_order_" + i + "_" + j); + } + } + } + //2020-1~2021-2 + else { + for (Integer i = startYear; i <= endYear; i++) { + //如果是第一年 + if (i.equals(startYear)) { + //计算从 开始月份 到 今年到12月 + for (Integer j = startMonth; j <= 12; j++) { + collect.add("li_order_" + i + "_" + j); + } + } + //如果是最后一年 + else if (i.equals(endYear)) { + //计算从 1月 到 最后一年结束月份 + for (Integer j = 1; j <= endMonth; j++) { + collect.add("li_order_" + i + "_" + j); + } + } + //中间年份处理 + else { + //中间年份,每个月都要进行查询处理 + for (Integer j = 1; j <= 12; j++) { + collect.add("li_order_" + i + "_" + j); + } + } + } + } + + return collect; + } +} + diff --git a/framework/src/main/java/cn/lili/config/sharding/CreateTimeShardingTableAlgorithmBak.java b/framework/src/main/java/cn/lili/config/sharding/CreateTimeShardingTableAlgorithmBak.java new file mode 100644 index 00000000..8818e31b --- /dev/null +++ b/framework/src/main/java/cn/lili/config/sharding/CreateTimeShardingTableAlgorithmBak.java @@ -0,0 +1,50 @@ +package cn.lili.config.sharding; + +import cn.lili.common.utils.DateUtil; +import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; +import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; +import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm; +import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * 按创建时间月份分表 + * + * @author Chopper + */ +public class CreateTimeShardingTableAlgorithmBak implements PreciseShardingAlgorithm, RangeShardingAlgorithm { + + + @Override + public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) { + Long create_time = preciseShardingValue.getValue(); + String value = DateUtil.toString(create_time, "MM"); + Integer month = Integer.valueOf(value); + //li_order_1,li_order_2~ + return "li_order_" + month; + + } + + @Override + public Collection doSharding(Collection collection, RangeShardingValue rangeShardingValue) { + Collection collect = new ArrayList<>(); + //Range valueRange = rangeShardingValue.getValueRange(); + //开始年份结束年份 + //String start = DateUtil.toString(valueRange.lowerEndpoint().longValue(), "MM"); + //String end = DateUtil.toString(valueRange.upperEndpoint().longValue(), "MM"); + //循环增加区间的查询条件 + + + // 因为考虑到 假设2019-05~2020-05 + // 这快是没办法处理的,因为分库分表之后,每个库都要进行查询,如果操作为,1-4月,那么2020年数据查询正确了,可是2019年到5-12月数据就查询不到了 + // 这里需要做一些性能的浪费,现在看来是没办法处理到 + + for (Integer i = 1; i <= 12; i++) { + collect.add("li_order_" + i); + } + return collect; + } +} + diff --git a/framework/src/main/java/cn/lili/config/swagger/Swagger2Config.java b/framework/src/main/java/cn/lili/config/swagger/Swagger2Config.java new file mode 100644 index 00000000..dc1b1117 --- /dev/null +++ b/framework/src/main/java/cn/lili/config/swagger/Swagger2Config.java @@ -0,0 +1,250 @@ +package cn.lili.config.swagger; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.*; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Chopper + */ +@Slf4j +@Configuration +@EnableSwagger2WebMvc +public class Swagger2Config { + + @Value("${swagger.title}") + private String title; + + @Value("${swagger.description}") + private String description; + + @Value("${swagger.version}") + private String version; + + @Value("${swagger.termsOfServiceUrl}") + private String termsOfServiceUrl; + + @Value("${swagger.contact.name}") + private String name; + + @Value("${swagger.contact.url}") + private String url; + + @Value("${swagger.contact.email}") + private String email; + + private List securitySchemes() { + List apiKeys = new ArrayList<>(); + apiKeys.add(new ApiKey("Authorization", "accessToken", "header")); + return apiKeys; + } + + private List securityContexts() { + List securityContexts = new ArrayList<>(); + securityContexts.add(SecurityContext.builder() + .securityReferences(defaultAuth()) + .forPaths(PathSelectors.regex("^(?!auth).*$")).build()); + return securityContexts; + } + + private List defaultAuth() { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; + } + + @Bean + public Docket goodsRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("商品") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 +// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.goods")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket memberRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("会员") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.member")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket promotionRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("促销") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.promotion")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket storeRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("店铺") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.store")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket tradeRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("交易") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.trade")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + + @Bean + public Docket settingRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("设置") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.setting")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket permissionRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("权限") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.permission")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket otherRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("其他") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.other")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket commonRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("通用") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.common")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + @Bean + public Docket distributionRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("分销") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.distribution")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket statisticsRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("统计") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.statistics")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket paymentRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("支付") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.payment")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + @Bean + public Docket passportRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .groupName("登录") + .apiInfo(apiInfo()).select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.basePackage("cn.lili.controller.passport")) + .paths(PathSelectors.any()) + .build() + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title(title) + .description(description) + .termsOfServiceUrl(termsOfServiceUrl) + .contact(new Contact(name, url, email)) + .version(version) + .build(); + } +} diff --git a/framework/src/main/java/cn/lili/config/thread/ThreadConfig.java b/framework/src/main/java/cn/lili/config/thread/ThreadConfig.java new file mode 100644 index 00000000..a265444a --- /dev/null +++ b/framework/src/main/java/cn/lili/config/thread/ThreadConfig.java @@ -0,0 +1,43 @@ +package cn.lili.config.thread; + +import cn.lili.config.properties.ThreadProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +/** + * 多线程配置 + * + * @author Chopper + * @version v1.0 + * @since + * 2020-03-12 10:50 上午 + */ +@Configuration +public class ThreadConfig implements AsyncConfigurer { + + + @Autowired + private ThreadProperties threadProperties; + + + @Override + public Executor getAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); +// 核心线程数,默认为5 + executor.setCorePoolSize(threadProperties.getCorePoolSize()); +// 最大线程数,默认为10 + executor.setMaxPoolSize(threadProperties.getMaxPoolSize()); +// 队列最大长度,一般需要设置值为足够大 + executor.setQueueCapacity(threadProperties.getQueueCapacity()); +// 线程池维护线程所允许的空闲时间,默认为60s + executor.setKeepAliveSeconds(threadProperties.getKeepAliveSeconds()); +// 允许超时关闭 + executor.setAllowCoreThreadTimeOut(threadProperties.getAllowCoreThreadTimeOut()); + executor.initialize(); + return executor; + } +} diff --git a/framework/src/main/java/cn/lili/config/websocket/WebSocketStompConfig.java b/framework/src/main/java/cn/lili/config/websocket/WebSocketStompConfig.java new file mode 100644 index 00000000..f849c5dd --- /dev/null +++ b/framework/src/main/java/cn/lili/config/websocket/WebSocketStompConfig.java @@ -0,0 +1,41 @@ +package cn.lili.config.websocket; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +/** + * @author Chopper + */ +@Configuration +@EnableWebSocketMessageBroker +public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer { + + /** + * 注册stomp端点 + * @param registry + */ + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + + // 允许使用socketJs方式访问 即可通过http://IP:PORT/manager/ws来和服务端websocket连接 + registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS(); + } + + /** + * 配置信息代理 + * @param registry + */ + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + + // 订阅Broker名称 user点对点 topic广播即群发 + registry.enableSimpleBroker("/user","/topic"); + // 全局(客户端)使用的消息前缀 + registry.setApplicationDestinationPrefixes("/app"); + // 点对点使用的前缀 无需配置 默认/user + registry.setUserDestinationPrefix("/user"); + } +} diff --git a/framework/src/main/java/cn/lili/generator/CodeGenerator.java b/framework/src/main/java/cn/lili/generator/CodeGenerator.java new file mode 100644 index 00000000..be7e6666 --- /dev/null +++ b/framework/src/main/java/cn/lili/generator/CodeGenerator.java @@ -0,0 +1,345 @@ +package cn.lili.generator; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.StringUtils; +import cn.lili.generator.bean.Entity; +import org.beetl.core.Configuration; +import org.beetl.core.GroupTemplate; +import org.beetl.core.Template; +import org.beetl.core.resource.ClasspathResourceLoader; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + + +/** + * 代码生成器 Mybatis-Plus + * + * @author Chopper + */ +public class CodeGenerator { + + /** + * 代码生成在哪个项目 + */ + private static final String project = "framework"; + + /** + * 代码生成在哪个包下边 + */ + private static final String packages = "cn.lili.modules."; + + /** + * modules + */ + private static final String modules = "message"; + + + /** + * 实体类名 + * 建议仅需修改 + */ + private static final String className = "ShortLink"; + + /** + * 类说明描述 + * 建议仅需修改 + */ + private static final String description = "短链接"; + + /** + * 作者名 + * 建议仅需修改 + */ + private static final String author = "Chopper"; + + /** + * 数据库表名前缀 + * 下方请根据需要修改 + */ + private static final String tablePre = "li_"; + + /** + * 主键类型 + */ + private static final String primaryKeyType = "String"; + + + /** + * endity + */ + private static final String entityPackage = packages + modules + ".entity"; + + /** + * dao + */ + private static final String daoPackage = packages + modules + ".mapper"; + + /** + * service + */ + private static final String servicePackage = packages + modules + ".service"; + + /** + * serviceImpl + */ + private static final String serviceImplPackage = packages + modules + ".serviceimpl"; + + /** + * controller + */ + private static final String controllerPackage = packages + modules + ".controller"; + + /** + * 运行该主函数即可生成代码 + * + * @param args + * @throws IOException + */ + public static void main(String[] args) throws IOException { + // 模板路径 + ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("/templates/java/"); + Configuration cfg = Configuration.defaultConfiguration(); + GroupTemplate gt = new GroupTemplate(resourceLoader, cfg); + // 生成代码 + generateCode(gt); + //根据类名删除生成的代码 +// deleteCode(className); + } + + /** + * 生成代码 + * + * @param gt + * @throws IOException + */ + private static void generateCode(GroupTemplate gt) throws IOException { + + Template entityTemplate = gt.getTemplate("entity.btl"); + Template daoTemplate = gt.getTemplate("mapper.btl"); + Template serviceTemplate = gt.getTemplate("service.btl"); + Template serviceImplTemplate = gt.getTemplate("serviceImpl.btl"); + Template controllerTemplate = gt.getTemplate("controller.btl"); + Template mapperXmlTemplate = gt.getTemplate("mapperXml.btl"); + + Entity entity = new Entity(); + entity.setEntityPackage(entityPackage); + entity.setDaoPackage(daoPackage); + entity.setServicePackage(servicePackage); + entity.setServiceImplPackage(serviceImplPackage); + entity.setControllerPackage(controllerPackage); + entity.setAuthor(author); + entity.setClassName(className); + entity.setTableName(tablePre + StringUtils.camel2Underline(className)); + entity.setClassNameLowerCase(name(className, false)); + entity.setDescription(description); + entity.setPrimaryKeyType(primaryKeyType); + + OutputStream out = null; + + //生成实体类代码 + entityTemplate.binding("entity", entity); + String entityResult = entityTemplate.render(); + System.out.println(entityResult); + //创建文件 + String entityFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(entityPackage) + "/" + className + ".java"; + File entityFile = new File(entityFileUrl); + File entityDir = entityFile.getParentFile(); + if (!entityDir.exists()) { + entityDir.mkdirs(); + } + if (!entityFile.exists()) { + // 若文件存在则不重新生成 + entityFile.createNewFile(); + out = new FileOutputStream(entityFile); + entityTemplate.renderTo(out); + } + + //生成dao代码 + daoTemplate.binding("entity", entity); + String daoResult = daoTemplate.render(); + System.out.println(daoResult); + //创建文件 + String daoFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(daoPackage) + "/" + className + "Mapper.java"; + File daoFile = new File(daoFileUrl); + File daoDir = daoFile.getParentFile(); + if (!daoDir.exists()) { + daoDir.mkdirs(); + } + if (!daoFile.exists()) { + // 若文件存在则不重新生成 + daoFile.createNewFile(); + out = new FileOutputStream(daoFile); + daoTemplate.renderTo(out); + } + + //生成service代码 + serviceTemplate.binding("entity", entity); + String serviceResult = serviceTemplate.render(); + System.out.println(serviceResult); + //创建文件 + String serviceFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(servicePackage) + "/" + className + "Service.java"; + File serviceFile = new File(serviceFileUrl); + File serviceDir = serviceFile.getParentFile(); + if (!serviceDir.exists()) { + serviceDir.mkdirs(); + } + if (!serviceFile.exists()) { + // 若文件存在则不重新生成 + serviceFile.createNewFile(); + out = new FileOutputStream(serviceFile); + serviceTemplate.renderTo(out); + } + + //生成serviceImpl代码 + serviceImplTemplate.binding("entity", entity); + String serviceImplResult = serviceImplTemplate.render(); + System.out.println(serviceImplResult); + //创建文件 + System.out.println(System.getProperty("user.dir")); + String serviceImplFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(serviceImplPackage) + "/" + className + "ServiceImpl.java"; + File serviceImplFile = new File(serviceImplFileUrl); + File serviceImplDir = serviceImplFile.getParentFile(); + if (!serviceImplDir.exists()) { + serviceImplDir.mkdirs(); + } + if (!serviceImplFile.exists()) { + // 若文件存在则不重新生成 + serviceImplFile.createNewFile(); + out = new FileOutputStream(serviceImplFile); + serviceImplTemplate.renderTo(out); + } + + //生成controller代码 + controllerTemplate.binding("entity", entity); + String controllerResult = controllerTemplate.render(); + System.out.println(controllerResult); + //创建文件 + String controllerFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(controllerPackage) + "/" + className + "Controller.java"; + File controllerFile = new File(controllerFileUrl); + File controllerDir = controllerFile.getParentFile(); + if (!controllerDir.exists()) { + controllerDir.mkdirs(); + } + if (!controllerFile.exists()) { + // 若文件存在则不重新生成 + controllerFile.createNewFile(); + out = new FileOutputStream(controllerFile); + controllerTemplate.renderTo(out); + } + + //生成mapperXml代码 + mapperXmlTemplate.binding("entity", entity); + String mapperXmlResult = mapperXmlTemplate.render(); + System.out.println(mapperXmlResult); + //创建文件 + String mapperXmlFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/resources/mapper/" + className + "Mapper.xml"; + File mapperXmlFile = new File(mapperXmlFileUrl); + File mapperXmlDir = mapperXmlFile.getParentFile(); + if (!mapperXmlDir.exists()) { + mapperXmlDir.mkdirs(); + } + if (!mapperXmlFile.exists()) { + // 若文件存在则不重新生成 + mapperXmlFile.createNewFile(); + out = new FileOutputStream(mapperXmlFile); + mapperXmlTemplate.renderTo(out); + } + + if (out != null) { + out.close(); + } + System.out.println("生成代码成功!"); + } + + /** + * 删除指定类代码 + * + * @param className + * @throws IOException + */ + private static void deleteCode(String className) throws IOException { + + String entityFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(entityPackage) + "/" + className + ".java"; + File entityFile = new File(entityFileUrl); + if (entityFile.exists()) { + entityFile.delete(); + } + String daoFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(daoPackage) + "/" + className + "Mapper.java"; + File daoFile = new File(daoFileUrl); + if (daoFile.exists()) { + daoFile.delete(); + } + + String serviceFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(servicePackage) + "/" + className + "Service.java"; + File serviceFile = new File(serviceFileUrl); + if (serviceFile.exists()) { + serviceFile.delete(); + } + + String serviceImplFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(serviceImplPackage) + "/" + className + "ServiceImpl.java"; + File serviceImplFile = new File(serviceImplFileUrl); + if (serviceImplFile.exists()) { + serviceImplFile.delete(); + } + + String controllerFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/java/" + dotToLine(controllerPackage) + "/" + className + "Controller.java"; + File controllerFile = new File(controllerFileUrl); + if (controllerFile.exists()) { + controllerFile.delete(); + } + + String mapperXmlFileUrl = System.getProperty("user.dir") + "/" + project + "/src/main/resources/mapper/" + className + "Mapper.xml"; + File mapperXmlFile = new File(mapperXmlFileUrl); + if (mapperXmlFile.exists()) { + mapperXmlFile.delete(); + } + + System.out.println("删除代码完毕!"); + } + + /** + * 点转斜线 + * + * @param str + * @return + */ + public static String dotToLine(String str) { + return str.replace(".", "/"); + } + + + /** + * 首字母是否大小写 + * + * @param name + * @param isFirstUpper + * @return + */ + public static String name(String name, boolean isFirstUpper) { + + if (StrUtil.isBlank(name)) { + throw new ServiceException("name不能为空"); + } + + if (name.length() == 1) { + if (isFirstUpper) { + return name.toUpperCase(); + } else { + return name.toLowerCase(); + } + } + + StringBuffer sb = new StringBuffer(); + if (isFirstUpper) { + sb.append(Character.toUpperCase(name.charAt(0))); + } else { + sb.append(Character.toLowerCase(name.charAt(0))); + } + sb.append(name.substring(1)); + return sb.toString(); + } +} diff --git a/framework/src/main/java/cn/lili/generator/VueCodeGenerator.java b/framework/src/main/java/cn/lili/generator/VueCodeGenerator.java new file mode 100644 index 00000000..b715e244 --- /dev/null +++ b/framework/src/main/java/cn/lili/generator/VueCodeGenerator.java @@ -0,0 +1,244 @@ +package cn.lili.generator; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.ResultMessage; +import cn.lili.generator.bean.Field; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.beetl.core.Configuration; +import org.beetl.core.GroupTemplate; +import org.beetl.core.Template; +import org.beetl.core.resource.ClasspathResourceLoader; +import org.elasticsearch.common.util.ArrayUtils; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * @author lili + */ +@Slf4j +@RestController +@Api(tags = "Vue代码生成") +@RequestMapping(value = "/manager/generate") +public class VueCodeGenerator { + + @RequestMapping(value = "/table/{vueName}/{rowNum}", method = RequestMethod.POST) + @ApiOperation(value = "增删改表格生成") + public ResultMessage generateTable(@PathVariable String vueName, + @PathVariable Integer rowNum, + @RequestBody List fields) throws IOException { + String result = generate("table.btl", vueName, rowNum, fields); + return ResultUtil.data(result); + } + + @RequestMapping(value = "/tree/{vueName}/{rowNum}", method = RequestMethod.POST) + @ApiOperation(value = "树形结构生成") + public ResultMessage generateTree(@PathVariable String vueName, + @PathVariable Integer rowNum, + @RequestBody List fields) throws IOException { + + + String result = generate("tree.btl", vueName, rowNum, fields); + return ResultUtil.data(result); + } + + @RequestMapping(value = "/getEntityData/{path}", method = RequestMethod.GET) + @ApiOperation(value = "通过实体类生成Vue代码Json数据") + public ResultMessage getEntityData(@PathVariable String path) { + + String result = ""; + try { + result = generateClassData(path); + } catch (Exception e) { + return ResultUtil.error(ResultCode.ERROR); + } + return ResultUtil.data(result); + } + + /** + * 生成代码 + * + * @param template 模版名称 + * @param vueName 表单名称 + * @param rowNum 树形表格所需参数,一行几列 + * @param fields + * @return + * @throws IOException + */ + public String generate(String template, String vueName, Integer rowNum, List fields) throws IOException { + + // 模板路径 + ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("/templates/vue/"); + Configuration cfg = Configuration.defaultConfiguration(); + GroupTemplate gt = new GroupTemplate(resourceLoader, cfg); + + Template tableTemplate = gt.getTemplate(template); + // 排序 + Collections.sort(fields, Comparator.comparing(Field::getSortOrder)); + // 绑定变量 + tableTemplate.binding("vueName", vueName); + tableTemplate.binding("fields", fields); + // 判断有无upload和日期范围搜索 + Boolean upload = false; + for (Field f : fields) { + if ("upload".equals(f.getType())) { + upload = true; + } + } + tableTemplate.binding("upload", upload); + if ("table.btl".equals(template)) { + // 判断有无upload和日期范围搜索 + Boolean daterangeSearch = false; + for (Field f : fields) { + if (f.getSearchable() && "daterange".equals(f.getSearchType())) { + daterangeSearch = true; + } + } + tableTemplate.binding("daterangeSearch", daterangeSearch); + // 统计搜索栏个数 判断是否隐藏搜索栏 + Boolean hideSearch = false; + List firstTwo = new ArrayList<>(); + List rest = new ArrayList<>(); + Integer count = 0; + for (Field f : fields) { + if (f.getSearchable()) { + count++; + if (count <= 2) { + firstTwo.add(f); + } else { + rest.add(f); + } + } + } + if (count >= 4) { + hideSearch = true; + tableTemplate.binding("firstTwo", firstTwo); + tableTemplate.binding("rest", rest); + } + tableTemplate.binding("searchSize", count); + tableTemplate.binding("hideSearch", hideSearch); + // 获取默认排序字段 + String defaultSort = "", defaultSortType = ""; + for (Field f : fields) { + if (f.getDefaultSort()) { + defaultSort = f.getField(); + defaultSortType = f.getDefaultSortType(); + break; + } + } + tableTemplate.binding("defaultSort", defaultSort); + tableTemplate.binding("defaultSortType", defaultSortType); + } + // 一行几列 + tableTemplate.binding("rowNum", rowNum); + if (rowNum == 1) { + tableTemplate.binding("modalWidth", 500); + tableTemplate.binding("width", "100%"); + tableTemplate.binding("editWidth", "100%"); + tableTemplate.binding("itemWidth", ""); + tableTemplate.binding("span", "9"); + } else if (rowNum == 2) { + tableTemplate.binding("modalWidth", 770); + tableTemplate.binding("width", "250px"); + tableTemplate.binding("editWidth", "250px"); + tableTemplate.binding("itemWidth", "350px"); + tableTemplate.binding("span", "17"); + } else if (rowNum == 3) { + tableTemplate.binding("modalWidth", 980); + tableTemplate.binding("width", "200px"); + tableTemplate.binding("editWidth", "200px"); + tableTemplate.binding("itemWidth", "300px"); + tableTemplate.binding("span", "17"); + } else if (rowNum == 4) { + tableTemplate.binding("modalWidth", 1130); + tableTemplate.binding("width", "160px"); + tableTemplate.binding("editWidth", "160px"); + tableTemplate.binding("itemWidth", "260px"); + tableTemplate.binding("span", "17"); + } else { + throw new ServiceException("rowNum仅支持数字1-4"); + } + // 生成代码 + String result = tableTemplate.render(); + return result; + } + + /** + * 生成代码数据 + * + * @param path + * @return + * @throws Exception + */ + public String generateClassData(String path) throws Exception { + + Class c = Class.forName(path); + Object obj = c.newInstance(); + java.lang.reflect.Field[] fields = ArrayUtils.concat(obj.getClass().getDeclaredFields(), obj.getClass().getSuperclass().getDeclaredFields(), java.lang.reflect.Field.class); + + //下标 + int index = 0; + + StringBuffer fieldsData = new StringBuffer(); + for (java.lang.reflect.Field field : fields) { + index++; + field.setAccessible(true); + // 字段名 + String fieldName = field.getName(); + String fieldType = field.getType().getName(); + // 序列化id,不参与表单 + if ("serialVersionUID".equals(fieldName)) { + continue; + } + + // 获得字段注解 + ApiModelProperty myFieldAnnotation = field.getAnnotation(ApiModelProperty.class); + //表单名称 + String formName = fieldName; + if (myFieldAnnotation != null) { + formName = myFieldAnnotation.value(); + } + formName = StringUtils.isEmpty(formName) ? fieldName : formName; + + //默认类型 + String type = "text"; + String searchType = "text"; + // 日期字段特殊处理,其他一律按 字符串界面处理 + if (fieldType == "java.lang.Date" || fieldType == "java.util.Date" || fieldType == "Date") { + type = "date"; + searchType = "daterange"; + } + //表单子属性 + String formItem = "{" + + "\"sortOrder\":" + index + "," + + "\"field\":\"" + fieldName + "\"," + + "\"name\":\"" + formName + "\"," + + "\"level\":\"2\"," + + "\"tableShow\":true," + + "\"editable\":true," + + "\"type\":\"" + type + "\"," + + "\"searchType\":\"" + searchType + "\"," + + "\"searchLevel\":\"2\"," + + "\"validate\":false," + + "\"searchable\":true," + + "\"sortable\":false," + + "\"defaultSort\":false," + + "\"defaultSortType\":\"desc\"},"; + fieldsData.append(formItem); + } + String start = "{\"data\":["; + String end = "]}"; + String json = start + fieldsData.substring(0, fieldsData.length()-1) + end; + return json; + } +} diff --git a/framework/src/main/java/cn/lili/generator/bean/Entity.java b/framework/src/main/java/cn/lili/generator/bean/Entity.java new file mode 100644 index 00000000..9f0c2957 --- /dev/null +++ b/framework/src/main/java/cn/lili/generator/bean/Entity.java @@ -0,0 +1,55 @@ +package cn.lili.generator.bean; + +import lombok.Data; + +/** + * @author Chopper + */ +@Data +public class Entity { + + /** + * 实体类,dao,service,controller目录 + */ + private String entityPackage; + + private String daoPackage; + + private String servicePackage; + + private String serviceImplPackage; + + private String controllerPackage; + + + /** + * 作者 + */ + private String author; + + /** + * 类名 + */ + private String className; + + /** + * 首字母小写的类名字,用于模版内的变量名称 + */ + private String classNameLowerCase; + + /** + * 数据库 + */ + private String tableName; + + /** + * 类说明描述,一般设定关键字即可 例如:会员 + */ + private String description; + + /** + * 主键类型 + */ + private String primaryKeyType; + +} diff --git a/framework/src/main/java/cn/lili/generator/bean/Field.java b/framework/src/main/java/cn/lili/generator/bean/Field.java new file mode 100644 index 00000000..1815eeb6 --- /dev/null +++ b/framework/src/main/java/cn/lili/generator/bean/Field.java @@ -0,0 +1,40 @@ +package cn.lili.generator.bean; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author lili + */ +@Data +public class Field { + + private String field; + + private String name; + + private String level; + + private Boolean tableShow; + + private BigDecimal sortOrder; + + private Boolean searchable; + + private Boolean editable; + + private String type; + + private Boolean validate; + + private String searchType; + + private String searchLevel; + + private Boolean sortable; + + private Boolean defaultSort; + + private String defaultSortType; +} diff --git a/framework/src/main/java/cn/lili/modules/base/aspect/DemoInterceptor.java b/framework/src/main/java/cn/lili/modules/base/aspect/DemoInterceptor.java new file mode 100644 index 00000000..b7d71308 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/aspect/DemoInterceptor.java @@ -0,0 +1,33 @@ +package cn.lili.modules.base.aspect; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.config.properties.SystemSetting; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 演示站点拦截 + * DemoInterceptor + * + * @author Chopper + * @version v1.0 + * 2021-05-12 17:55 + */ +@Component +@Aspect +public class DemoInterceptor { + + @Autowired + private SystemSetting systemSetting; + + @Before("@annotation(demoSite)") + public void doAfter(DemoSite demoSite) { + if (systemSetting.getIsDemoSite()) { + throw new ServiceException(ResultCode.DEMO_SITE_EXCEPTION); + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/base/aspect/DemoSite.java b/framework/src/main/java/cn/lili/modules/base/aspect/DemoSite.java new file mode 100644 index 00000000..a0e344e0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/aspect/DemoSite.java @@ -0,0 +1,15 @@ +package cn.lili.modules.base.aspect; + +import java.lang.annotation.*; + +/** + * 演示站点注解 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DemoSite { + + +} diff --git a/framework/src/main/java/cn/lili/modules/base/entity/dos/VerificationSource.java b/framework/src/main/java/cn/lili/modules/base/entity/dos/VerificationSource.java new file mode 100644 index 00000000..77ac279f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/entity/dos/VerificationSource.java @@ -0,0 +1,37 @@ +package cn.lili.modules.base.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 验证码资源维护 + * @author Chopper + * @date 2021/1/30 4:13 下午 + */ +@Data +@Entity +@Table(name = "li_verification_source") +@TableName("li_verification_source") +@ApiModel(value = "验证码资源维护") +public class VerificationSource extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "名称") + private String name; + + @ApiModelProperty(value = "资源地址") + private String resource; + + /** + * @see cn.lili.modules.base.entity.enums.VerificationSourceEnum + */ + @ApiModelProperty(value = "验证码资源类型 SLIDER/SOURCE") + private String type; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/base/entity/enums/ClientTypeEnum.java b/framework/src/main/java/cn/lili/modules/base/entity/enums/ClientTypeEnum.java new file mode 100644 index 00000000..e4a7b838 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/entity/enums/ClientTypeEnum.java @@ -0,0 +1,32 @@ +package cn.lili.modules.base.entity.enums; + + +/** + * 客户端类型 + * + * @author Chopper + * @date 2020/12/8 9:46 + */ + +public enum ClientTypeEnum { + + H5("移动端"), + PC("PC端"), + WECHAT_MP("小程序端"), + APP("移动应用端"), + UNKNOWN("未知"); + + private final String clientName; + + ClientTypeEnum(String des) { + this.clientName = des; + } + + public String clientName() { + return this.clientName; + } + + public String value() { + return this.name(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/base/entity/enums/VerificationSourceEnum.java b/framework/src/main/java/cn/lili/modules/base/entity/enums/VerificationSourceEnum.java new file mode 100644 index 00000000..8ad6023d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/entity/enums/VerificationSourceEnum.java @@ -0,0 +1,20 @@ +package cn.lili.modules.base.entity.enums; + + +/** + * 验证码资源枚举 + * + * @author Chopper + * @date 2021/1/26 15:55 + */ +public enum VerificationSourceEnum { + + SLIDER("滑块"), + RESOURCE("验证码源"); + + private final String description; + + VerificationSourceEnum(String des) { + this.description = des; + } +} diff --git a/framework/src/main/java/cn/lili/modules/base/entity/systemlog/SystemLogVO.java b/framework/src/main/java/cn/lili/modules/base/entity/systemlog/SystemLogVO.java new file mode 100644 index 00000000..aa73148f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/entity/systemlog/SystemLogVO.java @@ -0,0 +1,78 @@ +package cn.lili.modules.base.entity.systemlog; + +import cn.lili.common.utils.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + + +/** + * 日志 + * + * @author Chopper + * @date 2020/12/2 17:50 + */ +@Data +public class SystemLogVO implements Serializable { + + + private static final long serialVersionUID = -8995552592401630086L; + + + @ApiModelProperty(value = "id") + private String id; + + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "日志记录时间") + private Date createTime = new Date(); + + @ApiModelProperty(value = "请求用户") + private String username; + + @ApiModelProperty(value = "请求路径") + private String requestUrl; + + @ApiModelProperty(value = "请求参数") + private String requestParam; + @ApiModelProperty(value = "响应参数") + private String responseBody; + + @ApiModelProperty(value = "ip") + private String ip; + + @ApiModelProperty(value = "方法操作名称") + private String name; + + + @ApiModelProperty(value = "请求类型") + private String requestType; + + + @ApiModelProperty(value = "自定义日志内容") + private String customerLog; + + + @ApiModelProperty(value = "ip信息") + private String ipInfo; + + @ApiModelProperty(value = "花费时间") + private Integer costTime; + + @ApiModelProperty(value = "商家") + private Long storeId = -1L; + + /** + * 转换请求参数为Json + * + * @param paramMap + */ + public void setMapToParams(Map paramMap) { + + this.requestParam = ObjectUtil.mapToString(paramMap); + } +} diff --git a/framework/src/main/java/cn/lili/modules/base/entity/vo/RedisVo.java b/framework/src/main/java/cn/lili/modules/base/entity/vo/RedisVo.java new file mode 100644 index 00000000..638da988 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/entity/vo/RedisVo.java @@ -0,0 +1,18 @@ +package cn.lili.modules.base.entity.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * REDIS VO + * @author Chopper + * @date 2020/12/2 17:50 + */ +@Data +@AllArgsConstructor +public class RedisVo { + + private String key; + + private String value; +} diff --git a/framework/src/main/java/cn/lili/modules/base/entity/vo/VerificationVO.java b/framework/src/main/java/cn/lili/modules/base/entity/vo/VerificationVO.java new file mode 100644 index 00000000..df5a1bca --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/entity/vo/VerificationVO.java @@ -0,0 +1,29 @@ +package cn.lili.modules.base.entity.vo; + +import cn.lili.modules.base.entity.dos.VerificationSource; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 验证码资源缓存VO + * + * @author Chopper + * @date 2020/12/2 17:50 + */ +@Data +public class VerificationVO implements Serializable { + + + /** + * 缓存资源 + */ + List verificationResources; + + /** + * 缓存滑块资源 + */ + List verificationSlider; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/base/mapper/RegionMapper.java b/framework/src/main/java/cn/lili/modules/base/mapper/RegionMapper.java new file mode 100644 index 00000000..9e1e5751 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/mapper/RegionMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.base.mapper; + +import cn.lili.modules.system.entity.dos.Region; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 行政地区数据处理层 + * + * @author Chopper + * @date 2020/12/8 9:46 + */ +public interface RegionMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/base/mapper/VerificationSourceMapper.java b/framework/src/main/java/cn/lili/modules/base/mapper/VerificationSourceMapper.java new file mode 100644 index 00000000..23928931 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/mapper/VerificationSourceMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.base.mapper; + +import cn.lili.modules.base.entity.dos.VerificationSource; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 验证码资源维护数据处理层 + * + * @author Chopper + * @date 2021/1/30 4:17 下午 + */ +public interface VerificationSourceMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/base/service/RegionService.java b/framework/src/main/java/cn/lili/modules/base/service/RegionService.java new file mode 100644 index 00000000..c4b38c79 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/service/RegionService.java @@ -0,0 +1,50 @@ +package cn.lili.modules.base.service; + +import cn.lili.modules.system.entity.dos.Region; +import cn.lili.modules.system.entity.vo.RegionVO; +import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; + +import java.util.List; +import java.util.Map; + +/** + * 行政地区业务层 + * + * @author Chopper + * @date 2020/12/2 14:14 + */ +@CacheConfig(cacheNames = "{regions}") +public interface RegionService extends IService { + + /** + * 同步行政数据 + * + * @param url + */ + @CacheEvict + void synchronizationData(String url); + + + @Cacheable(key = "#id") + List getItem(String id); + + /** + * 获取地址 + * + * @param cityCode 城市编码 + * @param townName 镇名称 + * @return + */ + Map getRegion(String cityCode, String townName); + + /** + * 获取所有的城市 + * + * @return + */ + @Cacheable(key = "'ALL_CITY'") + List getAllCity(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/base/service/VerificationSourceService.java b/framework/src/main/java/cn/lili/modules/base/service/VerificationSourceService.java new file mode 100644 index 00000000..7db9558a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/service/VerificationSourceService.java @@ -0,0 +1,31 @@ +package cn.lili.modules.base.service; + +import cn.lili.common.cache.CachePrefix; +import cn.lili.modules.base.entity.dos.VerificationSource; +import cn.lili.modules.base.entity.vo.VerificationVO; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 验证码资源维护 业务层 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +public interface VerificationSourceService extends IService { + + //缓存 + String VERIFICATION_CACHE = CachePrefix.VERIFICATION.getPrefix(); + + + /** + * 初始化缓存 + * + * @return + */ + VerificationVO initCache(); + + /** + * 获取验证缓存 + */ + VerificationVO getVerificationCache(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/base/serviceimpl/RegionServiceImpl.java b/framework/src/main/java/cn/lili/modules/base/serviceimpl/RegionServiceImpl.java new file mode 100644 index 00000000..98331398 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/serviceimpl/RegionServiceImpl.java @@ -0,0 +1,260 @@ +package cn.lili.modules.base.serviceimpl; + +import cn.lili.common.utils.HttpClientUtils; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.base.mapper.RegionMapper; +import cn.lili.modules.base.service.RegionService; +import cn.lili.modules.system.entity.dos.Region; +import cn.lili.modules.system.entity.vo.RegionVO; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 行政地区业务层实现 + * + * @author Chopper + * @date 2020/12/2 11:11 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RegionServiceImpl extends ServiceImpl implements RegionService { + + /** + * 同步请求地址 + */ + private final String syncUrl = "https://restapi.amap.com/v3/config/district?subdistrict=4&key=e456d77800e2084a326f7b777278f89d"; + + @Override + public void synchronizationData(String url) { + try { + + //清空数据 + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.ne("id", "-1"); + this.remove(queryWrapper); + + //读取数据 + String jsonString = HttpClientUtils.doGet(StringUtils.isEmpty(url) ? syncUrl : url, null); + + //构造存储数据库的对象集合 + List regions = this.initData(jsonString); + for (int i = 0; i < (regions.size() / 100 + (regions.size() % 100 == 0 ? 0 : 1)); i++) { + int endPoint = Math.min((100 + (i * 100)), regions.size()); + this.saveBatch(regions.subList(i * 100, endPoint)); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public List getItem(String id) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(Region::getParentId, id); + List regions = this.list(lambdaQueryWrapper); + regions.sort(new Comparator() { + @Override + public int compare(Region o1, Region o2) { + return o1.getOrderNum().compareTo(o2.getOrderNum()); + } + }); + return regions; + } + + @Override + public Map getRegion(String cityCode, String townName) { + //获取地址信息 + Region region = this.baseMapper.selectOne(new QueryWrapper() + .eq("city_code", cityCode) + .eq("name", townName)); + if (region != null) { + //获取它的层级关系 + String path = region.getPath(); + String[] result = path.split(","); + //因为有无用数据 所以先删除前两个 + result = ArrayUtils.remove(result, 0); + result = ArrayUtils.remove(result, 0); + String regionIds = ""; //地址id + String regionNames = "";//地址名称 + //循环构建新的数据 + for (String regionId : result) { + Region reg = this.baseMapper.selectById(regionId); + if (reg != null) { + regionIds += regionId + ","; + regionNames += reg.getName() + ","; + } + } + regionIds += region.getId(); + regionNames += region.getName(); + //构建返回数据 + Map obj = new HashMap<>(); + obj.put("id", regionIds); + obj.put("name", regionNames); + return obj; + } + return null; + } + + @Override + public List getAllCity() { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + //查询所有省市 + lambdaQueryWrapper.in(Region::getLevel, "city", "province"); + return regionTree(this.list(lambdaQueryWrapper)); + } + + private List regionTree(List regions) { + List regionVOS = new ArrayList<>(); + regions.stream().filter(region -> region.getLevel().equals("province")).forEach(item -> { + regionVOS.add(new RegionVO(item)); + }); + regions.stream().filter(region -> region.getLevel().equals("city")).forEach(item -> { + for (RegionVO region : regionVOS) { + if (region.getId().equals(item.getParentId())) { + region.getChildren().add(new RegionVO(item)); + } + } + }); + return regionVOS; + } + + /** + * 构造数据模型 + * + * @param jsonString + * @throws Exception + */ + private List initData(String jsonString) { + + // 最终数据承载对象 + List regions = new ArrayList<>(); + JSONObject jsonObject = JSONObject.parseObject(jsonString); + //获取到国家及下面所有的信息 开始循环插入,这里可以写成递归调用,但是不如这样方便查看、理解 + JSONArray countryAll = jsonObject.getJSONArray("districts"); + for (int i = 0; i < countryAll.size(); i++) { + JSONObject contry = countryAll.getJSONObject(i); +// String citycode0 = contry.getString("citycode"); +// String adcode0 = contry.getString("adcode"); +// String name0 = contry.getString("name"); +// String center0 = contry.getString("center"); +// String country = contry.getString("level"); +// int level = 0; +// if (country.equals("country")) { +// level = 0; +// } +// 插入国家 +// Integer id1 = insert(0, adcode0, citycode0, name0, center0, level, name0); +// Integer id1 = insert(0, adcode0, citycode0, name0, center0, level); + String id1 = "0"; + JSONArray provinceAll = contry.getJSONArray("districts"); + for (int j = 0; j < provinceAll.size(); j++) { + JSONObject province = provinceAll.getJSONObject(j); + String citycode1 = province.getString("citycode"); + String adcode1 = province.getString("adcode"); + String name1 = province.getString("name"); + String center1 = province.getString("center"); + String level1 = province.getString("level"); + //插入省 + String id2 = insert(regions, id1, citycode1, adcode1, name1, center1, level1, j, id1); + JSONArray cityAll = province.getJSONArray("districts"); + + for (int z = 0; z < cityAll.size(); z++) { + JSONObject city = cityAll.getJSONObject(z); + String citycode2 = city.getString("citycode"); + String adcode2 = city.getString("adcode"); + String name2 = city.getString("name"); + String center2 = city.getString("center"); + String level2 = city.getString("level"); + //插入市 + String id3 = insert(regions, id2, citycode2, adcode2, name2, center2, level2, z, id1, id2); + JSONArray districtAll = city.getJSONArray("districts"); + for (int w = 0; w < districtAll.size(); w++) { + JSONObject district = districtAll.getJSONObject(w); + String citycode3 = district.getString("citycode"); + String adcode3 = district.getString("adcode"); + String name3 = district.getString("name"); + String center3 = district.getString("center"); + String level3 = district.getString("level"); + //插入区县 + String id4 = insert(regions, id3, citycode3, adcode3, name3, center3, level3, w, id1, id2, id3); + // JSONArray street = street3.getJSONArray("districts"); + //有需要可以继续向下遍历 + JSONArray streetAll = district.getJSONArray("districts"); + for (int r = 0; r < streetAll.size(); r++) { + JSONObject street = streetAll.getJSONObject(r); + String citycode4 = street.getString("citycode"); + String adcode4 = street.getString("adcode"); + String name4 = street.getString("name"); + String center4 = street.getString("center"); + String level4 = street.getString("level"); + //插入区县 + insert(regions, id4, citycode4, adcode4, name4, center4, level4, r, id1, id2, id3, id4); + + } + + } + + } + } + } + return regions; + } + + /** + * 公共的插入方法 + * + * @param parentId 父id + * @param cityCode 城市编码 + * @param adCode 区域编码 街道没有独有的adcode,均继承父类(区县)的adcode + * @param name 城市名称 (行政区名称) + * @param center 地理坐标 + * @param level country:国家 + * province:省份(直辖市会在province和city显示) + * city:市(直辖市会在province和city显示) + * district:区县 + * street:街道 + * @param ids 地区id集合 + * @return + */ + public String insert(List regions, String parentId, String cityCode, String adCode, String name, String center, String level, Integer order, String... ids) { +// \"citycode\": [],\n" + +// " \"adcode\": \"100000\",\n" + +// " \"name\": \"中华人民共和国\",\n" + +// " \"center\": \"116.3683244,39.915085\",\n" + +// " \"level\": \"country\",\n" + + Region record = new Region(); + if (!adCode.equals("[]")) { + record.setAdCode(adCode); + } + if (!cityCode.equals("[]")) { + record.setCityCode(cityCode); + } + record.setCenter(center); + record.setLevel(level); + record.setName(name); + record.setParentId(parentId); + record.setOrderNum(order); + String megName = ","; + for (int i = 0; i < ids.length; i++) { + megName = megName + ids[i]; + if (i < ids.length - 1) { + megName = megName + ","; + } + } + record.setPath(megName); + regions.add(record); + return record.getId(); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/base/serviceimpl/VerificationSourceServiceImpl.java b/framework/src/main/java/cn/lili/modules/base/serviceimpl/VerificationSourceServiceImpl.java new file mode 100644 index 00000000..f2637674 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/base/serviceimpl/VerificationSourceServiceImpl.java @@ -0,0 +1,58 @@ +package cn.lili.modules.base.serviceimpl; + +import cn.lili.common.cache.Cache; +import cn.lili.modules.base.entity.dos.VerificationSource; +import cn.lili.modules.base.entity.enums.VerificationSourceEnum; +import cn.lili.modules.base.entity.vo.VerificationVO; +import cn.lili.modules.base.mapper.VerificationSourceMapper; +import cn.lili.modules.base.service.VerificationSourceService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 验证码资源维护 业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:48 下午 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class VerificationSourceServiceImpl extends ServiceImpl implements VerificationSourceService { + + private final Cache cache; + + @Override + public VerificationVO initCache() { + List dbList = this.list(); + List resourceList = new ArrayList<>(); + List sliderList = new ArrayList<>(); + for (VerificationSource item : dbList) { + if (item.getType().equals(VerificationSourceEnum.RESOURCE.name())) { + resourceList.add(item); + } else if (item.getType().equals(VerificationSourceEnum.SLIDER.name())) { + sliderList.add(item); + } + } + VerificationVO verificationVO = new VerificationVO(); + verificationVO.setVerificationResources(resourceList); + verificationVO.setVerificationSlider(sliderList); + cache.put(VERIFICATION_CACHE, verificationVO); + return verificationVO; + } + + @Override + public VerificationVO getVerificationCache() { + VerificationVO verificationVO = cache.get(VERIFICATION_CACHE); + if (verificationVO == null) { + return initCache(); + } + return verificationVO; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/config/AuthConfig.java b/framework/src/main/java/cn/lili/modules/connect/config/AuthConfig.java new file mode 100644 index 00000000..15a7be77 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/config/AuthConfig.java @@ -0,0 +1,112 @@ +package cn.lili.modules.connect.config; + +import cn.lili.modules.connect.entity.dto.AuthCallback; +import com.xkcoding.http.config.HttpConfig; +import lombok.*; + +import java.util.List; + +/** + * JustAuth配置类 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.8 + */ +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuthConfig { + + /** + * 客户端id:对应各平台的appKey + */ + private String clientId; + + /** + * 客户端Secret:对应各平台的appSecret + */ + private String clientSecret; + + /** + * 登录成功后的回调地址 + */ + private String redirectUri; + + /** + * 支付宝公钥:当选择支付宝登录时,该值可用 + * 对应“RSA2(SHA256)密钥”中的“支付宝公钥” + */ + private String alipayPublicKey; + + /** + * 是否需要申请unionid,目前只针对qq登录 + * 注:qq授权登录时,获取unionid需要单独发送邮件申请权限。如果个人开发者账号中申请了该权限,可以将该值置为true,在获取openId时就会同步获取unionId + * 参考链接:http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D + *

+ * 1.7.1版本新增参数 + */ + private boolean unionId; + + /** + * Stack Overflow Key + *

+ * + * @since 1.9.0 + */ + private String stackOverflowKey; + + /** + * 企业微信,授权方的网页应用ID + * + * @since 1.10.0 + */ + private String agentId; + + /** + * 使用 Coding 登录时,需要传该值。 + *

+ * 团队域名前缀,比如以“ https://justauth.coding.net/ ”为例,{@code codingGroupName} = justauth + * + * @since 1.15.5 + */ + private String codingGroupName; + + /** + * 针对国外服务可以单独设置代理 + * HttpConfig config = new HttpConfig(); + * config.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10080))); + * config.setTimeout(15000); + * + * @since 1.15.5 + */ + private HttpConfig httpConfig; + + /** + * 忽略校验 {@code state} 参数,默认不开启。当 {@code ignoreCheckState} 为 {@code true} 时, + * {@link cn.lili.modules.connect.request.AuthDefaultRequest#login(AuthCallback)}} 将不会校验 {@code state} 的合法性。 + *

+ * 使用场景:当且仅当使用自实现 {@code state} 校验逻辑时开启 + *

+ * 以下场景使用方案仅作参考: + * 1. 授权、登录为同端,并且全部使用 JustAuth 实现时,该值建议设为 {@code false}; + * 2. 授权和登录为不同端实现时,比如前端页面拼装 {@code authorizeUrl},并且前端自行对{@code state}进行校验, + * 后端只负责使用{@code code}获取用户信息时,该值建议设为 {@code true}; + * + * 如非特殊需要,不建议开启这个配置 + *

+ * 该方案主要为了解决以下类似场景的问题: + * + * @see https://github.com/justauth/JustAuth/issues/83 + * @since 1.15.6 + */ + private boolean ignoreCheckState; + + /** + * 支持自定义授权平台的 scope 内容 + * + * @since 1.15.7 + */ + private List scopes; +} diff --git a/framework/src/main/java/cn/lili/modules/connect/config/ConnectAuth.java b/framework/src/main/java/cn/lili/modules/connect/config/ConnectAuth.java new file mode 100644 index 00000000..179373e9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/config/ConnectAuth.java @@ -0,0 +1,67 @@ +package cn.lili.modules.connect.config; + + +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import cn.lili.modules.connect.exception.AuthException; + +/** + * OAuth平台的API地址的统一接口 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/12/4 12:17 + */ +public interface ConnectAuth { + + /** + * 授权的api + * + * @return url + */ + String authorize(); + + /** + * 获取accessToken的api + * + * @return url + */ + String accessToken(); + + /** + * 获取用户信息的api + * + * @return url + */ + String userInfo(); + + /** + * 取消授权的api + * + * @return url + */ + default String revoke() { + throw new AuthException(AuthResponseStatus.UNSUPPORTED); + } + + /** + * 刷新授权的api + * + * @return url + */ + default String refresh() { + throw new AuthException(AuthResponseStatus.UNSUPPORTED); + } + + /** + * 获取Source的字符串名字 + * + * @return name + */ + default String getName() { + if (this instanceof Enum) { + return String.valueOf(this); + } + return this.getClass().getSimpleName(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/config/ConnectAuthEnum.java b/framework/src/main/java/cn/lili/modules/connect/config/ConnectAuthEnum.java new file mode 100644 index 00000000..7004313f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/config/ConnectAuthEnum.java @@ -0,0 +1,118 @@ +package cn.lili.modules.connect.config; + + +/** + * 用户信息 枚举 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/12/4 14:10 + */ +public enum ConnectAuthEnum implements ConnectAuth { + + /** + * 微信开放平台 + */ + WECHAT { + @Override + public String authorize() { + return "https://open.weixin.qq.com/connect/oauth2/authorize"; + } + + @Override + public String accessToken() { + return "https://api.weixin.qq.com/sns/oauth2/access_token"; + } + + @Override + public String userInfo() { + return "https://api.weixin.qq.com/sns/userinfo"; + } + }, + + /** + * 微信开放平台 + */ + WECHAT_PC { + @Override + public String authorize() { + return "https://open.weixin.qq.com/connect/qrconnect"; + } + + @Override + public String accessToken() { + return "https://api.weixin.qq.com/sns/oauth2/access_token"; + } + + @Override + public String userInfo() { + return "https://api.weixin.qq.com/sns/userinfo"; + } + + }, + + /** + * QQ + */ + QQ { + @Override + public String authorize() { + return "https://graph.qq.com/oauth2.0/authorize"; + } + + @Override + public String accessToken() { + return "https://graph.qq.com/oauth2.0/token"; + } + + @Override + public String userInfo() { + return "https://graph.qq.com/user/get_user_info"; + } + + }, + + /** + * 支付宝 + */ + ALIPAY { + @Override + public String authorize() { + return "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm"; + } + + @Override + public String accessToken() { + return "https://openapi.alipay.com/gateway.do"; + } + + @Override + public String userInfo() { + return "https://openapi.alipay.com/gateway.do"; + } + + }, + + /** + * 新浪微博 + */ + WEIBO { + @Override + public String authorize() { + return "https://api.weibo.com/oauth2/authorize"; + } + + @Override + public String accessToken() { + return "https://api.weibo.com/oauth2/access_token"; + } + + @Override + public String userInfo() { + return "https://api.weibo.com/2/users/show.json"; + } + + } + +} diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/Connect.java b/framework/src/main/java/cn/lili/modules/connect/entity/Connect.java new file mode 100644 index 00000000..238b5fc9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/Connect.java @@ -0,0 +1,74 @@ +package cn.lili.modules.connect.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + +/** + * @author Chopper + */ +@Data +@Entity +@Table(name = "li_connect") +@TableName("li_connect") +@ApiModel(value = "联合登陆") +@NoArgsConstructor +public class Connect implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty("用户id") + private String userId; + + @ApiModelProperty("联合登录id") + private String unionId; + + /** + * @see cn.lili.modules.connect.entity.enums.ConnectEnum + */ + @ApiModelProperty(value = "联合登录类型") + private String unionType; + + + public Connect(String userId, String unionId, String unionType) { + this.userId = userId; + this.unionId = unionId; + this.unionType = unionType; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/ConnectConfig.java b/framework/src/main/java/cn/lili/modules/connect/entity/ConnectConfig.java new file mode 100644 index 00000000..b32d62e6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/ConnectConfig.java @@ -0,0 +1,49 @@ +package cn.lili.modules.connect.entity; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.connect.entity.enums.ConnectConfigEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Chopper + */ +@Data +@Entity +@Table(name = "li_connect_config") +@TableName("li_connect_config") +@ApiModel(value = "联合登陆配置") +@NoArgsConstructor +public class ConnectConfig extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * @see cn.lili.modules.connect.entity.enums.ConnectConfigEnum + */ + @ApiModelProperty(value = "配置key") + private String configKey; + + + @ApiModelProperty(value = "配置") + private String configValue; + + public ConnectConfig(String configKey) { + this.configKey = configKey; + ConnectConfigEnum configEnum = ConnectConfigEnum.valueOf(configKey); + String[] formItems = configEnum.getForm().split(","); + Map config = new HashMap<>(formItems.length); + for (int i = 0; i < formItems.length; i++) { + config.put(formItems[i], ""); + } + this.configValue = config.toString(); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/dto/AuthCallback.java b/framework/src/main/java/cn/lili/modules/connect/entity/dto/AuthCallback.java new file mode 100644 index 00000000..1fb29699 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/dto/AuthCallback.java @@ -0,0 +1,37 @@ +package cn.lili.modules.connect.entity.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 授权回调时的参数类 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.8.0 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AuthCallback implements Serializable { + + /** + * 访问AuthorizeUrl后回调时带的参数code + */ + private String code; + + /** + * 访问AuthorizeUrl后回调时带的参数auth_code,该参数目前只使用于支付宝登录 + */ + private String auth_code; + + /** + * 访问AuthorizeUrl后回调时带的参数state,用于和请求AuthorizeUrl前的state比较,防止CSRF攻击 + */ + private String state; + +} diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/dto/AuthResponse.java b/framework/src/main/java/cn/lili/modules/connect/entity/dto/AuthResponse.java new file mode 100644 index 00000000..34147e22 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/dto/AuthResponse.java @@ -0,0 +1,47 @@ +package cn.lili.modules.connect.entity.dto; + +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * AuthResponse + * + * @author Chopper + * @version v1.0 + * 2020-12-07 14:20 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuthResponse implements Serializable { + private static final long serialVersionUID = 7668539215757528636L; + /** + * 授权响应状态码 + */ + private int code; + + /** + * 授权响应信息 + */ + private String msg; + + /** + * 授权响应数据,当且仅当 code = 2000 时返回 + */ + private T data; + + /** + * 是否请求成功 + * + * @return true or false + */ + public boolean ok() { + return this.code == AuthResponseStatus.SUCCESS.getCode(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/dto/AuthToken.java b/framework/src/main/java/cn/lili/modules/connect/entity/dto/AuthToken.java new file mode 100644 index 00000000..cd136c92 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/dto/AuthToken.java @@ -0,0 +1,42 @@ +package cn.lili.modules.connect.entity.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 联合登陆授权token + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/12/4 23:48 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AuthToken implements Serializable { + + private static final long serialVersionUID = -2701476618576443366L; + + //第三方token + private String accessToken; + //第三方刷新token + private String refreshToken; + //有效时间 + private int expireIn; + //会员id + private String uid; + //联合登录id + private String unionId; + //联合登录openid + private String openId; + //请求码 + private String accessCode; + + private String scope; +} diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/dto/ConnectAuthUser.java b/framework/src/main/java/cn/lili/modules/connect/entity/dto/ConnectAuthUser.java new file mode 100644 index 00000000..d9e5522d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/dto/ConnectAuthUser.java @@ -0,0 +1,84 @@ +package cn.lili.modules.connect.entity.dto; + +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.enums.AuthUserGender; +import com.alibaba.fastjson.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * AuthUser + * + * @author Chopper + * @version v1.0 + * 2020-12-07 14:18 + */ +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ConnectAuthUser implements Serializable { + private static final long serialVersionUID = -747696192479927491L; + /** + * 用户第三方系统的唯一id + */ + private String uuid; + /** + * 用户名 + */ + private String username; + /** + * 用户昵称 + */ + private String nickname; + /** + * 用户头像 + */ + private String avatar; + /** + * 用户网址 + */ + private String blog; + /** + * 所在公司 + */ + private String company; + /** + * 位置 + */ + private String location; + /** + * 用户邮箱 + */ + private String email; + /** + * 用户备注(各平台中的用户个人介绍) + */ + private String remark; + /** + * 性别 + */ + private AuthUserGender gender; + /** + * 用户来源 + */ + private String source; + /** + * 用户授权的token信息 + */ + private AuthToken token; + /** + * 第三方平台返回的原始用户信息 + */ + private JSONObject rawUserInfo; + + /** + * 联合登陆类型 + */ + private ConnectAuthEnum connectEnum; + +} diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/dto/WechatMPLoginParams.java b/framework/src/main/java/cn/lili/modules/connect/entity/dto/WechatMPLoginParams.java new file mode 100644 index 00000000..e69c231e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/dto/WechatMPLoginParams.java @@ -0,0 +1,23 @@ +package cn.lili.modules.connect.entity.dto; + +import lombok.Data; + +/** + * WechatMPLoginParams + * + * @author Chopper + * @version v1.0 + * 2021-02-19 16:34 + */ +@Data +public class WechatMPLoginParams { + /** + * uuid 用户uuid + * code 微信返回code 用于与微信交互获取openid 等信息 + * encryptedData 微信返回加密信息 + * iv 微信返回 + * image 微信头像 + * nickname 微信用户昵称 + */ + private String uuid, code, encryptedData, iv, image, nickName; +} diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/enums/AuthResponseStatus.java b/framework/src/main/java/cn/lili/modules/connect/entity/enums/AuthResponseStatus.java new file mode 100644 index 00000000..ecdda41d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/enums/AuthResponseStatus.java @@ -0,0 +1,35 @@ +package cn.lili.modules.connect.entity.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * JustAuth通用的状态码对照表 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.8 + */ +@Getter +@AllArgsConstructor +public enum AuthResponseStatus { + /** + * 2000:正常; + * other:调用异常,具体异常内容见{@code msg} + */ + SUCCESS(2000, "Success"), + FAILURE(5000, "Failure"), + NOT_IMPLEMENTED(5001, "Not Implemented"), + PARAMETER_INCOMPLETE(5002, "Parameter incomplete"), + UNSUPPORTED(5003, "Unsupported operation"), + NO_AUTH_SOURCE(5004, "AuthDefaultSource cannot be null"), + UNIDENTIFIED_PLATFORM(5005, "Unidentified platform"), + ILLEGAL_REDIRECT_URI(5006, "Illegal redirect uri"), + ILLEGAL_REQUEST(5007, "Illegal request"), + ILLEGAL_CODE(5008, "Illegal code"), + ILLEGAL_STATUS(5009, "Illegal state"), + REQUIRED_REFRESH_TOKEN(5010, "The refresh token is required; it must not be null"), + ; + + private final int code; + private final String msg; +} diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/enums/AuthUserGender.java b/framework/src/main/java/cn/lili/modules/connect/entity/enums/AuthUserGender.java new file mode 100644 index 00000000..cd9a21bb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/enums/AuthUserGender.java @@ -0,0 +1,59 @@ +package cn.lili.modules.connect.entity.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.util.StringUtils; + +import java.util.Arrays; + +/** + * 用户性别 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.8 + */ +@Getter +@AllArgsConstructor +public enum AuthUserGender { + /** + * MALE/FAMALE为正常值,通过{@link AuthUserGender#getRealGender(String)}方法获取真实的性别 + * UNKNOWN为容错值,部分平台不会返回用户性别,为了方便统一,使用UNKNOWN标记所有未知或不可测的用户性别信息 + */ + MALE("1", "男"), + FEMALE("0", "女"), + UNKNOWN("-1", "未知"); + + private final String code; + private final String desc; + + /** + * 获取用户的实际性别,常规网站 + * + * @param originalGender 用户第三方标注的原始性别 + * @return 用户性别 + */ + public static AuthUserGender getRealGender(String originalGender) { + if (null == originalGender || UNKNOWN.getCode().equals(originalGender)) { + return UNKNOWN; + } + String[] males = {"m", "男", "1", "male"}; + if (Arrays.asList(males).contains(originalGender.toLowerCase())) { + return MALE; + } + return FEMALE; + } + + /** + * 获取微信平台用户的实际性别,0表示未定义,1表示男性,2表示女性 + * + * @param originalGender 用户第三方标注的原始性别 + * @return 用户性别 + * @since 1.13.2 + */ + public static AuthUserGender getWechatRealGender(String originalGender) { + if (StringUtils.isEmpty(originalGender) || "0".equals(originalGender)) { + return AuthUserGender.UNKNOWN; + } + return getRealGender(originalGender); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/enums/ConnectConfigEnum.java b/framework/src/main/java/cn/lili/modules/connect/entity/enums/ConnectConfigEnum.java new file mode 100644 index 00000000..0023dde9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/enums/ConnectConfigEnum.java @@ -0,0 +1,50 @@ +package cn.lili.modules.connect.entity.enums; + +/** + * 联合登录配置 + * + * @author Chopper + * @version v1.0 + * 2020-11-25 18:23 + */ +public enum ConnectConfigEnum { + + /** + * 微信网页 + * 微信小程序 + * 微信APP + * 支付宝 + * 微博 + * qq + */ + WEIXIN_WEB("微信网页配置", "app_key,app_secret"), + WEIXIN_MP("微信小程序配置", "app_key,app_secret"), + WEIXIN_APP("微信APP配置", "app_key,app_secret"), + ALIPAY("支付宝配置", "app_id,private_key,public_key"), + QQ("QQ配置", "app_id,app_key"), + WEIBO("微博配置", "app_key,app_secret"), + ; + + /** + * 名称 + */ + String name; + + /** + * 表单项 + */ + String form; + + ConnectConfigEnum(String name, String form) { + this.name = name; + this.form = form; + } + + public String getName() { + return name; + } + + public String getForm() { + return form; + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/enums/ConnectEnum.java b/framework/src/main/java/cn/lili/modules/connect/entity/enums/ConnectEnum.java new file mode 100644 index 00000000..66dad6af --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/enums/ConnectEnum.java @@ -0,0 +1,29 @@ +package cn.lili.modules.connect.entity.enums; + +/** + * 联合登陆枚举 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/25 18:20 + */ +public enum ConnectEnum { + /** + * 联合登陆枚举各个类型 + */ + QQ("QQ登录"), + WEIBO("微博联合登录"), + WECHAT("微信联合登录"),//只存放unionid + WECHAT_OPEN_ID("微信openid登录"), + WECHAT_MP_OPEN_ID("微信openid登录"), + ALIPAY("支付宝登录"), + APPLE("苹果登录"); + + private final String description; + + ConnectEnum(String description) { + this.description = description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/connect/entity/vo/ConnectConfigForm.java b/framework/src/main/java/cn/lili/modules/connect/entity/vo/ConnectConfigForm.java new file mode 100644 index 00000000..8072b484 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/entity/vo/ConnectConfigForm.java @@ -0,0 +1,26 @@ +package cn.lili.modules.connect.entity.vo; + +import lombok.Data; + +/** + * ConnectConfigForm + * + * @author Chopper + * @version v1.0 + * 2020-11-25 19:01 + */ +@Data +public class ConnectConfigForm { + /** + * 配置名称 + */ + private String name; + /** + * 配置key + */ + private String key; + /** + * 配置表单 + */ + private String form; +} diff --git a/framework/src/main/java/cn/lili/modules/connect/exception/AuthException.java b/framework/src/main/java/cn/lili/modules/connect/exception/AuthException.java new file mode 100644 index 00000000..5cbb8b4e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/exception/AuthException.java @@ -0,0 +1,58 @@ +package cn.lili.modules.connect.exception; + +import cn.lili.modules.connect.config.ConnectAuth; +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; + +/** + * JustAuth通用异常类 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.8 + */ +public class AuthException extends RuntimeException { + + private int errorCode; + private String errorMsg; + + public AuthException(String errorMsg) { + this(AuthResponseStatus.FAILURE.getCode(), errorMsg); + } + + public AuthException(String errorMsg, ConnectAuth source) { + this(AuthResponseStatus.FAILURE.getCode(), errorMsg, source); + } + + public AuthException(int errorCode, String errorMsg) { + super(errorMsg); + this.errorCode = errorCode; + this.errorMsg = errorMsg; + } + + public AuthException(AuthResponseStatus status) { + this(status.getCode(), status.getMsg()); + } + + public AuthException(int errorCode, String errorMsg, ConnectAuth source) { + this(errorCode, String.format("%s [%s]", errorMsg, source.getName())); + } + + public AuthException(AuthResponseStatus status, ConnectAuth source) { + this(status.getCode(), status.getMsg(), source); + } + + public AuthException(String message, Throwable cause) { + super(message, cause); + } + + public AuthException(Throwable cause) { + super(cause); + } + + public int getErrorCode() { + return errorCode; + } + + public String getErrorMsg() { + return errorMsg; + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/mapper/ConnectConfigMapper.java b/framework/src/main/java/cn/lili/modules/connect/mapper/ConnectConfigMapper.java new file mode 100644 index 00000000..0ecf2a12 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/mapper/ConnectConfigMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.connect.mapper; + +import cn.lili.modules.connect.entity.ConnectConfig; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 联合登陆配置数据处理层 + * + * @author Chopper + */ +public interface ConnectConfigMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/mapper/ConnectMapper.java b/framework/src/main/java/cn/lili/modules/connect/mapper/ConnectMapper.java new file mode 100644 index 00000000..711b4bcf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/mapper/ConnectMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.connect.mapper; + +import cn.lili.modules.connect.entity.Connect; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 联合登陆数据处理层 + * + * @author Chopper + */ +public interface ConnectMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/package-info.java b/framework/src/main/java/cn/lili/modules/connect/package-info.java new file mode 100644 index 00000000..d4ecba5a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/package-info.java @@ -0,0 +1,5 @@ +/** + * 项目部分参考 JustAuth + * git地址 https://gitee.com/yadong.zhang/JustAuth + */ +package cn.lili.modules.connect; diff --git a/framework/src/main/java/cn/lili/modules/connect/request/AuthAlipayRequest.java b/framework/src/main/java/cn/lili/modules/connect/request/AuthAlipayRequest.java new file mode 100644 index 00000000..b51f3d2d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/request/AuthAlipayRequest.java @@ -0,0 +1,142 @@ +package cn.lili.modules.connect.request; + +import cn.lili.common.cache.Cache; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.utils.UrlBuilder; +import cn.lili.modules.connect.config.AuthConfig; +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.dto.AuthResponse; +import cn.lili.modules.connect.entity.dto.AuthToken; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import cn.lili.modules.connect.entity.enums.AuthUserGender; +import cn.lili.modules.connect.exception.AuthException; +import com.alibaba.fastjson.JSONObject; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayClient; +import com.alipay.api.DefaultAlipayClient; +import com.alipay.api.request.AlipaySystemOauthTokenRequest; +import com.alipay.api.request.AlipayUserInfoShareRequest; +import com.alipay.api.response.AlipaySystemOauthTokenResponse; +import com.alipay.api.response.AlipayUserInfoShareResponse; + + +/** + * 支付宝登录 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.0.1 + */ +public class AuthAlipayRequest extends AuthDefaultRequest { + + private final AlipayClient alipayClient; + + + public AuthAlipayRequest(AuthConfig config, Cache cache) { + super(config, ConnectAuthEnum.ALIPAY, cache); + this.alipayClient = new DefaultAlipayClient(ConnectAuthEnum.ALIPAY.accessToken(), config.getClientId(), config.getClientSecret(), "json", "UTF-8", config + .getAlipayPublicKey(), "RSA2"); + } + + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest(); + request.setGrantType("authorization_code"); + request.setCode(authCallback.getAuth_code()); + AlipaySystemOauthTokenResponse response = null; + try { + response = this.alipayClient.execute(request); + } catch (Exception e) { + throw new AuthException(e); + } + if (!response.isSuccess()) { + throw new AuthException(response.getSubMsg()); + } + return AuthToken.builder() + .accessToken(response.getAccessToken()) + .uid(response.getUserId()) + .expireIn(Integer.parseInt(response.getExpiresIn())) + .refreshToken(response.getRefreshToken()) + .build(); + } + + /** + * 刷新access token (续期) + * + * @param authToken 登录成功后返回的Token信息 + * @return AuthResponse + */ + @Override + public AuthResponse refresh(AuthToken authToken) { + AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest(); + request.setGrantType("refresh_token"); + request.setRefreshToken(authToken.getRefreshToken()); + AlipaySystemOauthTokenResponse response = null; + try { + response = this.alipayClient.execute(request); + } catch (Exception e) { + throw new AuthException(e); + } + if (!response.isSuccess()) { + throw new AuthException(response.getSubMsg()); + } + return AuthResponse.builder() + .code(AuthResponseStatus.SUCCESS.getCode()) + .data(AuthToken.builder() + .accessToken(response.getAccessToken()) + .uid(response.getUserId()) + .expireIn(Integer.parseInt(response.getExpiresIn())) + .refreshToken(response.getRefreshToken()) + .build()) + .build(); + } + + @Override + protected ConnectAuthUser getUserInfo(AuthToken authToken) { + String accessToken = authToken.getAccessToken(); + AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest(); + AlipayUserInfoShareResponse response = null; + try { + response = this.alipayClient.execute(request, accessToken); + } catch (AlipayApiException e) { + throw new AuthException(e.getErrMsg(), e); + } + if (!response.isSuccess()) { + throw new AuthException(response.getSubMsg()); + } + + String province = response.getProvince(), city = response.getCity(); + String location = String.format("%s %s", StringUtils.isEmpty(province) ? "" : province, StringUtils.isEmpty(city) ? "" : city); + + return ConnectAuthUser.builder() + .rawUserInfo(JSONObject.parseObject(JSONObject.toJSONString(response))) + .uuid(response.getUserId()) + .username(StringUtils.isEmpty(response.getUserName()) ? response.getNickName() : response.getUserName()) + .nickname(response.getNickName()) + .avatar(response.getAvatar()) + .location(location) + .gender(AuthUserGender.getRealGender(response.getGender())) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + * @since 1.9.3 + */ + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(source.authorize()) + .queryParam("app_id", config.getClientId()) + .queryParam("scope", "auth_user") + .queryParam("redirect_uri", config.getRedirectUri()) + .queryParam("state", getRealState(state)) + .build(); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/connect/request/AuthDefaultRequest.java b/framework/src/main/java/cn/lili/modules/connect/request/AuthDefaultRequest.java new file mode 100644 index 00000000..c138aa9f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/request/AuthDefaultRequest.java @@ -0,0 +1,278 @@ +package cn.lili.modules.connect.request; + +import cn.lili.common.cache.Cache; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.utils.UrlBuilder; +import cn.lili.modules.connect.config.AuthConfig; +import cn.lili.modules.connect.config.ConnectAuth; +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.dto.AuthResponse; +import cn.lili.modules.connect.entity.dto.AuthToken; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import cn.lili.modules.connect.exception.AuthException; +import cn.lili.modules.connect.util.AuthChecker; +import cn.lili.modules.connect.util.HttpUtils; +import cn.lili.modules.connect.util.UuidUtils; +import com.xkcoding.http.util.UrlUtil; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +/** + * 默认的request处理类 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @author yangkai.shen (https://xkcoding.com) + * @since 1.0.0 + */ +@Slf4j +public abstract class AuthDefaultRequest implements AuthRequest { + protected AuthConfig config; + protected ConnectAuth source; + protected Cache cache; + + + public AuthDefaultRequest(AuthConfig config, ConnectAuth connectAuth, Cache cache) { + this.config = config; + this.source = connectAuth; + this.cache = cache; + if (!AuthChecker.isSupportedAuth(config, source)) { + throw new AuthException(AuthResponseStatus.PARAMETER_INCOMPLETE, source); + } + // 校验配置合法性 + AuthChecker.checkConfig(config, source); + } + + /** + * 获取access token + * + * @param authCallback 授权成功后的回调参数 + * @return token + */ + protected abstract AuthToken getAccessToken(AuthCallback authCallback); + + /** + * 使用token换取用户信息 + * + * @param authToken token信息 + * @return 用户信息 + */ + protected abstract ConnectAuthUser getUserInfo(AuthToken authToken); + + /** + * 统一的登录入口。当通过{@link AuthRequest#login(AuthCallback)} (String)}授权成功后,会跳转到调用方的相关回调方法中 + * 方法的入参可以使用{@code AuthCallback},{@code AuthCallback}类中封装好了OAuth2授权回调所需要的参数 + * + * @param authCallback 用于接收回调参数的实体 + * @return AuthResponse + */ + @Override + public AuthResponse login(AuthCallback authCallback) { + try { + AuthChecker.checkCode(source, authCallback); +// if (!config.isIgnoreCheckState()) { +// AuthChecker.checkState(authCallback.getState(), source, cache); +// } + + AuthToken authToken = this.getAccessToken(authCallback); + ConnectAuthUser user = this.getUserInfo(authToken); + return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(user).build(); + } catch (Exception e) { + log.error("Failed to login with oauth authorization.", e); + return this.responseError(e); + } + } + + /** + * 处理{@link AuthDefaultRequest#login(AuthCallback)} 发生异常的情况,统一响应参数 + * + * @param e 具体的异常 + * @return AuthResponse + */ + private AuthResponse responseError(Exception e) { + int errorCode = AuthResponseStatus.FAILURE.getCode(); + String errorMsg = e.getMessage(); + if (e instanceof AuthException) { + AuthException authException = ((AuthException) e); + errorCode = authException.getErrorCode(); + if (StringUtils.isNotEmpty(authException.getErrorMsg())) { + errorMsg = authException.getErrorMsg(); + } + } + return AuthResponse.builder().code(errorCode).msg(errorMsg).build(); + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + * @since 1.9.3 + */ + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(source.authorize()) + .queryParam("response_type", "code") + .queryParam("client_id", config.getClientId()) + .queryParam("redirect_uri", config.getRedirectUri()) + .queryParam("state", getRealState(state)) + .build(); + } + + + /** + * 返回获取accessToken的url + * + * @param code 授权码 + * @return 返回获取accessToken的url + */ + protected String accessTokenUrl(String code) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("code", code) + .queryParam("client_id", config.getClientId()) + .queryParam("client_secret", config.getClientSecret()) + .queryParam("grant_type", "authorization_code") + .queryParam("redirect_uri", config.getRedirectUri()) + .build(); + } + + /** + * 返回获取accessToken的url + * + * @param refreshToken refreshToken + * @return 返回获取accessToken的url + */ + protected String refreshTokenUrl(String refreshToken) { + return UrlBuilder.fromBaseUrl(source.refresh()) + .queryParam("client_id", config.getClientId()) + .queryParam("client_secret", config.getClientSecret()) + .queryParam("refresh_token", refreshToken) + .queryParam("grant_type", "refresh_token") + .queryParam("redirect_uri", config.getRedirectUri()) + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param authToken token + * @return 返回获取userInfo的url + */ + protected String userInfoUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.userInfo()).queryParam("access_token", authToken.getAccessToken()).build(); + } + + /** + * 返回获取revoke authorization的url + * + * @param authToken token + * @return 返回获取revoke authorization的url + */ + protected String revokeUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.revoke()).queryParam("access_token", authToken.getAccessToken()).build(); + } + + /** + * 获取state,如果为空, 则默认取当前日期的时间戳 + * + * @param state 原始的state + * @return 返回不为null的state + */ + protected String getRealState(String state) { + if (StringUtils.isEmpty(state)) { + state = UuidUtils.getUUID(); + } + /* 缓存state 回调时候验证缓存里是否有state,如果没有的话则代表是非法访问*/ + cache.put(state, state, 600L); + return state; + } + + /** + * 通用的 authorizationCode 协议 + * + * @param code code码 + * @return Response + */ + protected String doPostAuthorizationCode(String code) { + return new HttpUtils(config.getHttpConfig()).post(accessTokenUrl(code)); + } + + /** + * 通用的 authorizationCode 协议 + * + * @param code code码 + * @return Response + */ + protected String doGetAuthorizationCode(String code) { + return new HttpUtils(config.getHttpConfig()).get(accessTokenUrl(code)); + } + + /** + * 通用的 用户信息 + * + * @param authToken token封装 + * @return Response + */ + @Deprecated + protected String doPostUserInfo(AuthToken authToken) { + return new HttpUtils(config.getHttpConfig()).post(userInfoUrl(authToken)); + } + + /** + * 通用的 用户信息 + * + * @param authToken token封装 + * @return Response + */ + protected String doGetUserInfo(AuthToken authToken) { + return new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken)); + } + + /** + * 通用的post形式的取消授权方法 + * + * @param authToken token封装 + * @return Response + */ + @Deprecated + protected String doPostRevoke(AuthToken authToken) { + return new HttpUtils(config.getHttpConfig()).post(revokeUrl(authToken)); + } + + /** + * 通用的post形式的取消授权方法 + * + * @param authToken token封装 + * @return Response + */ + protected String doGetRevoke(AuthToken authToken) { + return new HttpUtils(config.getHttpConfig()).get(revokeUrl(authToken)); + } + + /** + * 获取以 {@code separator}分割过后的 scope 信息 + * + * @param separator 多个 {@code scope} 间的分隔符 + * @param encode 是否 encode 编码 + * @param defaultScopes 默认的 scope, 当客户端没有配置 {@code scopes} 时启用 + * @return String + * @since 1.16.7 + */ + protected String getScopes(String separator, boolean encode, List defaultScopes) { + List scopes = config.getScopes(); + if (null == scopes || scopes.isEmpty()) { + if (null == defaultScopes || defaultScopes.isEmpty()) { + return ""; + } + scopes = defaultScopes; + } + if (null == separator) { + // 默认为空格 + separator = " "; + } + String scopeStr = String.join(separator, scopes); + return encode ? UrlUtil.urlEncode(scopeStr) : scopeStr; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/connect/request/AuthQQRequest.java b/framework/src/main/java/cn/lili/modules/connect/request/AuthQQRequest.java new file mode 100644 index 00000000..164ec3a1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/request/AuthQQRequest.java @@ -0,0 +1,132 @@ +package cn.lili.modules.connect.request; + +import cn.lili.common.cache.Cache; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.utils.UrlBuilder; +import cn.lili.modules.connect.config.AuthConfig; +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.dto.AuthResponse; +import cn.lili.modules.connect.entity.dto.AuthToken; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import cn.lili.modules.connect.entity.enums.AuthUserGender; +import cn.lili.modules.connect.exception.AuthException; +import cn.lili.modules.connect.util.GlobalAuthUtils; +import cn.lili.modules.connect.util.HttpUtils; +import com.alibaba.fastjson.JSONObject; + +import java.util.Map; + +/** + * qq登录 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @author yangkai.shen (https://xkcoding.com) + * @since 1.1.0 + */ +public class AuthQQRequest extends AuthDefaultRequest { + + public AuthQQRequest(AuthConfig config, Cache cache) { + super(config, ConnectAuthEnum.QQ, cache); + } + + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + String response = doGetAuthorizationCode(authCallback.getCode()); + return getAuthToken(response); + } + + @Override + public AuthResponse refresh(AuthToken authToken) { + String response = new HttpUtils(config.getHttpConfig()).get(refreshTokenUrl(authToken.getRefreshToken())); + return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(getAuthToken(response)).build(); + } + + @Override + protected ConnectAuthUser getUserInfo(AuthToken authToken) { + String openId = this.getOpenId(authToken); + String response = doGetUserInfo(authToken); + JSONObject object = JSONObject.parseObject(response); + if (object.getIntValue("ret") != 0) { + throw new AuthException(object.getString("msg")); + } + String avatar = object.getString("figureurl_qq_2"); + if (StringUtils.isEmpty(avatar)) { + avatar = object.getString("figureurl_qq_1"); + } + + String location = String.format("%s-%s", object.getString("province"), object.getString("city")); + return ConnectAuthUser.builder() + .rawUserInfo(object) + .username(object.getString("nickname")) + .nickname(object.getString("nickname")) + .avatar(avatar) + .location(location) + .uuid(openId) + .gender(AuthUserGender.getRealGender(object.getString("gender"))) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 获取QQ用户的OpenId,支持自定义是否启用查询unionid的功能,如果启用查询unionid的功能, + * 那就需要开发者先通过邮件申请unionid功能,参考链接 {@see http://wiki.connect.qq.com/unionid%E4%BB%8B%E7%BB%8D} + * + * @param authToken 通过{@link AuthQQRequest#getAccessToken(AuthCallback)}获取到的{@code authToken} + * @return openId + */ + private String getOpenId(AuthToken authToken) { + String response = new HttpUtils(config.getHttpConfig()).get(UrlBuilder.fromBaseUrl("https://graph.qq.com/oauth2.0/me") + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("unionid", config.isUnionId() ? 1 : 0) + .build()); + String removePrefix = response.replace("callback(", ""); + String removeSuffix = removePrefix.replace(");", ""); + String openId = removeSuffix.trim(); + JSONObject object = JSONObject.parseObject(openId); + if (object.containsKey("error")) { + throw new AuthException(object.get("error") + ":" + object.get("error_description")); + } + authToken.setOpenId(object.getString("openid")); + if (object.containsKey("unionid")) { + authToken.setUnionId(object.getString("unionid")); + } + return StringUtils.isEmpty(authToken.getUnionId()) ? authToken.getOpenId() : authToken.getUnionId(); + } + + /** + * 返回获取userInfo的url + * + * @param authToken 用户授权token + * @return 返回获取userInfo的url + */ + @Override + protected String userInfoUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.userInfo()) + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("oauth_consumer_key", config.getClientId()) + .queryParam("openid", authToken.getOpenId()) + .build(); + } + + private AuthToken getAuthToken(String response) { + Map accessTokenObject = GlobalAuthUtils.parseStringToMap(response); + if (!accessTokenObject.containsKey("access_token") || accessTokenObject.containsKey("code")) { + throw new AuthException(accessTokenObject.get("msg")); + } + return AuthToken.builder() + .accessToken(accessTokenObject.get("access_token")) + .expireIn(Integer.parseInt(accessTokenObject.getOrDefault("expires_in", "0"))) + .refreshToken(accessTokenObject.get("refresh_token")) + .build(); + } + + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(super.authorize(state)) + .queryParam("scope", "get_user_info") + .build(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/request/AuthRequest.java b/framework/src/main/java/cn/lili/modules/connect/request/AuthRequest.java new file mode 100644 index 00000000..fddd7329 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/request/AuthRequest.java @@ -0,0 +1,93 @@ +package cn.lili.modules.connect.request; + +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.dto.AuthResponse; +import cn.lili.modules.connect.entity.dto.AuthToken; +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import cn.lili.modules.connect.exception.AuthException; + + +/** + * JustAuth {@code Request}公共接口,所有平台的{@code Request}都需要实现该接口 + *

+ * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.8 + */ +public interface AuthRequest { + + /** + * 返回授权url,可自行跳转页面 + *

+ * 不建议使用该方式获取授权地址,不带{@code state}的授权地址,容易受到csrf攻击。 + * 建议使用{@link AuthDefaultRequest#authorize(String)}方法生成授权地址,在回调方法中对{@code state}进行校验 + * + * @return 返回授权地址 + */ + @Deprecated + default String authorize() { + throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED); + } + + /** + * 返回带{@code state} 第三方openid 获取方式,这里主要为微信公众号支付获取openid服务,以及根据openid自动登录 + * 授权回调时会带上这个{@code state} 这里用做uuid + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + */ + default String snsBaseApi(String state) { + throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED); + } + + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + */ + default String authorize(String state) { + throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED); + } + + /** + * 服务端授权回调之后的跳转页面 + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + */ + default String loginUrl(String state) { + throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED); + } + + /** + * 第三方登录 + * + * @param authCallback 用于接收回调参数的实体 + * @return 返回登录成功后的用户信息 + */ + default AuthResponse login(AuthCallback authCallback) { + throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED); + } + /** + * 撤销授权 + * + * @param authToken 登录成功后返回的Token信息 + * @return AuthResponse + */ + default AuthResponse revoke(AuthToken authToken) { + throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED); + } + + /** + * 刷新access token (续期) + * + * @param authToken 登录成功后返回的Token信息 + * @return AuthResponse + */ + default AuthResponse refresh(AuthToken authToken) { + throw new AuthException(AuthResponseStatus.NOT_IMPLEMENTED); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/connect/request/AuthWeChatPCRequest.java b/framework/src/main/java/cn/lili/modules/connect/request/AuthWeChatPCRequest.java new file mode 100644 index 00000000..e0c3bd2e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/request/AuthWeChatPCRequest.java @@ -0,0 +1,169 @@ +package cn.lili.modules.connect.request; + +import cn.lili.common.cache.Cache; +import cn.lili.common.utils.UrlBuilder; +import cn.lili.modules.connect.config.AuthConfig; +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.dto.AuthResponse; +import cn.lili.modules.connect.entity.dto.AuthToken; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import cn.lili.modules.connect.entity.enums.AuthUserGender; +import cn.lili.modules.connect.exception.AuthException; +import cn.lili.modules.connect.util.HttpUtils; +import com.alibaba.fastjson.JSONObject; + +/** + * 微信公众平台登录 + * + * @author yangkai.shen (https://xkcoding.com) + * @since 1.1.0 + */ +public class AuthWeChatPCRequest extends AuthDefaultRequest { + + public AuthWeChatPCRequest(AuthConfig config, Cache cache) { + super(config, ConnectAuthEnum.WECHAT_PC, cache); + } + + /** + * 微信的特殊性,此时返回的信息同时包含 openid 和 access_token + * + * @param authCallback 回调返回的参数 + * @return 所有信息 + */ + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + return this.getToken(accessTokenUrl(authCallback.getCode())); + } + + @Override + protected ConnectAuthUser getUserInfo(AuthToken authToken) { + String response = doGetUserInfo(authToken); + JSONObject object = JSONObject.parseObject(response); + + this.checkResponse(object); + + String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city")); + + if (object.containsKey("unionid")) { + authToken.setUnionId(object.getString("unionid")); + } + + return ConnectAuthUser.builder() + .rawUserInfo(object) + .username(object.getString("nickname")) + .nickname(object.getString("nickname")) + .avatar(object.getString("headimgurl")) + .location(location) + .uuid(authToken.getUnionId()) + .gender(AuthUserGender.getWechatRealGender(object.getString("sex"))) + .token(authToken) + .source(source.toString()) + .build(); + } + + @Override + public AuthResponse refresh(AuthToken oldToken) { + return AuthResponse.builder() + .code(AuthResponseStatus.SUCCESS.getCode()) + .data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken()))) + .build(); + } + + /** + * 检查响应内容是否正确 + * + * @param object 请求响应内容 + */ + private void checkResponse(JSONObject object) { + if (object.containsKey("errcode")) { + throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg")); + } + } + + /** + * 获取token,适用于获取access_token和刷新token + * + * @param accessTokenUrl 实际请求token的地址 + * @return token对象 + */ + private AuthToken getToken(String accessTokenUrl) { + String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl); + JSONObject accessTokenObject = JSONObject.parseObject(response); + + this.checkResponse(accessTokenObject); + + return AuthToken.builder() + .accessToken(accessTokenObject.getString("access_token")) + .refreshToken(accessTokenObject.getString("refresh_token")) + .expireIn(accessTokenObject.getIntValue("expires_in")) + .openId(accessTokenObject.getString("openid")) + .scope(accessTokenObject.getString("scope")) + .build(); + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + * @since 1.9.3 + */ + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(source.authorize()) + .queryParam("response_type", "code") + .queryParam("appid", config.getClientId()) + .queryParam("redirect_uri", config.getRedirectUri()) + .queryParam("scope", "snsapi_login") + .queryParam("state", getRealState(state)) + .build(); + } + + /** + * 返回获取accessToken的url + * + * @param code 授权码 + * @return 返回获取accessToken的url + */ + @Override + protected String accessTokenUrl(String code) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("appid", config.getClientId()) + .queryParam("secret", config.getClientSecret()) + .queryParam("code", code) + .queryParam("grant_type", "authorization_code") + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param authToken 用户授权后的token + * @return 返回获取userInfo的url + */ + @Override + protected String userInfoUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.userInfo()) + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("openid", authToken.getOpenId()) + .queryParam("lang", "zh_CN") + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param refreshToken getAccessToken方法返回的refreshToken + * @return 返回获取userInfo的url + */ + @Override + protected String refreshTokenUrl(String refreshToken) { + return UrlBuilder.fromBaseUrl(source.refresh()) + .queryParam("appid", config.getClientId()) + .queryParam("grant_type", "refresh_token") + .queryParam("refresh_token", refreshToken) + .build(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/request/AuthWeChatRequest.java b/framework/src/main/java/cn/lili/modules/connect/request/AuthWeChatRequest.java new file mode 100644 index 00000000..bc1655fd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/request/AuthWeChatRequest.java @@ -0,0 +1,170 @@ +package cn.lili.modules.connect.request; + +import cn.lili.common.cache.Cache; +import cn.lili.common.utils.UrlBuilder; +import cn.lili.modules.connect.config.AuthConfig; +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.dto.AuthResponse; +import cn.lili.modules.connect.entity.dto.AuthToken; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import cn.lili.modules.connect.entity.enums.AuthUserGender; +import cn.lili.modules.connect.exception.AuthException; +import cn.lili.modules.connect.util.GlobalAuthUtils; +import cn.lili.modules.connect.util.HttpUtils; +import com.alibaba.fastjson.JSONObject; + +/** + * 微信开放平台登录 + * + * @author yangkai.shen (https://xkcoding.com) + * @since 1.1.0 + */ +public class AuthWeChatRequest extends AuthDefaultRequest { + public AuthWeChatRequest(AuthConfig config, Cache cache) { + super(config, ConnectAuthEnum.WECHAT, cache); + } + + /** + * 微信的特殊性,此时返回的信息同时包含 openid 和 access_token + * + * @param authCallback 回调返回的参数 + * @return 所有信息 + */ + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + return this.getToken(accessTokenUrl(authCallback.getCode())); + } + + @Override + protected ConnectAuthUser getUserInfo(AuthToken authToken) { + String openId = authToken.getOpenId(); + + String response = doGetUserInfo(authToken); + JSONObject object = JSONObject.parseObject(response); + + this.checkResponse(object); + + String location = String.format("%s-%s-%s", object.getString("country"), object.getString("province"), object.getString("city")); + + if (object.containsKey("unionid")) { + authToken.setUnionId(object.getString("unionid")); + } + + return ConnectAuthUser.builder() + .rawUserInfo(object) + .username(object.getString("nickname")) + .nickname(object.getString("nickname")) + .avatar(object.getString("headimgurl")) + .location(location) + .uuid(openId) + .gender(AuthUserGender.getWechatRealGender(object.getString("sex"))) + .token(authToken) + .source(source.toString()) + .build(); + } + + @Override + public AuthResponse refresh(AuthToken oldToken) { + return AuthResponse.builder() + .code(AuthResponseStatus.SUCCESS.getCode()) + .data(this.getToken(refreshTokenUrl(oldToken.getRefreshToken()))) + .build(); + } + + /** + * 检查响应内容是否正确 + * + * @param object 请求响应内容 + */ + private void checkResponse(JSONObject object) { + if (object.containsKey("errcode")) { + throw new AuthException(object.getIntValue("errcode"), object.getString("errmsg")); + } + } + + /** + * 获取token,适用于获取access_token和刷新token + * + * @param accessTokenUrl 实际请求token的地址 + * @return token对象 + */ + private AuthToken getToken(String accessTokenUrl) { + String response = new HttpUtils(config.getHttpConfig()).get(accessTokenUrl); + JSONObject accessTokenObject = JSONObject.parseObject(response); + + this.checkResponse(accessTokenObject); + + return AuthToken.builder() + .accessToken(accessTokenObject.getString("access_token")) + .refreshToken(accessTokenObject.getString("refresh_token")) + .expireIn(accessTokenObject.getIntValue("expires_in")) + .openId(accessTokenObject.getString("openid")) + .build(); + } + + /** + * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} + * + * @param state state 验证授权流程的参数,可以防止csrf + * @return 返回授权地址 + * @since 1.9.3 + */ + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(source.authorize()) + .queryParam("appid", config.getClientId()) + .queryParam("redirect_uri", GlobalAuthUtils.urlEncode(config.getRedirectUri())) + .queryParam("response_type", "code") + .queryParam("scope", "snsapi_userinfo") + .queryParam("state", getRealState(state).concat("#wechat_redirect")) + .build(); + } + + /** + * 返回获取accessToken的url + * + * @param code 授权码 + * @return 返回获取accessToken的url + */ + @Override + protected String accessTokenUrl(String code) { + return UrlBuilder.fromBaseUrl(source.accessToken()) + .queryParam("code", code) + .queryParam("appid", config.getClientId()) + .queryParam("secret", config.getClientSecret()) + .queryParam("grant_type", "authorization_code") + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param authToken 用户授权后的token + * @return 返回获取userInfo的url + */ + @Override + protected String userInfoUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.userInfo()) + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("openid", authToken.getOpenId()) + .queryParam("lang", "zh_CN") + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param refreshToken getAccessToken方法返回的refreshToken + * @return 返回获取userInfo的url + */ + @Override + protected String refreshTokenUrl(String refreshToken) { + return UrlBuilder.fromBaseUrl(source.refresh()) + .queryParam("appid", config.getClientId()) + .queryParam("refresh_token", refreshToken) + .queryParam("grant_type", "refresh_token") + .build(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/request/AuthWeiboRequest.java b/framework/src/main/java/cn/lili/modules/connect/request/AuthWeiboRequest.java new file mode 100644 index 00000000..0a60fa62 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/request/AuthWeiboRequest.java @@ -0,0 +1,114 @@ +package cn.lili.modules.connect.request; + + +import cn.lili.common.cache.Cache; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.utils.UrlBuilder; +import cn.lili.modules.connect.config.AuthConfig; +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.dto.AuthResponse; +import cn.lili.modules.connect.entity.dto.AuthToken; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import cn.lili.modules.connect.entity.enums.AuthUserGender; +import cn.lili.modules.connect.exception.AuthException; +import cn.lili.modules.connect.util.HttpUtils; +import cn.lili.modules.connect.util.IpUtils; +import com.alibaba.fastjson.JSONObject; +import com.xkcoding.http.support.HttpHeader; + + +/** + * 微博登录 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.0.0 + */ +public class AuthWeiboRequest extends AuthDefaultRequest { + + public AuthWeiboRequest(AuthConfig config, Cache cache) { + super(config, ConnectAuthEnum.WEIBO, cache); + } + + @Override + protected AuthToken getAccessToken(AuthCallback authCallback) { + String response = doPostAuthorizationCode(authCallback.getCode()); + JSONObject accessTokenObject = JSONObject.parseObject(response); + if (accessTokenObject.containsKey("error")) { + throw new AuthException(accessTokenObject.getString("error_description")); + } + return AuthToken.builder() + .accessToken(accessTokenObject.getString("access_token")) + .uid(accessTokenObject.getString("uid")) + .openId(accessTokenObject.getString("uid")) + .expireIn(accessTokenObject.getIntValue("expires_in")) + .build(); + } + + @Override + protected ConnectAuthUser getUserInfo(AuthToken authToken) { + String accessToken = authToken.getAccessToken(); + String uid = authToken.getUid(); + String oauthParam = String.format("uid=%s&access_token=%s", uid, accessToken); + + HttpHeader httpHeader = new HttpHeader(); + httpHeader.add("Authorization", "OAuth2 " + oauthParam); + httpHeader.add("API-RemoteIP", IpUtils.getLocalIp()); + String userInfo = new HttpUtils(config.getHttpConfig()).get(userInfoUrl(authToken), null, httpHeader, false); + JSONObject object = JSONObject.parseObject(userInfo); + if (object.containsKey("error")) { + throw new AuthException(object.getString("error")); + } + return ConnectAuthUser.builder() + .rawUserInfo(object) + .uuid(object.getString("id")) + .username(object.getString("name")) + .avatar(object.getString("profile_image_url")) + .blog(StringUtils.isEmpty(object.getString("url")) ? "https://weibo.com/" + object.getString("profile_url") : object + .getString("url")) + .nickname(object.getString("screen_name")) + .location(object.getString("location")) + .remark(object.getString("description")) + .gender(AuthUserGender.getRealGender(object.getString("gender"))) + .token(authToken) + .source(source.toString()) + .build(); + } + + /** + * 返回获取userInfo的url + * + * @param authToken authToken + * @return 返回获取userInfo的url + */ + @Override + protected String userInfoUrl(AuthToken authToken) { + return UrlBuilder.fromBaseUrl(source.userInfo()) + .queryParam("access_token", authToken.getAccessToken()) + .queryParam("uid", authToken.getUid()) + .build(); + } + + @Override + public String authorize(String state) { + return UrlBuilder.fromBaseUrl(super.authorize(state)) + .queryParam("scope", "all") + .build(); + } + + @Override + public AuthResponse revoke(AuthToken authToken) { + String response = doGetRevoke(authToken); + JSONObject object = JSONObject.parseObject(response); + if (object.containsKey("error")) { + return AuthResponse.builder() + .code(AuthResponseStatus.FAILURE.getCode()) + .msg(object.getString("error")) + .build(); + } + // 返回 result = true 表示取消授权成功,否则失败 + AuthResponseStatus status = object.getBooleanValue("result") ? AuthResponseStatus.SUCCESS : AuthResponseStatus.FAILURE; + return AuthResponse.builder().code(status.getCode()).msg(status.getMsg()).build(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/service/AbstractConnectLoginPlugin.java b/framework/src/main/java/cn/lili/modules/connect/service/AbstractConnectLoginPlugin.java new file mode 100644 index 00000000..c37d22cc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/service/AbstractConnectLoginPlugin.java @@ -0,0 +1,48 @@ +package cn.lili.modules.connect.service; + +import cn.lili.common.utils.UrlBuilder; +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.dto.AuthToken; +import org.springframework.stereotype.Component; + + +/** + * 信任登录抽象类 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/12/4 10:57 + */ +@Component +public abstract class AbstractConnectLoginPlugin { + + protected static String callBackUrl = "http://www.baidu.com"; + + /** + * 获取授权登录的url + * + * @return URL + */ + protected abstract String getLoginUrl(String uuid); + + /** + * 回调地址 + * + * @return 登录凭证 + */ + protected String callbackUrl(String uuid, ConnectAuthEnum authInterface) { + return UrlBuilder.fromBaseUrl(callBackUrl) + .pathAppend("/buyer/connect/callback") + .pathAppend("/" + authInterface.getName()) + .queryParam("uuid", uuid).build(); + } + + /** + * 回调地址 + * + * @return 登录凭证 + */ + protected abstract AuthToken userInfo(String uuid); + +} diff --git a/framework/src/main/java/cn/lili/modules/connect/service/ConnectConfigService.java b/framework/src/main/java/cn/lili/modules/connect/service/ConnectConfigService.java new file mode 100644 index 00000000..ade0b4f2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/service/ConnectConfigService.java @@ -0,0 +1,36 @@ +package cn.lili.modules.connect.service; + +import cn.lili.modules.connect.entity.ConnectConfig; +import cn.lili.modules.connect.entity.vo.ConnectConfigForm; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 联合登陆配置接口 + * + * @author Chopper + */ +public interface ConnectConfigService extends IService { + + /** + * 获取所有配置项目 + * + * @return + */ + List listForms(); + + /** + * 获取配置详情 + * + * @return + */ + ConnectConfig getConfig(String key); + + /** + * 获取配置详情 + * + * @return + */ + ConnectConfig saveConfig(ConnectConfig connectConfig); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/service/ConnectService.java b/framework/src/main/java/cn/lili/modules/connect/service/ConnectService.java new file mode 100644 index 00000000..a115c150 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/service/ConnectService.java @@ -0,0 +1,101 @@ +package cn.lili.modules.connect.service; + +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.token.Token; +import cn.lili.modules.connect.entity.Connect; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.entity.dto.WechatMPLoginParams; +import com.baomidou.mybatisplus.extension.service.IService; + +import javax.naming.NoPermissionException; +import java.util.List; + +/** + * 联合登陆接口 + * + * @author Chopper + */ +public interface ConnectService extends IService { + + + /** + * 联合登陆cookie 常量 + */ + String CONNECT_COOKIE = "CONNECT_COOKIE"; + /** + * 联合登陆cookie 常量 + */ + String CONNECT_TYPE = "CONNECT_TYPE"; + + /** + * 联合登陆 + * + * @param type + * @param unionid + * @return + */ + Token unionLoginCallback(String type, String unionid, String uuid,boolean longTerm) throws NoPermissionException; + + /** + * 联合登陆对象直接登录 + * + * @param type 第三方登录类型 + * @param authUser 第三方登录返回封装类 + * @param uuid 用户uuid + */ + Token unionLoginCallback(String type, ConnectAuthUser authUser, String uuid); + + /** + * 绑定 + * + * @param unionId + * @param type + * @return + */ + void bind(String unionId, String type); + + /** + * 解绑 + * + * @param type + */ + void unbind(String type); + + /** + * 已绑定列表 + * + * @return + */ + List bindList(); + + + /** + * 联合登录缓存key生成 + * 这个方法返回的key从缓存中可以获取到redis中记录到会员信息,有效时间30分钟 + * + * @param type 联合登陆类型 + * @param uuid 联合登陆uuid + * @return 返回KEY + */ + static String cacheKey(String type, String uuid) { + return CachePrefix.CONNECT_AUTH.getPrefix() + type + uuid; + } + + /** + * app联合登录 回调 + * + * @param authUser 登录对象 + * @param uuid uuid + * @return + */ + Token appLoginCallback(ConnectAuthUser authUser, String uuid); + + + /** + * 微信一键登录 + * 小程序自动登录 没有账户自动注册 + * + * @return + */ + Token miniProgramAutoLogin(WechatMPLoginParams params); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/serviceimpl/ConnectConfigServiceImpl.java b/framework/src/main/java/cn/lili/modules/connect/serviceimpl/ConnectConfigServiceImpl.java new file mode 100644 index 00000000..fa81bd46 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/serviceimpl/ConnectConfigServiceImpl.java @@ -0,0 +1,61 @@ +package cn.lili.modules.connect.serviceimpl; + +import cn.lili.modules.connect.entity.ConnectConfig; +import cn.lili.modules.connect.entity.enums.ConnectConfigEnum; +import cn.lili.modules.connect.entity.vo.ConnectConfigForm; +import cn.lili.modules.connect.mapper.ConnectConfigMapper; +import cn.lili.modules.connect.service.ConnectConfigService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 联合登陆配置接口实现 + * + * @author Chopper + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ConnectConfigServiceImpl extends ServiceImpl implements ConnectConfigService { + + private final ConnectConfigMapper connectConfigMapper; + + @Override + public List listForms() { + + List formList = new ArrayList<>(); + for (int i = 0; i < ConnectConfigEnum.values().length; i++) { + ConnectConfigEnum enums = ConnectConfigEnum.values()[i]; + ConnectConfigForm form = new ConnectConfigForm(); + form.setKey(enums.name()); + form.setName(enums.getName()); + form.setForm(enums.getForm()); + formList.add(form); + } + + return formList; + } + + @Override + public ConnectConfig getConfig(String configKey) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("config_key", configKey); + ConnectConfig connectConfig = this.getOne(queryWrapper); + if (connectConfig == null) { + return new ConnectConfig(configKey); + } + return connectConfig; + } + + @Override + public ConnectConfig saveConfig(ConnectConfig connectConfig) { + return null; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/serviceimpl/ConnectServiceImpl.java b/framework/src/main/java/cn/lili/modules/connect/serviceimpl/ConnectServiceImpl.java new file mode 100644 index 00000000..4d8f6fd3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/serviceimpl/ConnectServiceImpl.java @@ -0,0 +1,353 @@ +package cn.lili.modules.connect.serviceimpl; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.token.Token; +import cn.lili.common.token.base.generate.MemberTokenGenerate; +import cn.lili.common.utils.CookieUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.config.context.ThreadContextHolder; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.connect.entity.Connect; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.entity.dto.WechatMPLoginParams; +import cn.lili.modules.connect.entity.enums.ConnectEnum; +import cn.lili.modules.connect.mapper.ConnectMapper; +import cn.lili.modules.connect.service.ConnectService; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.connect.WechatConnectSetting; +import cn.lili.modules.system.entity.dto.connect.dto.WechatConnectSettingItem; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import cn.lili.modules.system.utils.HttpUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.naming.NoPermissionException; +import java.nio.charset.StandardCharsets; +import java.security.AlgorithmParameters; +import java.security.Security; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * 联合登陆接口实现 + * + * @author Chopper + */ +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ConnectServiceImpl extends ServiceImpl implements ConnectService { + + + @Autowired + private SettingService settingService; + + @Autowired + private MemberService memberService; + + private MemberTokenGenerate memberTokenGenerate; + + private final Cache cache; + + static boolean AUTO_REGION = true; + + @Autowired + public void setMemberTokenGenerate(MemberTokenGenerate memberTokenGenerate) { + this.memberTokenGenerate = memberTokenGenerate; + } + + @Override + public Token unionLoginCallback(String type, String unionid, String uuid, boolean longTerm) throws NoPermissionException { + + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Connect::getUnionId, unionid); + queryWrapper.eq(Connect::getUnionType, type); + //查询绑定关系 + Connect connect = this.getOne(queryWrapper); + if (connect == null) { + throw new NoPermissionException("未绑定用户"); + } + //查询会员 + Member member = memberService.getById(connect.getUserId()); + //如果未绑定会员,则把刚才查询到的联合登录表数据删除 + if (member == null) { + this.remove(queryWrapper); + throw new NoPermissionException("未绑定用户"); + } + return memberTokenGenerate.createToken(member.getUsername(), longTerm); + } catch (NoPermissionException e) { + throw e; + } + } + + @Override + public Token unionLoginCallback(String type, ConnectAuthUser authUser, String uuid) { + + Token token; + try { + token = this.unionLoginCallback(type, authUser.getUuid(), uuid, false); + } catch (NoPermissionException e) { + if (AUTO_REGION) { + token = memberService.autoRegister(authUser); + return token; + } else { + //写入cookie + CookieUtil.addCookie(CONNECT_COOKIE, uuid, 1800, ThreadContextHolder.getHttpResponse()); + CookieUtil.addCookie(CONNECT_TYPE, type, 1800, ThreadContextHolder.getHttpResponse()); + //自动登录失败,则把信息缓存起来 + cache.put(ConnectService.cacheKey(type, uuid), authUser, 30L, TimeUnit.MINUTES); + throw new ServiceException("未绑定用户"); + } + } catch (Exception e) { + log.error("联合登陆异常:", e); + throw new ServiceException("未知错误,请稍后重试"); + } + return token; + } + + @Override + public void bind(String unionId, String type) { + AuthUser authUser = UserContext.getCurrentUser(); + Connect connect = new Connect(authUser.getId(), unionId, type); + this.save(connect); + } + + @Override + public void unbind(String type) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + queryWrapper.eq(Connect::getUserId, UserContext.getCurrentUser().getId()); + queryWrapper.eq(Connect::getUnionType, type); + + this.remove(queryWrapper); + } + + @Override + public List bindList() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Connect::getUserId, UserContext.getCurrentUser().getId()); + List connects = this.list(queryWrapper); + List keys = new ArrayList<>(); + connects.forEach(item -> { + keys.add(item.getUnionType()); + }); + return keys; + } + + @Override + public Token appLoginCallback(ConnectAuthUser authUser, String uuid) { + try { + return this.unionLoginCallback(authUser.getSource(), authUser.getUuid(), uuid, true); + } catch (NoPermissionException e) { + return memberService.autoRegister(authUser); + } + } + + + @Override + public Token miniProgramAutoLogin(WechatMPLoginParams params) { + + Object cacheData = cache.get(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid()); + Map map = new HashMap<>(3); + if (cacheData == null) { + //得到微信小程序联合登陆信息 + JSONObject json = this.getConnect(params.getCode()); + //存储session key 后续登录用得到 + String sessionKey = json.getStr("session_key"); + String unionId = json.getStr("unionid"); + String openId = json.getStr("openid"); + map.put("sessionKey", sessionKey); + map.put("unionId", unionId); + map.put("openId", openId); + cache.put(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid(), map, 900L); + } else { + map = (Map) cacheData; + } + //微信联合登陆参数 + + return phoneMpBindAndLogin(map.get("sessionKey"), params, map.get("openId"), map.get("unionId")); + } + + /** + * 通过微信返回等code 获取openid 等信息 + * + * @param code + * @return + */ + public JSONObject getConnect(String code) { + WechatConnectSettingItem setting = getWechatMPSetting(); + String url = "https://api.weixin.qq.com/sns/jscode2session?" + + "appid=" + setting.getAppId() + "&" + + "secret=" + setting.getAppSecret() + "&" + + "js_code=" + code + "&" + + "grant_type=authorization_code"; + String content = HttpUtils.doGet(url, "UTF-8", 100, 1000); + log.error(content); + return JSONUtil.parseObj(content); + } + + /** + * 手机号 绑定 且 自动登录 + * + * @param sessionKey 微信sessionKey + * @param params 微信小程序自动登录参数 + * @param openId 微信openid + * @param unionId 微信unionid + * @return + */ + public Token phoneMpBindAndLogin(String sessionKey, WechatMPLoginParams params, String openId, String unionId) { + String encryptedData = params.getEncryptedData(), iv = params.getIv(); + JSONObject userInfo = this.getUserInfo(encryptedData, sessionKey, iv); + log.info("联合登陆返回:{}", userInfo.toString()); + String phone = (String) userInfo.get("purePhoneNumber"); + + //手机号登录 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(Member::getMobile, phone); + Member member = memberService.getOne(lambdaQueryWrapper); + //如果不存在会员,则进行绑定微信openid 和 unionid,并且登录 + if (member != null) { + bindMpMember(openId, unionId, member); + return memberTokenGenerate.createToken(member.getUsername(), true); + } + + //如果没有会员,则根据手机号注册会员 + Member newMember = new Member("m" + phone,"111111",phone,params.getNickName(),params.getImage()); + memberService.save(newMember); + newMember = memberService.findByUsername(newMember.getUsername()); + bindMpMember(openId, unionId, newMember); + return memberTokenGenerate.createToken(newMember.getUsername(), true); + } + + /** + * 会员绑定 绑定微信小程序 + *

+ * 如果openid 已经绑定其他账号,则这里不作处理,如果未绑定,则绑定最新的会员 + * 这样,微信小程序注册之后,其他app 公众号页面,都可以实现绑定自动登录功能 + *

+ * + * @param openId + * @param unionId + * @param member + */ + private void bindMpMember(String openId, String unionId, Member member) { + + + //如果unionid 不为空 则为账号绑定unionid + if (StringUtils.isNotEmpty(unionId)) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + lambdaQueryWrapper.eq(Connect::getUnionId, unionId); + lambdaQueryWrapper.eq(Connect::getUnionType, ConnectEnum.WECHAT.name()); + List connects = this.list(lambdaQueryWrapper); + if (connects.size() == 0) { + Connect connect = new Connect(); + connect.setUnionId(unionId); + connect.setUserId(member.getId()); + connect.setUnionType(ConnectEnum.WECHAT.name()); + this.save(connect); + } + }//如果openid 不为空 则为账号绑定openid + if (StringUtils.isNotEmpty(openId)) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + lambdaQueryWrapper.eq(Connect::getUnionId, openId); + lambdaQueryWrapper.eq(Connect::getUnionType, ConnectEnum.WECHAT_MP_OPEN_ID.name()); + List connects = this.list(lambdaQueryWrapper); + if (connects.size() == 0) { + Connect connect = new Connect(); + connect.setUnionId(openId); + connect.setUserId(member.getId()); + connect.setUnionType(ConnectEnum.WECHAT_MP_OPEN_ID.name()); + this.save(connect); + } + } + + } + + /** + * 获取微信小程序配置 + * + * @return + */ + private WechatConnectSettingItem getWechatMPSetting() { + Setting setting = settingService.get(SettingEnum.WECHAT_CONNECT.name()); + + WechatConnectSetting wechatConnectSetting = JSONUtil.toBean(setting.getSettingValue(), WechatConnectSetting.class); + + if (wechatConnectSetting == null) { + return null; + } + //寻找对应对微信小程序登录配置 + for (WechatConnectSettingItem wechatConnectSettingItem : wechatConnectSetting.getWechatConnectSettingItems()) { + if (wechatConnectSettingItem.getClientType().equals(ClientTypeEnum.WECHAT_MP.name())) { + return wechatConnectSettingItem; + } + } + + return null; + } + + + /** + * 解密,获取微信信息 + * + * @param encryptedData 加密信息 + * @param sessionKey 微信sessionKey + * @param iv 微信揭秘参数 + * @return 用户信息 + */ + public JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) { + // 被加密的数据 + byte[] dataByte = Base64.getDecoder().decode(encryptedData); + // 加密秘钥 + byte[] keyByte = Base64.getDecoder().decode(sessionKey); + // 偏移量 + byte[] ivByte = Base64.getDecoder().decode(iv); + try { + // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要 + int base = 16; + if (keyByte.length % base != 0) { + int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0); + byte[] temp = new byte[groups * base]; + Arrays.fill(temp, (byte) 0); + System.arraycopy(keyByte, 0, temp, 0, keyByte.length); + keyByte = temp; + } + // 初始化 + Security.addProvider(new BouncyCastleProvider()); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); + SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); + AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); + parameters.init(new IvParameterSpec(ivByte)); + // 初始化 + cipher.init(Cipher.DECRYPT_MODE, spec, parameters); + byte[] resultByte = cipher.doFinal(dataByte); + if (null != resultByte && resultByte.length > 0) { + String result = new String(resultByte, StandardCharsets.UTF_8); + return JSONUtil.parseObj(result); + } + } catch (Exception e) { + e.printStackTrace(); + } + throw new ServiceException(ResultCode.USER_CONNECT_ERROR); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/connect/util/AuthChecker.java b/framework/src/main/java/cn/lili/modules/connect/util/AuthChecker.java new file mode 100644 index 00000000..250b0104 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/util/AuthChecker.java @@ -0,0 +1,90 @@ +package cn.lili.modules.connect.util; + + +import cn.lili.common.cache.Cache; +import cn.lili.modules.connect.config.AuthConfig; +import cn.lili.modules.connect.config.ConnectAuth; +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.enums.AuthResponseStatus; +import cn.lili.modules.connect.exception.AuthException; +import org.apache.commons.lang3.StringUtils; + + +/** + * 授权配置类的校验器 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.6.1-beta + */ +public class AuthChecker { + + /** + * 是否支持第三方登录 + * + * @param config config + * @param connectAuth source + * @return true or false + * @since 1.6.1-beta + */ + public static boolean isSupportedAuth(AuthConfig config, ConnectAuth connectAuth) { + boolean isSupported = StringUtils.isNotEmpty(config.getClientId()) && StringUtils.isNotEmpty(config.getClientSecret()) && StringUtils.isNotEmpty(config.getRedirectUri()); + if (isSupported && ConnectAuthEnum.ALIPAY == connectAuth) { + isSupported = StringUtils.isNotEmpty(config.getAlipayPublicKey()); + } + return isSupported; + } + + /** + * 检查配置合法性。针对部分平台, 对redirect uri有特定要求。一般来说redirect uri都是http://,而对于facebook平台, redirect uri 必须是https的链接 + * + * @param config config + * @param connectAuth source + * @since 1.6.1-beta + */ + public static void checkConfig(AuthConfig config, ConnectAuth connectAuth) { + String redirectUri = config.getRedirectUri(); + if (!GlobalAuthUtils.isHttpProtocol(redirectUri) && !GlobalAuthUtils.isHttpsProtocol(redirectUri)) { + throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, connectAuth); + } + // 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1 + if (ConnectAuthEnum.ALIPAY == connectAuth && GlobalAuthUtils.isLocalHost(redirectUri)) { + // The redirect uri of alipay is forbidden to use localhost or 127.0.0.1 + throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, connectAuth); + } + } + + /** + * 校验回调传回的code + *

+ * {@code v1.10.0}版本中改为传入{@code source}和{@code callback},对于不同平台使用不同参数接受code的情况统一做处理 + * + * @param connectAuth 当前授权平台 + * @param callback 从第三方授权回调回来时传入的参数集合 + * @since 1.8.0 + */ + public static void checkCode(ConnectAuth connectAuth, AuthCallback callback) { + String code = callback.getCode(); + if (connectAuth == ConnectAuthEnum.ALIPAY) { + code = callback.getAuth_code(); + } + if (StringUtils.isEmpty(code)) { + throw new AuthException(AuthResponseStatus.ILLEGAL_CODE, connectAuth); + } + } + + /** + * 校验回调传回的{@code state},为空或者不存在 + *

+ * {@code state}不存在的情况只有两种: + * 1. {@code state}已使用,被正常清除 + * 2. {@code state}为前端伪造,本身就不存在 + * + * @param state {@code state}一定不为空 + */ + public static void checkState(String state, ConnectAuth connectAuth, Cache cache) { + if (StringUtils.isEmpty(state) || !cache.hasKey(state)) { + throw new AuthException(AuthResponseStatus.ILLEGAL_STATUS, connectAuth); + } + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/util/Base64Utils.java b/framework/src/main/java/cn/lili/modules/connect/util/Base64Utils.java new file mode 100644 index 00000000..8d0c2fe0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/util/Base64Utils.java @@ -0,0 +1,263 @@ +package cn.lili.modules.connect.util; + +import sun.misc.BASE64Decoder; +import sun.misc.BASE64Encoder; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * Base64编码 + * + * @author looly + * @since 3.2.0 + */ +public class Base64Utils { + + private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + /** + * 标准编码表 + */ + private static final byte[] STANDARD_ENCODE_TABLE = { // + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // + 'w', 'x', 'y', 'z', '0', '1', '2', '3', // + '4', '5', '6', '7', '8', '9', '+', '/' // + }; + /** + * URL安全的编码表,将 + 和 / 替换为 - 和 _ + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { // + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // + 'w', 'x', 'y', 'z', '0', '1', '2', '3', // + '4', '5', '6', '7', '8', '9', '-', '_' // + }; + + // -------------------------------------------------------------------- encode + + /** + * 编码为Base64,非URL安全的 + * + * @param arr 被编码的数组 + * @param lineSep 在76个char之后是CRLF还是EOF + * @return 编码后的bytes + */ + public static byte[] encode(byte[] arr, boolean lineSep) { + return encode(arr, lineSep, false); + } + + /** + * 编码为Base64,URL安全的 + * + * @param arr 被编码的数组 + * @param lineSep 在76个char之后是CRLF还是EOF + * @return 编码后的bytes + * @since 3.0.6 + */ + public static byte[] encodeUrlSafe(byte[] arr, boolean lineSep) { + return encode(arr, lineSep, true); + } + + /** + * base64编码 + * + * @param source 被编码的base64字符串 + * @return 被加密后的字符串 + */ + public static String encode(CharSequence source) { + return encode(source, DEFAULT_CHARSET); + } + + /** + * base64编码,URL安全 + * + * @param source 被编码的base64字符串 + * @return 被加密后的字符串 + * @since 3.0.6 + */ + public static String encodeUrlSafe(CharSequence source) { + return encodeUrlSafe(source, DEFAULT_CHARSET); + } + + /** + * base64编码 + * + * @param source 被编码的base64字符串 + * @param charset 字符集 + * @return 被加密后的字符串 + */ + public static String encode(CharSequence source, Charset charset) { + return encode(bytes(source, charset)); + } + + /** + * base64编码,URL安全的 + * + * @param source 被编码的base64字符串 + * @param charset 字符集 + * @return 被加密后的字符串 + * @since 3.0.6 + */ + public static String encodeUrlSafe(CharSequence source, Charset charset) { + return encodeUrlSafe(bytes(source, charset)); + } + + /** + * base64编码 + * + * @param source 被编码的base64字符串 + * @return 被加密后的字符串 + */ + public static String encode(byte[] source) { + return str(encode(source, false), DEFAULT_CHARSET); + } + + /** + * base64编码,URL安全的 + * + * @param source 被编码的base64字符串 + * @return 被加密后的字符串 + * @since 3.0.6 + */ + public static String encodeUrlSafe(byte[] source) { + return str(encodeUrlSafe(source, false), DEFAULT_CHARSET); + } + + /** + * 编码为Base64
+ * 如果isMultiLine为true,则每76个字符一个换行符,否则在一行显示 + * + * @param arr 被编码的数组 + * @param isMultiLine 在76个char之后是CRLF还是EOF + * @param isUrlSafe 是否使用URL安全字符,一般为false + * @return 编码后的bytes + */ + public static byte[] encode(byte[] arr, boolean isMultiLine, boolean isUrlSafe) { + if (null == arr) { + return null; + } + + int len = arr.length; + if (len == 0) { + return new byte[0]; + } + + int evenlen = (len / 3) * 3; + int cnt = ((len - 1) / 3 + 1) << 2; + int destlen = cnt + (isMultiLine ? (cnt - 1) / 76 << 1 : 0); + byte[] dest = new byte[destlen]; + + byte[] encodeTable = isUrlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; + + for (int s = 0, d = 0, cc = 0; s < evenlen; ) { + int i = (arr[s++] & 0xff) << 16 | (arr[s++] & 0xff) << 8 | (arr[s++] & 0xff); + + dest[d++] = encodeTable[(i >>> 18) & 0x3f]; + dest[d++] = encodeTable[(i >>> 12) & 0x3f]; + dest[d++] = encodeTable[(i >>> 6) & 0x3f]; + dest[d++] = encodeTable[i & 0x3f]; + + if (isMultiLine && ++cc == 19 && d < destlen - 2) { + dest[d++] = '\r'; + dest[d++] = '\n'; + cc = 0; + } + } + + int left = len - evenlen;// 剩余位数 + if (left > 0) { + int i = ((arr[evenlen] & 0xff) << 10) | (left == 2 ? ((arr[len - 1] & 0xff) << 2) : 0); + + dest[destlen - 4] = encodeTable[i >> 12]; + dest[destlen - 3] = encodeTable[(i >>> 6) & 0x3f]; + + if (isUrlSafe) { + // 在URL Safe模式下,=为URL中的关键字符,不需要补充。空余的byte位要去掉。 + int urlSafeLen = destlen - 2; + if (2 == left) { + dest[destlen - 2] = encodeTable[i & 0x3f]; + urlSafeLen += 1; + } + byte[] urlSafeDest = new byte[urlSafeLen]; + System.arraycopy(dest, 0, urlSafeDest, 0, urlSafeLen); + return urlSafeDest; + } else { + dest[destlen - 2] = (left == 2) ? encodeTable[i & 0x3f] : (byte) '='; + dest[destlen - 1] = '='; + } + } + return dest; + } + + /** + * 编码字符串 + * + * @param str 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 编码后的字节码 + */ + public static byte[] bytes(CharSequence str, Charset charset) { + if (str == null) { + return null; + } + + if (null == charset) { + return str.toString().getBytes(); + } + return str.toString().getBytes(charset); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) { + if (data == null) { + return null; + } + + if (null == charset) { + return new String(data); + } + return new String(data, charset); + } + + + /** + * 二进制流转Base64字符串 + * + * @param data 二进制流 + * @return data + * @throws IOException 异常 + */ + public static String getImageString(byte[] data) throws IOException { + BASE64Encoder encoder = new BASE64Encoder(); + return data != null ? encoder.encode(data) : ""; + } + + + /** + * Base64字符串转 二进制流 + * + * @param base64String Base64 + * @return base64String + * @throws IOException 异常 + */ + public static byte[] getStringImage(String base64String) throws IOException { + BASE64Decoder decoder = new sun.misc.BASE64Decoder(); + return base64String != null ? decoder.decodeBuffer(base64String) : null; + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/util/ConnectUtil.java b/framework/src/main/java/cn/lili/modules/connect/util/ConnectUtil.java new file mode 100644 index 00000000..4df6953b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/util/ConnectUtil.java @@ -0,0 +1,227 @@ +package cn.lili.modules.connect.util; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.token.Token; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.config.properties.ApiProperties; +import cn.lili.config.properties.DomainProperties; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.connect.config.AuthConfig; +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.dto.AuthCallback; +import cn.lili.modules.connect.entity.dto.AuthResponse; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.exception.AuthException; +import cn.lili.modules.connect.request.AuthRequest; +import cn.lili.modules.connect.request.AuthWeChatPCRequest; +import cn.lili.modules.connect.request.AuthWeChatRequest; +import cn.lili.modules.connect.service.ConnectService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.connect.QQConnectSetting; +import cn.lili.modules.system.entity.dto.connect.WechatConnectSetting; +import cn.lili.modules.system.entity.dto.connect.dto.QQConnectSettingItem; +import cn.lili.modules.system.entity.dto.connect.dto.WechatConnectSettingItem; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 联合登陆工具类 + * + * @author Chopper + * @version v1.0 + * 2020-11-25 21:16 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ConnectUtil { + + + private final Cache cache; + + private final ConnectService connectService; + + private final SettingService settingService; + + private final ApiProperties apiProperties; + + private final DomainProperties domainProperties; + + + static String prefix = "/buyer/connect/callback/"; + + //回调地址获取 + String getRedirectUri(ConnectAuthEnum connectAuthEnum) { + return apiProperties.getBuyer() + prefix + connectAuthEnum.getName(); + } + + /** + * 登录回调 + * + * @param type + * @param callback + * @param httpServletResponse + * @throws IOException + */ + public void callback(String type, AuthCallback callback, HttpServletResponse httpServletResponse) throws IOException { + AuthRequest authRequest = this.getAuthRequest(type); + AuthResponse response = authRequest.login(callback); + ResultMessage resultMessage; + //联合登陆处理,如果响应正常,则录入响应结果到redis + if (response.ok()) { + ConnectAuthUser authUser = response.getData(); + Token token; + try { + token = connectService.unionLoginCallback(type, authUser, callback.getState()); + resultMessage = ResultUtil.data(token); + } catch (ServiceException e) { + resultMessage = ResultUtil.error(400,e.getMessage()); + } + } + //否则录入响应结果,等待前端获取信息 + else { + resultMessage = ResultUtil.error(400,response.getMsg()); + } + //缓存写入登录结果,300秒有效 + cache.put(CachePrefix.CONNECT_RESULT.getPrefix() + callback.getCode(), resultMessage, 300L); + +// String url = buyer + "/login?state=" + callback.getCode(); + String url = domainProperties.getWap() + "/pages/public/login?state=" + callback.getCode(); + try { + httpServletResponse.sendRedirect(url); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 获取响应结果 + * + * @param state + * @return + */ + public ResultMessage getResult(String state) { + Object object = cache.get(CachePrefix.CONNECT_RESULT.getPrefix() + state); + if (object == null) { + return null; + } else { + cache.remove(CachePrefix.CONNECT_RESULT.getPrefix() + state); + return (ResultMessage) object; + } + } + + /** + * 联合登录 + * + * @param type 枚举 + * @return + */ + public AuthRequest getAuthRequest(String type) { + ConnectAuthEnum authInterface = ConnectAuthEnum.valueOf(type); + if (authInterface == null) { + throw new ServiceException("错误的登录方式"); + } + AuthRequest authRequest = null; + switch (authInterface) { + case WECHAT: { + //寻找配置 + Setting setting = settingService.get(SettingEnum.WECHAT_CONNECT.name()); + WechatConnectSetting wechatConnectSetting = JSONUtil.toBean(setting.getSettingValue(), WechatConnectSetting.class); + + for (WechatConnectSettingItem wechatConnectSettingItem : wechatConnectSetting.getWechatConnectSettingItems()) { + if (wechatConnectSettingItem.getClientType().equals(ClientTypeEnum.H5.name())) { + authRequest = new AuthWeChatRequest(AuthConfig.builder() + .clientId(wechatConnectSettingItem.getAppId()) + .clientSecret(wechatConnectSettingItem.getAppSecret()) + .redirectUri(getRedirectUri(authInterface)) + .build(), cache); + } + } + break; + } + case WECHAT_PC: { + //寻找配置 + Setting setting = settingService.get(SettingEnum.WECHAT_CONNECT.name()); + WechatConnectSetting wechatConnectSetting = JSONUtil.toBean(setting.getSettingValue(), WechatConnectSetting.class); + for (WechatConnectSettingItem wechatConnectSettingItem : wechatConnectSetting.getWechatConnectSettingItems()) { + if (wechatConnectSettingItem.getClientType().equals(ClientTypeEnum.PC.name())) { + authRequest = new AuthWeChatPCRequest(AuthConfig.builder() + .clientId(wechatConnectSettingItem.getAppId()) + .clientSecret(wechatConnectSettingItem.getAppSecret()) + .redirectUri(getRedirectUri(authInterface)) + .build(), cache); + } + } + + break; + } + case QQ: + + //寻找配置 + Setting setting = settingService.get(SettingEnum.QQ_CONNECT.name()); + QQConnectSetting qqConnectSetting = JSONUtil.toBean(setting.getSettingValue(), QQConnectSetting.class); + for (QQConnectSettingItem qqConnectSettingItem : qqConnectSetting.getQqConnectSettingItemList()) { + if (qqConnectSettingItem.getClientType().equals(ClientTypeEnum.PC.name())) { + authRequest = new AuthWeChatPCRequest(AuthConfig.builder() + .clientId(qqConnectSettingItem.getAppId()) + .clientSecret(qqConnectSettingItem.getAppKey()) + .redirectUri(getRedirectUri(authInterface)) + .build(), cache); + } + } + + break; +// case ALIPAY: +// // 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1,所以这儿的回调地址使用的局域网内的ip +// authRequest = new AuthAlipayRequest(AuthConfig.builder() +// .clientId("") +// .clientSecret("") +// .alipayPublicKey("") +// .redirectUri(getRedirectUri(authInterface)) +// .build(), cache); +// break; +// case WEIBO: +// List scopes = new ArrayList<>(); +// scopes.add("all"); +// authRequest = new AuthWeiboRequest(AuthConfig.builder() +// .clientId("") +// .clientSecret("") +// .redirectUri(getRedirectUri(authInterface)) +// .scopes(scopes) +// .build(), cache); +// break; +// case "wechat_open": +// authRequest = new AuthWeChatOpenRequest(AuthConfig.builder() +// .clientId("") +// .clientSecret("") +// .redirectUri("https://z171l91606.51mypc.cn/callback/wechat") +// .build()); +// break; +// case "wechat_mp": +// authRequest = new AuthWeChatMpRequest(AuthConfig.builder() +// .clientId("") +// .clientSecret("") +// .redirectUri("") +// .build()); +// break; + default: + break; + } + if (null == authRequest) { + throw new AuthException("暂不支持第三方登陆"); + } + return authRequest; + } + + +} + diff --git a/framework/src/main/java/cn/lili/modules/connect/util/GlobalAuthUtils.java b/framework/src/main/java/cn/lili/modules/connect/util/GlobalAuthUtils.java new file mode 100644 index 00000000..e4a7a256 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/util/GlobalAuthUtils.java @@ -0,0 +1,303 @@ +package cn.lili.modules.connect.util; + +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.connect.exception.AuthException; +import com.alibaba.fastjson.JSON; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; + +/** + * 全局的工具类 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.0.0 + */ +public class GlobalAuthUtils { + private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; + private static final String HMAC_SHA1 = "HmacSHA1"; + private static final String HMAC_SHA_256 = "HmacSHA256"; + + /** + * 生成钉钉请求的Signature + * + * @param secretKey 平台应用的授权密钥 + * @param timestamp 时间戳 + * @return Signature + */ + public static String generateDingTalkSignature(String secretKey, String timestamp) { + byte[] signData = sign(secretKey.getBytes(DEFAULT_ENCODING), timestamp.getBytes(DEFAULT_ENCODING), HMAC_SHA_256); + return urlEncode(new String(Base64Utils.encode(signData, false))); + } + + /** + * 签名 + * + * @param key key + * @param data data + * @param algorithm algorithm + * @return byte[] + */ + private static byte[] sign(byte[] key, byte[] data, String algorithm) { + try { + Mac mac = Mac.getInstance(algorithm); + mac.init(new SecretKeySpec(key, algorithm)); + return mac.doFinal(data); + } catch (NoSuchAlgorithmException ex) { + throw new AuthException("Unsupported algorithm: " + algorithm, ex); + } catch (InvalidKeyException ex) { + throw new AuthException("Invalid key: " + Arrays.toString(key), ex); + } + } + + /** + * 编码 + * + * @param value str + * @return encode str + */ + public static String urlEncode(String value) { + if (value == null) { + return ""; + } + try { + String encoded = URLEncoder.encode(value, DEFAULT_ENCODING.displayName()); + return encoded.replace("+", "%20").replace("*", "%2A").replace("~", "%7E").replace("/", "%2F"); + } catch (UnsupportedEncodingException e) { + throw new AuthException("Failed To Encode Uri", e); + } + } + + + /** + * 解码 + * + * @param value str + * @return decode str + */ + public static String urlDecode(String value) { + if (value == null) { + return ""; + } + try { + return URLDecoder.decode(value, DEFAULT_ENCODING.displayName()); + } catch (UnsupportedEncodingException e) { + throw new AuthException("Failed To Decode Uri", e); + } + } + + /** + * string字符串转map,str格式为 {@code xxx=xxx&xxx=xxx} + * + * @param accessTokenStr 待转换的字符串 + * @return map + */ + public static Map parseStringToMap(String accessTokenStr) { + Map res = new HashMap<>(6); + if (accessTokenStr.contains("&")) { + String[] fields = accessTokenStr.split("&"); + for (String field : fields) { + if (field.contains("=")) { + String[] keyValue = field.split("="); + res.put(urlDecode(keyValue[0]), keyValue.length == 2 ? urlDecode(keyValue[1]) : null); + } + } + } + return res; + } + + /** + * map转字符串,转换后的字符串格式为 {@code xxx=xxx&xxx=xxx} + * + * @param params 待转换的map + * @param encode 是否转码 + * @return str + */ + public static String parseMapToString(Map params, boolean encode) { + if (null == params || params.isEmpty()) { + return ""; + } + List paramList = new ArrayList<>(); + params.forEach((k, v) -> { + if (null == v) { + paramList.add(k + "="); + } else { + paramList.add(k + "=" + (encode ? urlEncode(v) : v)); + } + }); + return String.join("&", paramList); + } + + /** + * 是否为http协议 + * + * @param url 待验证的url + * @return true: http协议, false: 非http协议 + */ + public static boolean isHttpProtocol(String url) { + if (StringUtils.isEmpty(url)) { + return false; + } + return url.startsWith("http://"); + } + + /** + * 是否为https协议 + * + * @param url 待验证的url + * @return true: https协议, false: 非https协议 + */ + public static boolean isHttpsProtocol(String url) { + if (StringUtils.isEmpty(url)) { + return false; + } + return url.startsWith("https://"); + } + + /** + * 是否为本地主机(域名) + * + * @param url 待验证的url + * @return true: 本地主机(域名), false: 非本地主机(域名) + */ + public static boolean isLocalHost(String url) { + return StringUtils.isEmpty(url) || url.contains("127.0.0.1") || url.contains("localhost"); + } + + + /** + * Generate nonce with given length + * + * @param len length + * @return nonce string + */ + public static String generateNonce(int len) { + String s = "0123456789QWERTYUIOPLKJHGFDSAZXCVBNMqwertyuioplkjhgfdsazxcvbnm"; + Random rng = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len; i++) { + int index = rng.nextInt(62); + sb.append(s, index, index + 1); + } + return sb.toString(); + } + + /** + * Get current timestamp + * + * @return timestamp string + */ + public static String getTimestamp() { + return String.valueOf(System.currentTimeMillis() / 1000); + } + + /** + * Generate Twitter signature + * https://developer.twitter.com/en/docs/basics/authentication/guides/creating-a-signature + * + * @param params parameters including: oauth headers, query params, body params + * @param method HTTP method + * @param baseUrl base url + * @param apiSecret api key secret can be found in the developer portal by viewing the app details page + * @param tokenSecret oauth token secret + * @return BASE64 encoded signature string + */ + public static String generateTwitterSignature(Map params, String method, String baseUrl, String apiSecret, String tokenSecret) { + TreeMap map = new TreeMap<>(params); + String str = parseMapToString(map, true); + String baseStr = method.toUpperCase() + "&" + urlEncode(baseUrl) + "&" + urlEncode(str); + String signKey = apiSecret + "&" + (StringUtils.isEmpty(tokenSecret) ? "" : tokenSecret); + byte[] signature = sign(signKey.getBytes(DEFAULT_ENCODING), baseStr.getBytes(DEFAULT_ENCODING), HMAC_SHA1); + + return new String(Base64Utils.encode(signature, false)); + } + + /** + * 生成饿了么请求的Signature + *

+ * 代码copy并修改自:https://coding.net/u/napos_openapi/p/eleme-openapi-java-sdk/git/blob/master/src/main/java/eleme/openapi/sdk/utils/SignatureUtil.java + * + * @param appKey 平台应用的授权key + * @param secret 平台应用的授权密钥 + * @param timestamp 时间戳,单位秒。API服务端允许客户端请求最大时间误差为正负5分钟。 + * @param action 饿了么请求的api方法 + * @param token 用户授权的token + * @param parameters 加密参数 + * @return Signature + */ + public static String generateElemeSignature(String appKey, String secret, long timestamp, String action, String token, Map parameters) { + final Map sorted = new TreeMap<>(parameters); + sorted.put("app_key", appKey); + sorted.put("timestamp", timestamp); + StringBuffer string = new StringBuffer(); + for (Map.Entry entry : sorted.entrySet()) { + string.append(entry.getKey()).append("=").append(JSON.toJSONString(entry.getValue())); + } + String splice = String.format("%s%s%s%s", action, token, string, secret); + String calculatedSignature = md5(splice); + return calculatedSignature.toUpperCase(); + } + + /** + * MD5加密 + *

+ * 代码copy并修改自:https://coding.net/u/napos_openapi/p/eleme-openapi-java-sdk/git/blob/master/src/main/java/eleme/openapi/sdk/utils/SignatureUtil.java + * + * @param str 待加密的字符串 + * @return md5 str + */ + public static String md5(String str) { + MessageDigest md = null; + StringBuilder buffer = null; + try { + md = MessageDigest.getInstance("MD5"); + md.update(str.getBytes(StandardCharsets.UTF_8)); + byte[] byteData = md.digest(); + buffer = new StringBuilder(); + for (byte byteDatum : byteData) { + buffer.append(Integer.toString((byteDatum & 0xff) + 0x100, 16).substring(1)); + } + } catch (Exception ignored) { + } + + return null == buffer ? "" : buffer.toString(); + } + + /** + * 生成京东宙斯平台的签名字符串 + * 宙斯签名规则过程如下: + * 将所有请求参数按照字母先后顺序排列,例如将access_token,app_key,method,timestamp,v 排序为access_token,app_key,method,timestamp,v + * 1.把所有参数名和参数值进行拼接,例如:access_tokenxxxapp_keyxxxmethodxxxxxxtimestampxxxxxxvx + * 2.把appSecret夹在字符串的两端,例如:appSecret+XXXX+appSecret + * 3.使用MD5进行加密,再转化成大写 + * link: http://open.jd.com/home/home#/doc/common?listId=890 + * link: https://github.com/pingjiang/jd-open-api-sdk-src/blob/master/src/main/java/com/jd/open/api/sdk/DefaultJdClient.java + * + * @param appSecret 京东应用密钥 + * @param params 签名参数 + * @return 签名后的字符串 + * @since 1.15.0 + */ + public static String generateJdSignature(String appSecret, Map params) { + Map treeMap = new TreeMap<>(params); + StringBuilder signBuilder = new StringBuilder(appSecret); + for (Map.Entry entry : treeMap.entrySet()) { + String name = entry.getKey(); + String value = String.valueOf(entry.getValue()); + if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) { + signBuilder.append(name).append(value); + } + } + signBuilder.append(appSecret); + return md5(signBuilder.toString()).toUpperCase(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/util/HttpUtils.java b/framework/src/main/java/cn/lili/modules/connect/util/HttpUtils.java new file mode 100644 index 00000000..e70a2b4e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/util/HttpUtils.java @@ -0,0 +1,108 @@ +package cn.lili.modules.connect.util; + +import com.xkcoding.http.HttpUtil; +import com.xkcoding.http.config.HttpConfig; +import com.xkcoding.http.support.HttpHeader; +import com.xkcoding.http.support.httpclient.HttpClientImpl; + +import java.util.Map; + +/** + * HttpUtil 工具,统一处理 http 请求,方便对 simple-http 做定制 + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @version 4.1 + * @since 1.0.0 + */ +public class HttpUtils { + + public HttpUtils(HttpConfig config) { + HttpUtil.setConfig(config); + HttpUtil.setHttp(new HttpClientImpl()); + } + + public HttpUtils() { + } + + + /** + * GET 请求 + * + * @param url URL + * @return 结果 + */ + public String get(String url) { + return HttpUtil.get(url); + } + + /** + * GET 请求 + * + * @param url URL + * @param params 参数 + * @param header 请求头 + * @param encode 是否需要 url encode + * @return 结果 + */ + public String get(String url, Map params, HttpHeader header, boolean encode) { + return HttpUtil.get(url, params, header, encode); + } + + /** + * POST 请求 + * + * @param url URL + * @return 结果 + */ + public String post(String url) { + return HttpUtil.post(url); + } + + /** + * POST 请求 + * + * @param url URL + * @param data JSON 参数 + * @return 结果 + */ + public String post(String url, String data) { + return HttpUtil.post(url, data); + } + + /** + * POST 请求 + * + * @param url URL + * @param data JSON 参数 + * @param header 请求头 + * @return 结果 + */ + public String post(String url, String data, HttpHeader header) { + return HttpUtil.post(url, data, header); + } + + /** + * POST 请求 + * + * @param url URL + * @param params form 参数 + * @param encode 是否需要 url encode + * @return 结果 + */ + public String post(String url, Map params, boolean encode) { + return HttpUtil.post(url, params, encode); + } + + /** + * POST 请求 + * + * @param url URL + * @param params form 参数 + * @param header 请求头 + * @param encode 是否需要 url encode + * @return 结果 + */ + public String post(String url, Map params, HttpHeader header, boolean encode) { + return HttpUtil.post(url, params, header, encode); + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/util/IpUtils.java b/framework/src/main/java/cn/lili/modules/connect/util/IpUtils.java new file mode 100644 index 00000000..35a9e3b6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/util/IpUtils.java @@ -0,0 +1,64 @@ +package cn.lili.modules.connect.util; + +import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * IpUtils + * + * @author Chopper + * @version v1.0 + * 2020-12-08 15:32 + */ +public class IpUtils { + + /** + * 获取本机IP + * + * @return ip + */ + public static String getLocalIp() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 获取客户端IP地址 + * + * @param request 请求 + * @return + */ + public static String getIpAddress(HttpServletRequest request) { + + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 + if (ip != null && ip.length() > 15) { + if (ip.indexOf(",") > 0) { + ip = ip.substring(0, ip.indexOf(",")); + } + } + if ("0:0:0:0:0:0:0:1".equals(ip)) { + ip = "127.0.0.1"; + } + return ip; + } + + + public static void main(String[] args) { + System.out.println(IpUtils.getLocalIp()); + } +} diff --git a/framework/src/main/java/cn/lili/modules/connect/util/UuidUtils.java b/framework/src/main/java/cn/lili/modules/connect/util/UuidUtils.java new file mode 100644 index 00000000..af9b2cdd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/connect/util/UuidUtils.java @@ -0,0 +1,65 @@ +package cn.lili.modules.connect.util; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 高性能的创建UUID的工具类,https://github.com/lets-mica/mica + * + * @author yadong.zhang (yadong.zhang0415(a)gmail.com) + * @since 1.9.3 + */ +public class UuidUtils { + + /** + * All possible chars for representing a number as a String + * copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/NumberUtil.java#L113 + */ + private final static byte[] DIGITS = { + '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z' + }; + + /** + * 生成uuid,采用 jdk 9 的形式,优化性能 + * copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L335 + *

+ * 关于mica uuid生成方式的压测结果,可以参考:https://github.com/lets-mica/mica-jmh/wiki/uuid + * + * @return UUID + */ + public static String getUUID() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + long lsb = random.nextLong(); + long msb = random.nextLong(); + byte[] buf = new byte[32]; + formatUnsignedLong(lsb, buf, 20, 12); + formatUnsignedLong(lsb >>> 48, buf, 16, 4); + formatUnsignedLong(msb, buf, 12, 4); + formatUnsignedLong(msb >>> 16, buf, 8, 4); + formatUnsignedLong(msb >>> 32, buf, 0, 8); + return new String(buf, StandardCharsets.UTF_8); + } + + /** + * copy from mica:https://github.com/lets-mica/mica/blob/master/mica-core/src/main/java/net/dreamlu/mica/core/utils/StringUtil.java#L348 + */ + private static void formatUnsignedLong(long val, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << 4; + int mask = radix - 1; + do { + buf[--charPos] = DIGITS[((int) val) & mask]; + val >>>= 4; + } while (charPos > offset); + } +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/dos/Distribution.java b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/Distribution.java new file mode 100644 index 00000000..a5a24a3d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/Distribution.java @@ -0,0 +1,67 @@ +package cn.lili.modules.distribution.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.distribution.entity.enums.DistributionStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 分销员对象 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Data +@Entity +@ApiModel(value = "分销员") +@TableName("li_distribution") +@Table(name = "li_distribution") +@NoArgsConstructor +public class Distribution extends BaseEntity { + + + private static final long serialVersionUID = -4878132663540847325L; + + public Distribution(String memberId, String memberName, String name, String idNumber) { + this.memberId = memberId; + this.memberName = memberName; + this.name = name; + this.idNumber = idNumber; + this.distributionStatus = DistributionStatusEnum.APPLY.name(); + } + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "会员姓名") + private String name; + + @ApiModelProperty(value = "身份证号") + private String idNumber; + + @ApiModelProperty(value = "分销总额") + private Double rebateTotal = 0D; + + @ApiModelProperty(value = "可提现金额") + private Double canRebate = 0D; + + @ApiModelProperty(value = "冻结金额") + private Double commissionFrozen = 0D; + + + /** + * @see DistributionStatusEnum + */ + @ApiModelProperty(value = "分销员状态", required = true) + private String distributionStatus; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionCash.java b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionCash.java new file mode 100644 index 00000000..9bfa342b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionCash.java @@ -0,0 +1,65 @@ +package cn.lili.modules.distribution.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.distribution.entity.enums.DistributionCashStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; + +/** + * 分销佣金 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Data +@Entity +@Table(name = "li_distribution_cash") +@TableName("li_distribution_cash") +@ApiModel(value = "分销佣金") +@NoArgsConstructor +@AllArgsConstructor +public class DistributionCash extends BaseEntity { + + + private static final long serialVersionUID = -233580160480894288L; + + @ApiModelProperty(value = "分销佣金sn") + private String sn; + + @ApiModelProperty(value = "分销员id") + private String distributionId; + + @ApiModelProperty(value = "分销员名称") + private String distributionName; + + @ApiModelProperty(value = "分销佣金") + private Double price; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "支付时间") + private Date payTime; + + @ApiModelProperty(value = "状态") + private String distributionCashStatus; + + public DistributionCash(String sn, String distributionId, Double price, String memberName) { + this.sn = sn; + this.distributionId = distributionId; + this.price = price; + this.distributionCashStatus = DistributionCashStatusEnum.APPLY.name(); + this.distributionName = memberName; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionGoods.java b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionGoods.java new file mode 100644 index 00000000..c967db20 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionGoods.java @@ -0,0 +1,123 @@ +package cn.lili.modules.distribution.entity.dos; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.Max; +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.Map; + +/** + * 分销商品 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Data +@Entity +@ApiModel(value = "分销商品") +@Table(name = "li_distribution_goods") +@TableName("li_distribution_goods") +@NoArgsConstructor +public class DistributionGoods { + + private static final long serialVersionUID = 1L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @NotNull(message = "商品ID不能为空") + @ApiModelProperty(value = "商品ID", required = true) + private String goodsId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @NotNull(message = "规格ID不能为空") + @ApiModelProperty(value = "规格ID", required = true) + private String skuId; + + @ApiModelProperty(value = "规格信息json", hidden = true) + @Column(columnDefinition = "TEXT") + @JsonIgnore + private String specs; + + @NotNull(message = "店铺ID不能为空") + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @NotNull(message = "店铺名称不能为空") + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @NotNull(message = "佣金金额") + @ApiModelProperty(value = "佣金金额", required = true) + private Double commission = 0D; + + @Max(value = 99999999, message = "价格不能超过99999999") + @ApiModelProperty(value = "商品价格") + private Double price; + + @ApiModelProperty(value = "缩略图路径") + private String thumbnail; + + @Max(value = 99999999, message = "库存不能超过99999999") + @ApiModelProperty(value = "库存") + private Integer quantity; + + public DistributionGoods(GoodsSku goodsSku, Double commission) { + this.goodsId = goodsSku.getGoodsId(); + this.goodsName = goodsSku.getGoodsName(); + this.skuId = goodsSku.getId(); + this.specs = ""; + JSONObject jsonObject = JSONUtil.parseObj(goodsSku.getSpecs()); + for (Map.Entry entry : jsonObject.entrySet()) { + if (!entry.getKey().equals("images")) { + this.specs = this.specs + entry.getKey() + ":" + entry.getValue() + " "; + } + } + + this.storeId = goodsSku.getStoreId(); + this.storeName = goodsSku.getStoreName(); + this.price = goodsSku.getPrice(); + this.thumbnail = goodsSku.getThumbnail(); + this.quantity = goodsSku.getQuantity(); + this.commission = commission; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionOrder.java b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionOrder.java new file mode 100644 index 00000000..85902f6b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionOrder.java @@ -0,0 +1,111 @@ +package cn.lili.modules.distribution.entity.dos; + +import cn.lili.modules.distribution.entity.enums.DistributionOrderStatusEnum; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 分销订单 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Data +@Entity +@ApiModel(value = "分销订单") +@TableName("li_distribution_order") +@Table(name = "li_distribution_order") +@NoArgsConstructor +public class DistributionOrder { + + private static final long serialVersionUID = 501799944909496507L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + /** + * @see DistributionOrderStatusEnum + */ + @ApiModelProperty(value = "分销订单状态") + private String distributionOrderStatus; + @ApiModelProperty(value = "购买会员的id") + private String memberId; + @ApiModelProperty(value = "购买会员的名称") + private String memberName; + @ApiModelProperty(value = "分销员id") + private String distributionId; + @ApiModelProperty(value = "分销员名称") + private String distributionName; + @ApiModelProperty(value = "解冻日期") + private Date settleCycle; + @ApiModelProperty(value = "提成金额") + private Double rebate; + @ApiModelProperty(value = "退款金额") + private Double sellBackRebate; + @ApiModelProperty(value = "店铺id") + private String storeId; + @ApiModelProperty(value = "店铺名称") + private String storeName; + @ApiModelProperty(value = "订单编号") + private String orderSn; + @ApiModelProperty(value = "子订单编号") + private String orderItemSn; + @ApiModelProperty(value = "商品ID") + private String goodsId; + @ApiModelProperty(value = "商品名称") + private String goodsName; + @ApiModelProperty(value = "货品ID") + private String skuId; + @ApiModelProperty(value = "规格") + @Column(columnDefinition = "TEXT") + private String specs; + @ApiModelProperty(value = "图片") + private String image; + @ApiModelProperty(value = "商品数量") + private Integer num; + + public DistributionOrder(StoreFlow storeFlow){ + distributionOrderStatus=DistributionOrderStatusEnum.WAIT_BILL.name(); + memberId=storeFlow.getMemberId(); + memberName=storeFlow.getMemberName(); + rebate=storeFlow.getDistributionRebate(); + storeId=storeFlow.getStoreId(); + storeName=storeFlow.getStoreName(); + orderSn=storeFlow.getOrderSn(); + orderItemSn=storeFlow.getOrderItemSn(); + goodsId=storeFlow.getGoodsId(); + goodsName=storeFlow.getGoodsName(); + skuId=storeFlow.getSkuId(); + specs=storeFlow.getSpecs(); + image=storeFlow.getImage(); + num= storeFlow.getNum(); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionSelectedGoods.java b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionSelectedGoods.java new file mode 100644 index 00000000..5174ad08 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/dos/DistributionSelectedGoods.java @@ -0,0 +1,48 @@ +package cn.lili.modules.distribution.entity.dos; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + * 分销员已选择分销商品 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Data +@Entity +@ApiModel(value = "分销商已选择分销商品") +@Table(name = "li_distribution_selected_goods") +@TableName("li_distribution_selected_goods") +@NoArgsConstructor +public class DistributionSelectedGoods { + + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @ApiModelProperty(value = "分销员ID") + private String distributionId; + + @ApiModelProperty(value = "分销员品ID") + private String distributionGoodsId; + + public DistributionSelectedGoods(String distributionId, String distributionGoodsId) { + this.distributionId = distributionId; + this.distributionGoodsId = distributionGoodsId; + } +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/dto/DistributionGoodsSearchParams.java b/framework/src/main/java/cn/lili/modules/distribution/entity/dto/DistributionGoodsSearchParams.java new file mode 100644 index 00000000..70cead14 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/dto/DistributionGoodsSearchParams.java @@ -0,0 +1,38 @@ +package cn.lili.modules.distribution.entity.dto; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 分销员商品查询条件 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Data +public class DistributionGoodsSearchParams extends PageVO { + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "是否已选择") + private boolean isChecked; + + + public QueryWrapper storeQueryWrapper() { + QueryWrapper queryWrapper = this.distributionQueryWrapper(); + queryWrapper.eq("dg.store_id", UserContext.getCurrentUser().getStoreId()); + return queryWrapper; + } + + public QueryWrapper distributionQueryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq(StringUtils.isNotEmpty(goodsName), "dg.goods_name", goodsName); + return queryWrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/dto/DistributionSearchParams.java b/framework/src/main/java/cn/lili/modules/distribution/entity/dto/DistributionSearchParams.java new file mode 100644 index 00000000..49d0b226 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/dto/DistributionSearchParams.java @@ -0,0 +1,29 @@ +package cn.lili.modules.distribution.entity.dto; + +import cn.lili.common.utils.StringUtils; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 分销查询参数 + * + * @author Chopper + * @date 2021/3/20 10:13 + */ +@Data +public class DistributionSearchParams { + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "分销员状态", allowableValues = "APPLY,RETREAT,REFUSE,PASS") + private String distributionStatus; + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.like(StringUtils.isNotEmpty(memberName), "member_name", memberName); + queryWrapper.eq(StringUtils.isNotEmpty(distributionStatus), "distribution_status", distributionStatus); + return queryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/enums/DistributionCashStatusEnum.java b/framework/src/main/java/cn/lili/modules/distribution/entity/enums/DistributionCashStatusEnum.java new file mode 100644 index 00000000..56e0a137 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/enums/DistributionCashStatusEnum.java @@ -0,0 +1,29 @@ +package cn.lili.modules.distribution.entity.enums; + +/** + * 分销佣金状态 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +public enum DistributionCashStatusEnum { + /** + * 待处理 + */ + APPLY("待处理"), + /** + * 通过 + */ + PASS("通过"), + /** + * 拒绝 + */ + REFUSE("拒绝"); + + + private final String description; + + DistributionCashStatusEnum(String description) { + this.description = description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/enums/DistributionOrderStatusEnum.java b/framework/src/main/java/cn/lili/modules/distribution/entity/enums/DistributionOrderStatusEnum.java new file mode 100644 index 00000000..dfc0266a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/enums/DistributionOrderStatusEnum.java @@ -0,0 +1,26 @@ +package cn.lili.modules.distribution.entity.enums; + +/** + * 分销员订单状态 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +public enum DistributionOrderStatusEnum { + //待结算(冻结) + WAIT_BILL("待结算"), + //待提现 + WAIT_CASH("待提现"), + //已提现 + COMPLETE_CASH("提现完成"), + //订单取消 + CANCEL("订单取消"), + //订单取消 + REFUND("退款"); + + private final String description; + + DistributionOrderStatusEnum(String description) { + this.description = description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/enums/DistributionStatusEnum.java b/framework/src/main/java/cn/lili/modules/distribution/entity/enums/DistributionStatusEnum.java new file mode 100644 index 00000000..fc02eab0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/enums/DistributionStatusEnum.java @@ -0,0 +1,32 @@ +package cn.lili.modules.distribution.entity.enums; + +/** + * 分销员状态 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +public enum DistributionStatusEnum { + /** + * 申请中 + */ + APPLY("申请中"), + /** + * 已清退 + */ + RETREAT("已清退"), + /** + * 审核拒绝 + */ + REFUSE("审核拒绝"), + /** + * 审核通过 + */ + PASS("审核通过"); + + private final String description; + + DistributionStatusEnum(String description) { + this.description = description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/vos/DistributionCashSearchParams.java b/framework/src/main/java/cn/lili/modules/distribution/entity/vos/DistributionCashSearchParams.java new file mode 100644 index 00000000..71302248 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/vos/DistributionCashSearchParams.java @@ -0,0 +1,50 @@ +package cn.lili.modules.distribution.entity.vos; + +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 分销佣金查询信息 + * + * @author pikachu + * @date 2020-03-26 09:04:53 + */ +@Data +public class DistributionCashSearchParams extends PageVO { + + /** + * 编号 + */ + @ApiModelProperty(value = "编号") + private String sn; + /** + * 会员名称 + */ + @ApiModelProperty(value = "会员名称") + private String memberName; + + /** + * 分销员提现状态 + */ + @ApiModelProperty(value = "分销员提现状态",allowableValues = "APPLY,PASS,REFUSE") + private String distributionCashStatus; + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StringUtils.isNotEmpty(memberName)) { + queryWrapper.like("distribution_name", memberName); + } + if (StringUtils.isNotEmpty(sn)) { + queryWrapper.like("sn", sn); + } + if (StringUtils.isNotEmpty(distributionCashStatus)) { + queryWrapper.like("distribution_cash_status", distributionCashStatus); + } + return queryWrapper; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/vos/DistributionGoodsVO.java b/framework/src/main/java/cn/lili/modules/distribution/entity/vos/DistributionGoodsVO.java new file mode 100644 index 00000000..73a075d9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/vos/DistributionGoodsVO.java @@ -0,0 +1,56 @@ +package cn.lili.modules.distribution.entity.vos; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import java.util.Date; + +/** + * 分销商品信息 + * + * @author pikachu + * @date 2020-03-26 09:04:53 + */ +@Data +public class DistributionGoodsVO { + + @ApiModelProperty(value = "分销商品ID") + private String id; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @Column(columnDefinition = "TEXT") + @ApiModelProperty(value = "规格") + private String specs; + + @ApiModelProperty(value = "库存") + private Integer quantity; + + @ApiModelProperty(value = "商品图片") + private String thumbnail; + + @ApiModelProperty(value = "商品价格") + private Double price; + + @ApiModelProperty(value = "商品编号") + private String sn; + + @ApiModelProperty(value = "规格ID") + private String skuId; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "佣金金额") + private Double commission; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "添加时间") + private Date createTime; + +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/entity/vos/DistributionOrderSearchParams.java b/framework/src/main/java/cn/lili/modules/distribution/entity/vos/DistributionOrderSearchParams.java new file mode 100644 index 00000000..9a4512e6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/entity/vos/DistributionOrderSearchParams.java @@ -0,0 +1,66 @@ +package cn.lili.modules.distribution.entity.vos; + +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 分销员对象 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Data +@ApiModel(value = "分销订单查询对象") +public class DistributionOrderSearchParams extends PageVO { + + private static final long serialVersionUID = -8736018687663645064L; + + @ApiModelProperty(value = "分销员名称") + private String distributionName; + + @ApiModelProperty(value = "订单sn") + private String orderSn; + + @ApiModelProperty(value = "分销员ID",hidden = true) + private String distributionId; + + @ApiModelProperty(value = "分销订单状态") + private String distributionOrderStatus; + + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "开始时间") + private Date startTime; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "结束时间") + private Date endTime; + + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.like(StringUtils.isNotBlank(distributionName), "distribution_name", distributionName); + queryWrapper.eq(StringUtils.isNotBlank(distributionOrderStatus), "distribution_order_status", distributionOrderStatus); + queryWrapper.eq(StringUtils.isNotBlank(orderSn), "order_sn", orderSn); + queryWrapper.eq(StringUtils.isNotBlank(StringUtils.toString(distributionId)), "distribution_id", distributionId); + queryWrapper.eq(StringUtils.isNotBlank(StringUtils.toString(storeId)), "store_id", storeId); + if (endTime != null && startTime != null) { + queryWrapper.between("create_time", startTime, endTime); + } + return queryWrapper; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionCashMapper.java b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionCashMapper.java new file mode 100644 index 00000000..ec21ecf1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionCashMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.distribution.mapper; + +import cn.lili.modules.distribution.entity.dos.DistributionCash; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 分销佣金数据处理层 + * + * @author pikachu + * @date 2020-03-26 18:45:56 + */ +public interface DistributionCashMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionGoodsMapper.java b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionGoodsMapper.java new file mode 100644 index 00000000..57e6d1a5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionGoodsMapper.java @@ -0,0 +1,29 @@ +package cn.lili.modules.distribution.mapper; + +import cn.lili.modules.distribution.entity.dos.DistributionGoods; +import cn.lili.modules.distribution.entity.vos.DistributionGoodsVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 分销商品数据处理层 + * + * @author pikachu + * @date 2020-03-24 23:04:56 + */ +public interface DistributionGoodsMapper extends BaseMapper { + + @Select("SELECT dg.* FROM li_distribution_goods dg WHERE dg.id NOT IN(SELECT distribution_goods_id FROM li_distribution_selected_goods WHERE distribution_id=${distributionId}) ${ew.customSqlSegment}") + IPage notSelectGoods(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper, String distributionId); + + @Select("SELECT dg.* FROM li_distribution_goods dg WHERE dg.id IN(SELECT distribution_goods_id FROM li_distribution_selected_goods WHERE distribution_id=${distributionId}) ${ew.customSqlSegment}") + IPage selectGoods(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper, String distributionId); + + @Select("SELECT dg.* FROM li_distribution_goods dg LEFT JOIN li_distribution_selected_goods dsg ON dg.id = dsg.distribution_goods_id ${ew.customSqlSegment}") + IPage getDistributionGoodsVO(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionMapper.java b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionMapper.java new file mode 100644 index 00000000..91617051 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionMapper.java @@ -0,0 +1,18 @@ +package cn.lili.modules.distribution.mapper; + +import cn.lili.modules.distribution.entity.dos.Distribution; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Update; + + +/** + * 分销员数据处理层 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +public interface DistributionMapper extends BaseMapper { + + @Update("UPDATE li_distribution set can_rebate = can_rebate+#{canRebate} WHERE id = #{distributionId}") + void updateCanRebate(Double canRebate,String distributionId); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionOrderMapper.java b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionOrderMapper.java new file mode 100644 index 00000000..02d8d8e1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionOrderMapper.java @@ -0,0 +1,24 @@ +package cn.lili.modules.distribution.mapper; + +import cn.hutool.core.date.DateTime; +import cn.lili.modules.distribution.entity.dos.DistributionOrder; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; + +/** + * 分销订单数据处理层 + * + * @author pikachu + * @date 2020-03-15 10:45:56 + */ +public interface DistributionOrderMapper extends BaseMapper { + + /** + * 分销提佣 + */ + @Select("UPDATE li_distribution AS d SET " + + "d.can_rebate =(can_rebate +(SELECT SUM( dorder.rebate ) FROM li_distribution_order AS dorder " + + "WHERE dorder.distribution_id = d.id AND dorder.distribution_order_status=#{distributionOrderStatus} AND dorder.settle_cycle<=#{settleCycle}))") + void rebate(String distributionOrderStatus, DateTime settleCycle); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionSelectedGoodsMapper.java b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionSelectedGoodsMapper.java new file mode 100644 index 00000000..e104a837 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/mapper/DistributionSelectedGoodsMapper.java @@ -0,0 +1,12 @@ +package cn.lili.modules.distribution.mapper; + +import cn.lili.modules.distribution.entity.dos.DistributionSelectedGoods; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +/** + * 选择分销商品数据处理层 + * + * @author pikachu + * @date 2020-03-15 10:45:56 + */ +public interface DistributionSelectedGoodsMapper extends BaseMapper { +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/service/DistributionCashService.java b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionCashService.java new file mode 100644 index 00000000..f7011a7d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionCashService.java @@ -0,0 +1,56 @@ +package cn.lili.modules.distribution.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.distribution.entity.dos.DistributionCash; +import cn.lili.modules.distribution.entity.vos.DistributionCashSearchParams; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * 分销佣金业务层 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +public interface DistributionCashService extends IService { + + /** + * 提交分销提现申请 + * + * @param applyMoney + */ + Boolean cash(Double applyMoney); + + /** + * 获取当前会员的分销提现分页列表 + * + * @return + */ + IPage getDistributionCash(PageVO page); + + /** + * 获取分销员提现分页列表 + * + * @param distributionCashSearchParams 搜索条件 + * @return 分销员提现分页列表 + */ + IPage getDistributionCash(DistributionCashSearchParams distributionCashSearchParams); + + /** + * 审核分销提现申请 + * + * @param id 分销提现申请ID + * @param result 处理结果 + * @return 分销提现申请 + */ + DistributionCash audit(@PathVariable String id, @RequestParam String result); + + /** + * 待处理分销员提现申请数量 + * + * @return 待处理分销员提现申请数量 + */ + Integer newDistributionCash(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/service/DistributionGoodsService.java b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionGoodsService.java new file mode 100644 index 00000000..868d35da --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionGoodsService.java @@ -0,0 +1,51 @@ +package cn.lili.modules.distribution.service; + +import cn.lili.modules.distribution.entity.dos.DistributionGoods; +import cn.lili.modules.distribution.entity.dto.DistributionGoodsSearchParams; +import cn.lili.modules.distribution.entity.vos.DistributionGoodsVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + + +/** + * 分销商品业务层 + * + * @author pikachu + * @date 2020-03-24 10:46:33 + */ +public interface DistributionGoodsService extends IService { + + /** + * 根据条件分页查询分销商品信息 + * + * @param distributionGoodsSearchParams 商品条件 + * @return + */ + IPage goodsPage(DistributionGoodsSearchParams distributionGoodsSearchParams); + + /** + * 获取分销商品 + * + * @param id 分销商品ID + * @return 分销商品 + */ + DistributionGoods distributionGoodsVO(String id); + + /** + * 获取分销商品 + * + * @param skuId SKUId + * @return 分销商品 + */ + DistributionGoods distributionGoodsVOBySkuId(String skuId); + + /** + * 选择分销商品 + * + * @param skuId SKU ID + * @param commission 佣金 + * @return + */ + DistributionGoods checked(String skuId, Double commission); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/service/DistributionOrderService.java b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionOrderService.java new file mode 100644 index 00000000..894773a6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionOrderService.java @@ -0,0 +1,48 @@ +package cn.lili.modules.distribution.service; + +import cn.lili.modules.distribution.entity.dos.DistributionOrder; +import cn.lili.modules.distribution.entity.vos.DistributionOrderSearchParams; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + + +/** + * 分销订单业务层 + * + * @author pikachu + * @date 2020-03-15 10:46:33 + */ +public interface DistributionOrderService extends IService { + + /** + * 获取分销订单分页 + * @param distributionOrderSearchParams 分销订单搜索参数 + * @return 分销订单分页 + */ + IPage getDistributionOrderPage(DistributionOrderSearchParams distributionOrderSearchParams); + + /** + * 支付订单 + * 记录分销订单 + * + * @param orderSn 订单编号 + */ + void payOrder(String orderSn); + + /** + * 取消订单 + * 记录分销订单 + * + * @param orderSn 订单编号 + */ + void cancelOrder(String orderSn); + + /** + * 订单退款 + * 记录分销订单 + * + * @param afterSaleSn 售后单号 + */ + void refundOrder(String afterSaleSn); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/service/DistributionSelectedGoodsService.java b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionSelectedGoodsService.java new file mode 100644 index 00000000..3912291d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionSelectedGoodsService.java @@ -0,0 +1,19 @@ +package cn.lili.modules.distribution.service; + +import cn.lili.modules.distribution.entity.dos.DistributionSelectedGoods; +import com.baomidou.mybatisplus.extension.service.IService; +/** + * 分销选择商品业务层 + * + * @author pikachu + * @date 2020-03-24 10:46:33 + */ +public interface DistributionSelectedGoodsService extends IService { + + /** + * 分销员添加分销商品 + * @param distributionGoodsId 分销商品ID + * @return + */ + boolean add(String distributionGoodsId); +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/service/DistributionService.java b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionService.java new file mode 100644 index 00000000..a43c1b77 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/service/DistributionService.java @@ -0,0 +1,85 @@ +package cn.lili.modules.distribution.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.distribution.entity.dos.Distribution; +import cn.lili.modules.distribution.entity.dto.DistributionSearchParams; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + + +/** + * 分销员业务层 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +public interface DistributionService extends IService { + + /** + * 获取分销员分页列表 + * + * @param distributionSearchParams 分销员 + * @param page 分页 + * @return + */ + IPage distributionPage(DistributionSearchParams distributionSearchParams, PageVO page); + + /** + * 获取当前登录的会员的分销员信息 + * + * @return + */ + Distribution getDistribution(); + + /** + * 提交分销申请 + * + * @return + */ + Distribution applyDistribution(String name, String idNumber); + + /** + * 审核分销申请 + * + * @param id 分销员ID + * @param status 审核状态 + * @return 操作状态 + */ + boolean audit(String id, String status); + + /** + * 清退分销员 + * + * @param id 分销员ID + * @return 操作状态 + */ + boolean retreat(String id); + + /** + * 恢复分销员 + * + * @param id 分销员ID + * @return 操作状态 + */ + boolean resume(String id); + + /** + * 绑定会员的分销员关系 + * + * @return + */ + void bindingDistribution(String distributionId); + + /** + * 检查分销设置开关 + */ + void checkDistributionSetting(); + + /** + * 修改可提现金额 + * @param canRebate 修改金额 + * @param distributionId 分销员ID + */ + void updateCanRebate(Double canRebate,String distributionId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionCashServiceImpl.java b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionCashServiceImpl.java new file mode 100644 index 00000000..bdde5e00 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionCashServiceImpl.java @@ -0,0 +1,163 @@ +package cn.lili.modules.distribution.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.MemberTagsEnum; +import cn.lili.common.rocketmq.tags.OtherTagsEnum; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.SnowFlake; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.distribution.entity.dos.Distribution; +import cn.lili.modules.distribution.entity.dos.DistributionCash; +import cn.lili.modules.distribution.entity.enums.DistributionCashStatusEnum; +import cn.lili.modules.distribution.entity.enums.DistributionStatusEnum; +import cn.lili.modules.distribution.entity.vos.DistributionCashSearchParams; +import cn.lili.modules.distribution.mapper.DistributionCashMapper; +import cn.lili.modules.distribution.service.DistributionCashService; +import cn.lili.modules.distribution.service.DistributionService; +import cn.lili.modules.member.entity.dto.MemberWithdrawalMessage; +import cn.lili.modules.member.entity.enums.MemberWithdrawalDestinationEnum; +import cn.lili.modules.member.service.MemberWalletService; +import cn.lili.modules.order.trade.entity.enums.DepositServiceTypeEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + + +/** + * 分销佣金业务层实现 + * + * @author pikachu + * @date 2020-03-126 18:04:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionCashServiceImpl extends ServiceImpl implements DistributionCashService { + //分销员 + private final DistributionService distributionService; + //会员余额 + private final MemberWalletService memberWalletService; + + private final RocketMQTemplate rocketMQTemplate; + + private final RocketmqCustomProperties rocketmqCustomProperties; + + @Override + public Boolean cash(Double applyMoney) { + + //检查分销功能开关 + distributionService.checkDistributionSetting(); + + //获取分销员 + Distribution distribution = distributionService.getDistribution(); + //如果未找到分销员或者分销员状态不是已通过则无法申请提现 + if (distribution != null && distribution.getDistributionStatus().equals(DistributionStatusEnum.PASS.name())) { + //校验分销佣金是否大于提现金额 + if (distribution.getCanRebate() < applyMoney) { + throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_INSUFFICIENT); + } + //将提现金额存入冻结金额,扣减可提现金额 + distribution.setCanRebate(CurrencyUtil.sub(distribution.getCanRebate(), applyMoney)); + distribution.setCommissionFrozen(CurrencyUtil.add(distribution.getCommissionFrozen(), applyMoney)); + distributionService.updateById(distribution); + //提现申请记录 + DistributionCash distributionCash = new DistributionCash("D" + SnowFlake.getId(), distribution.getId(), applyMoney, distribution.getMemberName()); + Boolean result = this.save(distributionCash); + if (result) { + //发送提现消息 + MemberWithdrawalMessage memberWithdrawalMessage = new MemberWithdrawalMessage(); + memberWithdrawalMessage.setMemberId(distribution.getMemberId()); + memberWithdrawalMessage.setPrice(applyMoney); + memberWithdrawalMessage.setDestination(MemberWithdrawalDestinationEnum.WALLET.name()); + String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_WITHDRAWAL.name(); + rocketMQTemplate.asyncSend(destination, memberWithdrawalMessage, RocketmqSendCallbackBuilder.commonCallback()); + return true; + } + return false; + + } + throw new ServiceException(ResultCode.DISTRIBUTION_NOT_EXIST); + + } + + @Override + public IPage getDistributionCash(PageVO page) { + Distribution distribution = distributionService.getDistribution(); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("distribution_id", distribution.getId()); + return this.page(PageUtil.initPage(page), queryWrapper); + } + + @Override + public IPage getDistributionCash(DistributionCashSearchParams distributionCashSearchParams) { + + return this.page(PageUtil.initPage(distributionCashSearchParams), distributionCashSearchParams.queryWrapper()); + } + + @Override + public DistributionCash audit(String id, String result) { + + //检查分销功能开关 + distributionService.checkDistributionSetting(); + + //获取分销佣金信息 + DistributionCash distributorCash = this.getById(id); + //只有分销员和分销佣金记录存在的情况才可以审核 + if (distributorCash != null) { + //获取分销员 + Distribution distribution = distributionService.getById(distributorCash.getDistributionId()); + if (distribution != null && distributorCash != null && distribution.getDistributionStatus().equals(DistributionStatusEnum.PASS.name())) { + //审核通过 + if (result.equals(DistributionCashStatusEnum.PASS.name())) { + //审核通过需要校验冻结金额不足情况 + if (distribution.getCommissionFrozen() < distributorCash.getPrice()) { + throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_INSUFFICIENT); + } + //分销员佣金解冻 + distribution.setCommissionFrozen(CurrencyUtil.sub(distribution.getCommissionFrozen(), distributorCash.getPrice())); + //分销记录操作 + distributorCash.setDistributionCashStatus(DistributionCashStatusEnum.PASS.name()); + distributorCash.setPayTime(new Date()); + //提现到余额 + memberWalletService.increase(distributorCash.getPrice(), distribution.getMemberId(), "分销佣金提现到余额", DepositServiceTypeEnum.WALLET_COMMISSION.name()); + } else { + //分销员佣金解冻 + distribution.setCommissionFrozen(CurrencyUtil.sub(distribution.getCommissionFrozen(), distributorCash.getPrice())); + //分销员可提现金额退回 + distribution.setCanRebate(CurrencyUtil.add(distribution.getCanRebate(), distributorCash.getPrice())); + distributorCash.setDistributionCashStatus(DistributionCashStatusEnum.REFUSE.name()); + } + //分销员金额相关处理 + distributionService.updateById(distribution); + //修改分销提现申请 + Boolean bool = this.updateById(distributorCash); + if (bool) { + return distributorCash; + } + throw new ServiceException(ResultCode.ERROR); + } + throw new ServiceException(ResultCode.DISTRIBUTION_NOT_EXIST); + } + throw new ServiceException(ResultCode.DISTRIBUTION_CASH_NOT_EXIST); + + } + + @Override + public Integer newDistributionCash() { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq("distribution_cash_status", DistributionCashStatusEnum.APPLY.name()); + return this.count(queryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionGoodsServiceImpl.java b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionGoodsServiceImpl.java new file mode 100644 index 00000000..2ccee635 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionGoodsServiceImpl.java @@ -0,0 +1,99 @@ +package cn.lili.modules.distribution.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.PageUtil; +import cn.lili.modules.distribution.entity.dos.Distribution; +import cn.lili.modules.distribution.entity.dos.DistributionGoods; +import cn.lili.modules.distribution.entity.dto.DistributionGoodsSearchParams; +import cn.lili.modules.distribution.entity.vos.DistributionGoodsVO; +import cn.lili.modules.distribution.mapper.DistributionGoodsMapper; +import cn.lili.modules.distribution.service.DistributionGoodsService; +import cn.lili.modules.distribution.service.DistributionService; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.service.GoodsSkuService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +/** + * 分销商品接口实现 + * + * @author pikachu + * @date 2020-03-24 23:04:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionGoodsServiceImpl extends ServiceImpl implements DistributionGoodsService { + + //分销商品 + private final DistributionGoodsMapper distributionGoodsMapper; + //分销员 + @Autowired + private DistributionService distributionService; + //规格商品 + @Autowired + private GoodsSkuService goodsSkuService; + + @Override + public IPage goodsPage(DistributionGoodsSearchParams searchParams) { + //获取商家的分销商品列表 + if (UserContext.getCurrentUser().getRole().equals(UserEnums.STORE)) { + return distributionGoodsMapper.getDistributionGoodsVO(PageUtil.initPage(searchParams), searchParams.storeQueryWrapper()); + } else if (UserContext.getCurrentUser().getRole().equals(UserEnums.MEMBER)) { + //判断当前登录用户是否为分销员 + Distribution distribution = distributionService.getDistribution(); + if (distribution != null) { + //判断查看已选择的分销商品列表 + if (searchParams.isChecked()) { + return distributionGoodsMapper.selectGoods(PageUtil.initPage(searchParams), searchParams.distributionQueryWrapper(), distribution.getId()); + } else { + return distributionGoodsMapper.notSelectGoods(PageUtil.initPage(searchParams), searchParams.distributionQueryWrapper(), distribution.getId()); + } + } + throw new ServiceException(ResultCode.DISTRIBUTION_NOT_EXIST); + } + //如果是平台则直接进行查询 + return distributionGoodsMapper.getDistributionGoodsVO(PageUtil.initPage(searchParams), searchParams.distributionQueryWrapper()); + } + + @Override + public DistributionGoods distributionGoodsVO(String id) { + + return this.getById(id); + } + + @Override + public DistributionGoods distributionGoodsVOBySkuId(String skuId) { + return this.getOne(new LambdaUpdateWrapper().eq(DistributionGoods::getSkuId,skuId)); + } + + @Override + public DistributionGoods checked(String skuId, Double commission) { + + //检查分销功能开关 + distributionService.checkDistributionSetting(); + + //判断是否存在分销商品,如果存在不能添加 + QueryWrapper queryWrapper = Wrappers.query().eq("sku_id", skuId); + + if (this.getOne(queryWrapper) != null) { + throw new ServiceException(ResultCode.DISTRIBUTION_GOODS_DOUBLE); + } + GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(skuId); + DistributionGoods distributionGoods = new DistributionGoods(goodsSku, commission); + this.save(distributionGoods); + return distributionGoods; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionOrderServiceImpl.java b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionOrderServiceImpl.java new file mode 100644 index 00000000..aebcf034 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionOrderServiceImpl.java @@ -0,0 +1,131 @@ +package cn.lili.modules.distribution.serviceimpl; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.json.JSONUtil; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.modules.distribution.entity.dos.Distribution; +import cn.lili.modules.distribution.entity.dos.DistributionOrder; +import cn.lili.modules.distribution.entity.enums.DistributionOrderStatusEnum; +import cn.lili.modules.distribution.entity.vos.DistributionOrderSearchParams; +import cn.lili.modules.distribution.mapper.DistributionOrderMapper; +import cn.lili.modules.distribution.service.DistributionOrderService; +import cn.lili.modules.distribution.service.DistributionService; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.order.order.service.StoreFlowService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.DistributionSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +/** + * 分销订单接口实现 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionOrderServiceImpl extends ServiceImpl implements DistributionOrderService { + + //订单 + private final OrderService orderService; + //店铺流水 + private final StoreFlowService storeFlowService; + //分销员 + private final DistributionService distributionService; + //系统设置 + private final SettingService settingService; + + @Override + public IPage getDistributionOrderPage(DistributionOrderSearchParams distributionOrderSearchParams) { + return this.page(PageUtil.initPage(distributionOrderSearchParams), distributionOrderSearchParams.queryWrapper()); + + } + + @Override + public void payOrder(String orderSn) { + + //根据订单编号获取订单数据 + Order order = orderService.getBySn(orderSn); + + //判断是否为分销订单,如果为分销订单则获取分销佣金 + if (order.getDistributionId() != null) { + + //根据订单编号获取有分销金额的店铺流水记录 + List storeFlowList = storeFlowService.list(new LambdaQueryWrapper() + .eq(StoreFlow::getOrderSn, orderSn) + .isNotNull(StoreFlow::getDistributionRebate)); + for (StoreFlow storeFlow : storeFlowList) { + DistributionOrder distributionOrder = new DistributionOrder(storeFlow); + distributionOrder.setDistributionId(order.getDistributionId()); + + //分销员信息 + Distribution distribution = distributionService.getById(order.getDistributionId()); + distributionOrder.setDistributionName(distribution.getMemberName()); + + //设置结算天数(解冻日期) + Setting setting = settingService.get(SettingEnum.DISTRIBUTION_SETTING.name()); + DistributionSetting distributionSetting = JSONUtil.toBean(setting.getSettingValue(), DistributionSetting.class); + DateTime dateTime = new DateTime(); + // dateTime.offsetNew(DateField.DAY_OF_MONTH,distributionSetting.getCashDay()); + dateTime.offsetNew(DateField.DAY_OF_MONTH, 1); + distributionOrder.setSettleCycle(dateTime); + this.save(distributionOrder); + } + } + } + + @Override + public void cancelOrder(String orderSn) { + this.update(new LambdaUpdateWrapper().eq(DistributionOrder::getOrderSn, orderSn) + .set(DistributionOrder::getDistributionOrderStatus, DistributionOrderStatusEnum.CANCEL.name())); + } + + @Override + public void refundOrder(String afterSaleSn) { + //判断是否为分销订单 + StoreFlow storeFlow = storeFlowService.getOne(new LambdaQueryWrapper() + .eq(StoreFlow::getRefundSn, afterSaleSn) + .isNotNull(StoreFlow::getDistributionRebate)); + if (storeFlow != null) { + + //获取收款分销订单 + DistributionOrder distributionOrder = this.getOne(new LambdaQueryWrapper() + .eq(DistributionOrder::getOrderItemSn, storeFlow.getOrderItemSn())); + + //已提交无法重复提交 + //如果未结算则将分销订单取消 + //如果已结算则创建退款分销订单 + if(distributionOrder.getDistributionOrderStatus().equals(DistributionOrderStatusEnum.CANCEL.name())){ + return ; + } else if(distributionOrder.getDistributionOrderStatus().equals(DistributionOrderStatusEnum.WAIT_BILL.name())){ + this.update(new LambdaUpdateWrapper().eq(DistributionOrder::getOrderItemSn, storeFlow.getOrderItemSn()) + .set(DistributionOrder::getDistributionOrderStatus, DistributionOrderStatusEnum.CANCEL.name())); + }else{ + //创建分销退款订单 + DistributionOrder backDistributionOrder = new DistributionOrder(); + + this.save(backDistributionOrder); + //修改分销员提成金额 + distributionService.updateCanRebate(CurrencyUtil.sub(0,storeFlow.getDistributionRebate()),distributionOrder.getDistributionId()); + } + } + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionSelectedGoodsServiceImpl.java b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionSelectedGoodsServiceImpl.java new file mode 100644 index 00000000..4abafd47 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionSelectedGoodsServiceImpl.java @@ -0,0 +1,35 @@ +package cn.lili.modules.distribution.serviceimpl; + +import cn.lili.modules.distribution.entity.dos.DistributionSelectedGoods; +import cn.lili.modules.distribution.mapper.DistributionSelectedGoodsMapper; +import cn.lili.modules.distribution.service.DistributionSelectedGoodsService; +import cn.lili.modules.distribution.service.DistributionService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 分销选择商品接口实现 + * + * @author pikachu + * @date 2020-03-24 23:04:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionSelectedGoodsServiceImpl extends ServiceImpl implements DistributionSelectedGoodsService { + + //分销员 + private final DistributionService distributionService; + @Override + public boolean add(String distributionGoodsId) { + //检查分销功能开关 + distributionService.checkDistributionSetting(); + + String distributionId=distributionService.getDistribution().getId(); + DistributionSelectedGoods distributionSelectedGoods=new DistributionSelectedGoods(distributionId,distributionGoodsId); + return this.save(distributionSelectedGoods); + } +} diff --git a/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionServiceImpl.java b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionServiceImpl.java new file mode 100644 index 00000000..37ecff55 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/distribution/serviceimpl/DistributionServiceImpl.java @@ -0,0 +1,178 @@ +package cn.lili.modules.distribution.serviceimpl; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.distribution.entity.dos.Distribution; +import cn.lili.modules.distribution.entity.dto.DistributionSearchParams; +import cn.lili.modules.distribution.entity.enums.DistributionStatusEnum; +import cn.lili.modules.distribution.mapper.DistributionMapper; +import cn.lili.modules.distribution.service.DistributionService; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.DistributionSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +/** + * 分销员接口实现 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionServiceImpl extends ServiceImpl implements DistributionService { + + //会员 + @Autowired + private MemberService memberService; + //分销员 + private final DistributionMapper distributionMapper; + //缓存 + private final Cache cache; + //设置 + private final SettingService settingService; + + @Override + public IPage distributionPage(DistributionSearchParams distributionSearchParams, PageVO page) { + return this.page(PageUtil.initPage(page), distributionSearchParams.queryWrapper()); + } + + @Override + public Distribution getDistribution() { + + return this.getOne(new LambdaQueryWrapper().eq(Distribution::getMemberId, UserContext.getCurrentUser().getId())); + + } + + @Override + public Distribution applyDistribution(String name, String idNumber) { + + //检查分销开关 + checkDistributionSetting(); + + //判断用户是否申请过分销 + Distribution distribution=getDistribution(); + + //如果分销员非空并未审核则提示用户请等待,如果分销员为拒绝状态则重新提交申请 + if(Optional.ofNullable(distribution).isPresent()){ + if(distribution.getDistributionStatus().equals(DistributionStatusEnum.APPLY.name())){ + throw new ServiceException(ResultCode.DISTRIBUTION_IS_APPLY); + }else if(distribution.getDistributionStatus().equals(DistributionStatusEnum.REFUSE.name())){ + distribution.setDistributionStatus(DistributionStatusEnum.APPLY.name()); + distribution.setName(name); + distribution.setIdNumber(idNumber); + this.updateById(distribution); + return distribution; + } + } + //如果未申请分销员则新增进行申请 + //获取当前登录用户 + Member member = memberService.getUserInfo(); + //新建分销员 + distribution = new Distribution(member.getId(), member.getUsername(), name, idNumber); + //添加分销员 + this.save(distribution); + + return distribution; + } + + @Override + public boolean audit(String id, String status) { + + //检查分销开关 + checkDistributionSetting(); + + //根据id获取分销员 + Distribution distribution = this.getById(id); + if (Optional.ofNullable(distribution).isPresent()) { + if (status.equals(DistributionStatusEnum.PASS.name())) { + distribution.setDistributionStatus(DistributionStatusEnum.PASS.name()); + } else { + distribution.setDistributionStatus(DistributionStatusEnum.REFUSE.name()); + } + return this.updateById(distribution); + } + return false; + } + + @Override + public boolean retreat(String id) { + + //检查分销开关 + checkDistributionSetting(); + + //根据id获取分销员 + Distribution distribution = this.getById(id); + if (Optional.ofNullable(distribution).isPresent()) { + distribution.setDistributionStatus(DistributionStatusEnum.RETREAT.name()); + return this.updateById(distribution); + } + return false; + } + + @Override + public boolean resume(String id) { + + //检查分销开关 + checkDistributionSetting(); + + //根据id获取分销员 + Distribution distribution = this.getById(id); + if (Optional.ofNullable(distribution).isPresent()) { + distribution.setDistributionStatus(DistributionStatusEnum.PASS.name()); + return this.updateById(distribution); + } + return false; + } + + @Override + public void bindingDistribution(String distributionId) { + //储存分销关系为3天 + Distribution distribution = this.getById(distributionId); + if (distribution!=null) { + Setting setting = settingService.get(SettingEnum.DISTRIBUTION_SETTING.name()); + DistributionSetting distributionSetting = JSONUtil.toBean(setting.getSettingValue(), DistributionSetting.class); + //cache.put(CachePrefix.DISTRIBUTION.getPrefix() + "_" + UserContext.getCurrentUser().getId(), distribution.getId(), distributionSetting.getDistributionDay().longValue(), TimeUnit.DAYS); + cache.put(CachePrefix.DISTRIBUTION.getPrefix() + "_" + UserContext.getCurrentUser().getId(), distribution.getId(), 3L, TimeUnit.DAYS); + } + + } + + /** + * 检查分销设置开关 + */ + @Override + public void checkDistributionSetting(){ + //获取分销是否开启 + Setting setting = settingService.get(SettingEnum.DISTRIBUTION_SETTING.name()); + DistributionSetting distributionSetting = JSONUtil.toBean(setting.getSettingValue(), DistributionSetting.class); + if(!distributionSetting.getIsOpen()){ + throw new ServiceException(ResultCode.DISTRIBUTION_CLOSE); + } + } + + @Override + public void updateCanRebate(Double canRebate, String distributionId) { + this.distributionMapper.updateCanRebate(canRebate,distributionId); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/file/entity/File.java b/framework/src/main/java/cn/lili/modules/file/entity/File.java new file mode 100644 index 00000000..5ad4ad11 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/file/entity/File.java @@ -0,0 +1,50 @@ +package cn.lili.modules.file.entity; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 文件系统 + * + * @author Chopper + * @date 2020/11/26 15:35 + */ +@Data +@Entity +@Table(name = "li_file") +@TableName("li_file") +@ApiModel(value = "文件") +public class File extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "原文件名") + private String name; + + @ApiModelProperty(value = "存储文件名") + private String fileKey; + + @ApiModelProperty(value = "大小") + @JsonSerialize(using = ToStringSerializer.class) + private Long fileSize; + + @ApiModelProperty(value = "文件类型") + private String fileType; + + @ApiModelProperty(value = "路径") + private String url; + + @ApiModelProperty(value = "拥有者id") + private String ownerId; + + @ApiModelProperty(value = "用户类型") + private String userEnums; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/file/entity/dto/FileOwnerDTO.java b/framework/src/main/java/cn/lili/modules/file/entity/dto/FileOwnerDTO.java new file mode 100644 index 00000000..cf2cbefc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/file/entity/dto/FileOwnerDTO.java @@ -0,0 +1,25 @@ +package cn.lili.modules.file.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 文件查询所属者参数对象 + * + * @author Chopper + * @date 2021-02-22 17:20 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class FileOwnerDTO { + + @ApiModelProperty(value = "拥有者id") + private String ownerId; + + @ApiModelProperty(value = "用户类型") + private String userEnums; + +} diff --git a/framework/src/main/java/cn/lili/modules/file/mapper/FileMapper.java b/framework/src/main/java/cn/lili/modules/file/mapper/FileMapper.java new file mode 100644 index 00000000..b16e8862 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/file/mapper/FileMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.file.mapper; + +import cn.lili.modules.file.entity.File; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 文件管理数据处理层 + * + * @author Chopper + * @date 2021-02-22 17:20 + */ +public interface FileMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/file/plugin/FileManagerPlugin.java b/framework/src/main/java/cn/lili/modules/file/plugin/FileManagerPlugin.java new file mode 100644 index 00000000..f16e90fd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/file/plugin/FileManagerPlugin.java @@ -0,0 +1,50 @@ +package cn.lili.modules.file.plugin; + +import java.io.InputStream; +import java.util.List; + +/** + * 文件管理插件 + * + * @author Chopper + * @date 2020/11/26 17:50 + */ +public interface FileManagerPlugin { + + + /** + * 文件路径上传 + * + * @param filePath + * @param key + * @return + */ + String pathUpload(String filePath, String key); + + /** + * 文件流上传 + * + * @param inputStream + * @param key + * @return + */ + String inputStreamUpload(InputStream inputStream, String key); + + + /** + * 删除文件 + * + * @param key + */ + void deleteFile(List key); + + /** + * 根据原图生成规定尺寸的图片 + * + * @param url 连接 + * @param width 宽 + * @param height 高 + * @return + */ + String getUrl(String url, Integer width, Integer height); +} diff --git a/framework/src/main/java/cn/lili/modules/file/plugin/impl/AliFileManagerPlugin.java b/framework/src/main/java/cn/lili/modules/file/plugin/impl/AliFileManagerPlugin.java new file mode 100644 index 00000000..cc189f17 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/file/plugin/impl/AliFileManagerPlugin.java @@ -0,0 +1,193 @@ +package cn.lili.modules.file.plugin.impl; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.file.plugin.FileManagerPlugin; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.OssSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.aliyun.oss.ClientException; +import com.aliyun.oss.OSS; +import com.aliyun.oss.OSSClientBuilder; +import com.aliyun.oss.OSSException; +import com.aliyun.oss.model.DeleteObjectsRequest; +import com.aliyun.oss.model.ObjectMetadata; +import com.google.gson.Gson; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.InputStream; +import java.util.List; + +/** + * 阿里oss 文件操作 + * + * @author Chopper + * @date 2020/11/26 17:50 + */ + +@Component +@Slf4j +public class AliFileManagerPlugin implements FileManagerPlugin { + + @Autowired + private SettingService settingService; + + /** + * 下一个初始化配置参数的时间 + * 这里为了防止多次调用redis,减少与redis的交互时间 + */ + private static Long nextInitSetting; + + /** + * 暂时设定3分账请求一次设置 + */ + private static final Long interval = 60 * 3 * 1000L; + + /** + * 静态设置,最快三分钟更新一次 + */ + private static OssSetting ossSetting; + + /** + * 获取oss client + * + * @return + */ + private OSS getOssClient() { + OssSetting ossSetting = getSetting(); + + return new OSSClientBuilder().build( + ossSetting.getEndPoint(), + ossSetting.getAccessKeyId(), + ossSetting.getAccessKeySecret()); + } + + /** + * 获取配置 + * + * @return + */ + private OssSetting getSetting() { + //如果没有配置,或者没有下次刷新时间,或者下次刷新时间小于当前时间,则从redis 更新一次 + if (ossSetting == null || nextInitSetting == null || nextInitSetting < System.currentTimeMillis()) { + Setting setting = settingService.getById(SettingEnum.OSS_SETTING.name()); + if (setting == null || StrUtil.isBlank(setting.getSettingValue())) { + throw new ServiceException("您还未配置阿里云OSS存储"); + } + nextInitSetting = System.currentTimeMillis() + interval; + ossSetting = new Gson().fromJson(setting.getSettingValue(), OssSetting.class); + return ossSetting; + } + return ossSetting; + } + + /** + * 获取配置前缀 + * + * @return + */ + private String getUrlPrefix() { + OssSetting ossSetting = getSetting(); + return "https://" + ossSetting.getBucketName() + "." + ossSetting.getEndPoint() + "/"; + } + + @Override + public String pathUpload(String filePath, String key) { + OSS ossClient = getOssClient(); + try { + ossClient.putObject(ossSetting.getBucketName(), key, new File(filePath)); + } catch (OSSException oe) { + log.error("Caught an OSSException, which means your request made it to OSS, " + + "but was rejected with an error response for some reason."); + log.error("Error Message: " + oe.getErrorMessage()); + log.error("Error Code: " + oe.getErrorCode()); + log.error("Request ID: " + oe.getRequestId()); + log.error("Host ID: " + oe.getHostId()); + throw new ServiceException("图片上传失败" + oe.getErrorMessage()); + } catch (ClientException ce) { + log.error("Caught an ClientException, which means the client encountered " + + "a serious internal problem while trying to communicate with OSS, " + + "such as not being able to access the network."); + log.error("Error Message: " + ce.getMessage()); + throw new ServiceException("图片上传失败" + ce.getErrorMessage()); + } finally { + /* + * Do not forget to shut down the client finally to release all allocated resources. + */ + ossClient.shutdown(); + } + ossClient.shutdown(); + return getUrlPrefix() + key; + } + + @Override + public String inputStreamUpload(InputStream inputStream, String key) { + OSS ossClient = getOssClient(); + try { + ObjectMetadata meta = new ObjectMetadata(); + meta.setContentType("image/jpg"); + ossClient.putObject(getSetting().getBucketName(), key, inputStream, meta); + } catch (OSSException oe) { + log.error("Caught an OSSException, which means your request made it to OSS, " + + "but was rejected with an error response for some reason."); + log.error("Error Message: " + oe.getErrorMessage()); + log.error("Error Code: " + oe.getErrorCode()); + log.error("Request ID: " + oe.getRequestId()); + log.error("Host ID: " + oe.getHostId()); + throw new ServiceException("图片上传失败" + oe.getErrorMessage()); + } catch (ClientException ce) { + log.error("Caught an ClientException, which means the client encountered " + + "a serious internal problem while trying to communicate with OSS, " + + "such as not being able to access the network."); + log.error("Error Message: " + ce.getMessage()); + throw new ServiceException("图片上传失败" + ce.getErrorMessage()); + } finally { + /* + * Do not forget to shut down the client finally to release all allocated resources. + */ + ossClient.shutdown(); + } + ossClient.shutdown(); + return getUrlPrefix() + key; + } + + @Override + public void deleteFile(List key) { + OSS ossClient = getOssClient(); + + try { + ossClient.deleteObjects( + new DeleteObjectsRequest(getSetting().getBucketName()).withKeys(key)); + } catch (OSSException oe) { + log.error("Caught an OSSException, which means your request made it to OSS, " + + "but was rejected with an error response for some reason."); + log.error("Error Message: " + oe.getErrorMessage()); + log.error("Error Code: " + oe.getErrorCode()); + log.error("Request ID: " + oe.getRequestId()); + log.error("Host ID: " + oe.getHostId()); + throw new ServiceException("图片删除失败" + oe.getErrorMessage()); + } catch (ClientException ce) { + log.error("Caught an ClientException, which means the client encountered " + + "a serious internal problem while trying to communicate with OSS, " + + "such as not being able to access the network."); + log.error("Error Message: " + ce.getMessage()); + throw new ServiceException("图片删除失败" + ce.getErrorMessage()); + } finally { + /* + * Do not forget to shut down the client finally to release all allocated resources. + */ + ossClient.shutdown(); + } + } + + @Override + public String getUrl(String url, Integer width, Integer height) { + // 缩略图全路径 + // 返回缩略图全路径 + return url + "?x-oss-process=style/" + width + "X" + height; + } +} diff --git a/framework/src/main/java/cn/lili/modules/file/service/FileService.java b/framework/src/main/java/cn/lili/modules/file/service/FileService.java new file mode 100644 index 00000000..7668510e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/file/service/FileService.java @@ -0,0 +1,57 @@ +package cn.lili.modules.file.service; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.file.entity.File; +import cn.lili.modules.file.entity.dto.FileOwnerDTO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 文件管理业务层 + * + * @author Chopper + * @date 2020/11/26 17:50 + */ +public interface FileService extends IService { + + + /** + * 批量删除 + * + * @param ids + */ + void batchDelete(List ids); + + /** + * 所有者批量删除 + * + * @param ids + */ + void batchDelete(List ids, AuthUser authUser); + + + /** + * 自定义搜索分页 + * + * @param file + * @param searchVO + * @param pageVo + * @return + */ + IPage customerPage(File file, SearchVO searchVO, PageVO pageVo); + + /** + * 所属文件数据查询 + * + * @param file + * @param searchVO + * @param pageVo + * @return + */ + IPage customerPageOwner(FileOwnerDTO ownerDTO, File file, SearchVO searchVO, PageVO pageVo); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/file/serviceimpl/FileServiceImpl.java b/framework/src/main/java/cn/lili/modules/file/serviceimpl/FileServiceImpl.java new file mode 100644 index 00000000..f56fb0e4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/file/serviceimpl/FileServiceImpl.java @@ -0,0 +1,103 @@ +package cn.lili.modules.file.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.file.entity.File; +import cn.lili.modules.file.entity.dto.FileOwnerDTO; +import cn.lili.modules.file.mapper.FileMapper; +import cn.lili.modules.file.plugin.FileManagerPlugin; +import cn.lili.modules.file.service.FileService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 文件管理业务层实现 + * + * @author Chopper + * @date 2020/11/26 17:50 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FileServiceImpl extends ServiceImpl implements FileService { + + private final FileManagerPlugin fileManagerPlugin; + + @Override + public void batchDelete(List ids) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + queryWrapper.in(File::getId, ids); + + List files = this.list(queryWrapper); + List keys = new ArrayList<>(); + files.forEach(item -> keys.add(item.getFileKey())); + fileManagerPlugin.deleteFile(keys); + this.remove(queryWrapper); + } + + @Override + public void batchDelete(List ids, AuthUser authUser) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + queryWrapper.in(File::getId, ids); + + queryWrapper.eq(File::getUserEnums, authUser.getRole().name()); + //操作图片属性判定 + switch (authUser.getRole()) { + case MEMBER: + queryWrapper.eq(File::getOwnerId, authUser.getId()); + break; + case STORE: + queryWrapper.eq(File::getOwnerId, authUser.getStoreId()); + break; + case MANAGER: + break; + default: + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + List files = this.list(queryWrapper); + List keys = new ArrayList<>(); + files.forEach(item -> keys.add(item.getFileKey())); + fileManagerPlugin.deleteFile(keys); + this.remove(queryWrapper); + } + + @Override + public IPage customerPage(File file, SearchVO searchVO, PageVO pageVo) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.like(StringUtils.isNotEmpty(file.getName()), File::getName, file.getName()) + .like(StringUtils.isNotEmpty(file.getFileKey()), File::getFileKey, file.getFileKey()) + .like(StringUtils.isNotEmpty(file.getFileType()), File::getFileType, file.getFileType()) + .between(StringUtils.isNotEmpty(searchVO.getStartDate()) && StringUtils.isNotEmpty(searchVO.getEndDate()), + File::getCreateTime, searchVO.getStartDate(), searchVO.getEndDate()); + IPage page = this.page(PageUtil.initPage(pageVo), queryWrapper); + return page; + } + + @Override + public IPage customerPageOwner(FileOwnerDTO ownerDTO, File file, SearchVO searchVO, PageVO pageVo) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(StringUtils.isNotEmpty(ownerDTO.getOwnerId()), File::getOwnerId, ownerDTO.getOwnerId()) + .eq(File::getUserEnums, ownerDTO.getUserEnums()) + .like(StringUtils.isNotEmpty(file.getName()), File::getName, file.getName()) + .like(StringUtils.isNotEmpty(file.getFileKey()), File::getFileKey, file.getFileKey()) + .like(StringUtils.isNotEmpty(file.getFileType()), File::getFileType, file.getFileType()) + .between(StringUtils.isNotEmpty(searchVO.getStartDate()) && StringUtils.isNotEmpty(searchVO.getEndDate()), + File::getCreateTime, searchVO.getStartDate(), searchVO.getEndDate()); + IPage page = this.page(PageUtil.initPage(pageVo), queryWrapper); + return page; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/Brand.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Brand.java new file mode 100644 index 00000000..934c0493 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Brand.java @@ -0,0 +1,41 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; + +/** + * 商品品牌 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@Data +@Entity +@Table(name = "li_brand") +@TableName("li_brand") +@ApiModel(value = "商品品牌") +public class Brand extends BaseEntity { + + + private static final long serialVersionUID = -8236865838438521426L; + /** + * 品牌名称 + */ + @NotEmpty(message = "品牌名称不能为空") + @ApiModelProperty(value = "品牌名称", required = true) + private String name; + /** + * 品牌图标 + */ + @NotEmpty(message = "品牌图标不能为空") + @ApiModelProperty(value = "品牌图标", required = true) + private String logo; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/Category.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Category.java new file mode 100644 index 00000000..b4f4d4f1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Category.java @@ -0,0 +1,76 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 商品分类 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@Data +@Entity +@Table(name = "li_category") +@TableName("li_category") +@ApiModel(value = "商品分类") +@AllArgsConstructor +@NoArgsConstructor +public class Category extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @NotEmpty(message = "分类名称不能为空") + @ApiModelProperty(value = "分类名称") + private String name; + + @ApiModelProperty(value = "父id, 根节点为0") + private String parentId; + + @ApiModelProperty(value = "层级, 从0开始") + private Integer level; + + @ApiModelProperty(value = "排序值") + private BigDecimal sortOrder; + + @ApiModelProperty(value = "佣金比例") + private Double commissionRate; + + @ApiModelProperty(value = "分类图标") + private String image; + + @ApiModelProperty(value = "是否支持频道") + private Boolean supportChannel; + + public Category(String id, String createBy, Date createTime, String updateBy, Date updateTime, Boolean deleteFlag, String name, String parentId, Integer level, BigDecimal sortOrder, Double commissionRate, String image, Boolean supportChannel) { + super(id, createBy, createTime, updateBy, updateTime, deleteFlag); + this.name = name; + this.parentId = parentId; + this.level = level; + this.sortOrder = sortOrder; + this.commissionRate = commissionRate; + this.image = image; + this.supportChannel = supportChannel; + } + + public Category(String id, String name, String parentId, Integer level, BigDecimal sortOrder, Double commissionRate, String image, Boolean supportChannel) { + this.name = name; + this.parentId = parentId; + this.level = level; + this.sortOrder = sortOrder; + this.commissionRate = commissionRate; + this.image = image; + this.supportChannel = supportChannel; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/CategoryBrand.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/CategoryBrand.java new file mode 100644 index 00000000..5ac1f8c1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/CategoryBrand.java @@ -0,0 +1,77 @@ +package cn.lili.modules.goods.entity.dos; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + + +/** + * 分类品牌关联 + * + * @author pikachu + * @date 2020-03-02 09:34:02 + */ +@Data +@Entity +@Table(name = "li_category_brand") +@TableName("li_category_brand") +@ApiModel(value = "商品分类品牌") +@NoArgsConstructor +public class CategoryBrand implements Serializable { + + private static final long serialVersionUID = 3315719881926878L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + /** + * 分类id + */ + @TableField(value = "category_id") + @ApiModelProperty(value = "分类id") + private String categoryId; + /** + * 品牌id + */ + @TableField(value = "brand_id") + @ApiModelProperty(value = "品牌id") + private String brandId; + + public CategoryBrand(String categoryId,String brandId){ + this.categoryId=categoryId; + this.brandId=brandId; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/CategoryParameterGroup.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/CategoryParameterGroup.java new file mode 100644 index 00000000..5c97992d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/CategoryParameterGroup.java @@ -0,0 +1,49 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 分类参数组关联 + * + * @author pikachu + * @date 2020-02-26 10:34:02 + */ +@Data +@Entity +@Table(name = "li_category_parameter_group") +@TableName("li_category_parameter_group") +@ApiModel(value = "分类绑定参数组") +public class CategoryParameterGroup extends BaseEntity { + + private static final long serialVersionUID = -3254446505349029420L; + + /** + * 参数组名称 + */ + @ApiModelProperty(value = "参数组名称", required = true) + @NotEmpty(message = "参数组名称不能为空") + @Length(max = 50, message = "参数组名称不能超过50字") + private String groupName; + /** + * 关联分类id + */ + @ApiModelProperty(value = "关联分类id", required = true) + @NotNull(message = "关联的分类不能为空") + private String categoryId; + /** + * + */ + @ApiModelProperty(value = "排序", hidden = true) + private Integer sort; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/CategorySpecification.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/CategorySpecification.java new file mode 100644 index 00000000..d286db1a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/CategorySpecification.java @@ -0,0 +1,44 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 分类参数组关联 + * + * @author pikachu + * @date 2020-02-26 10:34:02 + */ +@Data +@Entity +@Table(name = "li_category_specification") +@TableName("li_category_specification") +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(value = "商品分类规格") +public class CategorySpecification extends BaseEntity { + + + private static final long serialVersionUID = -4041367493342243147L; + /** + * 分类id + */ + @TableField(value = "category_id") + @ApiModelProperty(value = "分类id") + private String categoryId; + /** + * 规格id + */ + @TableField(value = "specification_id") + @ApiModelProperty(value = "规格id") + private String specificationId; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/DraftGoods.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/DraftGoods.java new file mode 100644 index 00000000..6281e45f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/DraftGoods.java @@ -0,0 +1,157 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.goods.entity.enums.DraftGoodsSaveType; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Max; + +/** + * 草稿商品 + * + * @author pikachu + * @date 2020-02-23 9:14:33 + */ +@Data +@Entity +@Table(name = "li_draft_goods") +@TableName("li_draft_goods") +@ApiModel(value = "草稿商品") +@AllArgsConstructor +@NoArgsConstructor +public class DraftGoods extends BaseEntity { + + private static final long serialVersionUID = 370683495251252601L; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @Length(max = 30, message = "商品规格编号太长,不能超过30个字符") + @ApiModelProperty(value = "商品编号") + private String sn; + + @ApiModelProperty(value = "品牌id") + private String brandId; + + @ApiModelProperty(value = "分类path") + private String categoryPath; + + @ApiModelProperty(value = "计量单位") + private String goodsUnit; + + @ApiModelProperty(value = "卖点") + private String sellingPoint; + + @ApiModelProperty(value = "重量") + @Max(value = 99999999, message = "重量不能超过99999999") + private Double weight; + /** + * @see GoodsStatusEnum + */ + @ApiModelProperty(value = "上架状态") + private String marketEnable; + + @ApiModelProperty(value = "详情") + private String intro; + + @Max(value = 99999999, message = "价格不能超过99999999") + @ApiModelProperty(value = "商品价格") + private Double price; + + @Max(value = 99999999, message = "成本价格99999999") + @ApiModelProperty(value = "成本价格") + private Double cost; + + @ApiModelProperty(value = "购买数量") + private Integer buyCount; + + @Max(value = 99999999, message = "库存不能超过99999999") + @ApiModelProperty(value = "库存") + private Integer quantity; + + @ApiModelProperty(value = "可用库存") + private Integer enableQuantity; + + @ApiModelProperty(value = "商品好评率") + private Double grade; + + @ApiModelProperty(value = "缩略图路径") + private String thumbnail; + + @ApiModelProperty(value = "大图路径") + private String big; + + @ApiModelProperty(value = "小图路径") + private String small; + + @ApiModelProperty(value = "原图路径") + private String original; + + @ApiModelProperty(value = "店铺分类id") + private String storeCategoryPath; + + @ApiModelProperty(value = "评论数量") + private Integer commentNum; + + @ApiModelProperty(value = "卖家id") + private String storeId; + + @ApiModelProperty(value = "卖家名字") + private String storeName; + + @ApiModelProperty(value = "运费模板id") + private String templateId; + + @ApiModelProperty(value = "运费承担者") + private String freightPayer; + + @ApiModelProperty(value = "是否自营") + private Boolean selfOperated; + /** + * 商品移动端详情 + */ + @ApiModelProperty(value = "商品移动端详情") + private String mobileIntro; + + @ApiModelProperty(value = "商品视频") + private String goodsVideo; + + @ApiModelProperty(value = "是否为推荐商品") + private boolean recommend; + + @ApiModelProperty(value = "销售模式") + private String salesModel; + + /** + * @see DraftGoodsSaveType + */ + @ApiModelProperty(value = "草稿商品保存类型") + private String saveType; + + @Column(columnDefinition = "TEXT") + @ApiModelProperty(value = "分类名称JSON") + private String categoryNameJson; + + @Column(columnDefinition = "TEXT") + @ApiModelProperty(value = "商品参数JSON") + private String goodsParamsListJson; + + @Column(columnDefinition = "TEXT") + @ApiModelProperty(value = "商品图片JSON") + private String goodsGalleryListJson; + + @Column(columnDefinition = "TEXT") + @ApiModelProperty(value = "sku列表JSON") + private String skuListJson; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/Goods.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Goods.java new file mode 100644 index 00000000..4d79ab44 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Goods.java @@ -0,0 +1,214 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.goods.entity.dto.GoodsOperationDTO; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Max; + +/** + * 商品 + * + * @author pikachu + * @date 2020-02-23 9:14:33 + */ +@Data +@Entity +@Table(name = "li_goods") +@TableName("li_goods") +@ApiModel(value = "商品") +public class Goods extends BaseEntity { + + private static final long serialVersionUID = 370683495251252601L; + /** + * 商品名称 + */ + @ApiModelProperty(value = "商品名称") + private String goodsName; + /** + * 商品编号 + */ + @Length(max = 30, message = "商品规格编号太长,不能超过30个字符") + @ApiModelProperty(value = "商品编号") + private String sn; + + @ApiModelProperty(value = "品牌id") + private String brandId; + + @ApiModelProperty(value = "分类path") + private String categoryPath; + + @ApiModelProperty(value = "计量单位") + private String goodsUnit; + + /** + * 卖点 + */ + @ApiModelProperty(value = "卖点") + private String sellingPoint; + + /** + * 重量 + */ + @ApiModelProperty(value = "重量") + @Max(value = 99999999, message = "重量不能超过99999999") + private Double weight; + /** + * 上架状态 + * + * @see GoodsStatusEnum + */ + @ApiModelProperty(value = "上架状态") + private String marketEnable; + /** + * 详情 + */ + @ApiModelProperty(value = "详情") + private String intro; + /** + * 商品价格 + */ + @Max(value = 99999999, message = "价格不能超过99999999") + @ApiModelProperty(value = "商品价格") + private Double price; + /** + * 成本价格 + */ + @Max(value = 99999999, message = "成本价格99999999") + @ApiModelProperty(value = "成本价格") + private Double cost; + + /** + * 购买数量 + */ + @ApiModelProperty(value = "购买数量") + private Integer buyCount; + /** + * 库存 + */ + @Max(value = 99999999, message = "库存不能超过99999999") + @ApiModelProperty(value = "库存") + private Integer quantity; + /** + * 商品好评率 + */ + @ApiModelProperty(value = "商品好评率") + private Double grade; + /** + * 缩略图路径 + */ + @ApiModelProperty(value = "缩略图路径") + private String thumbnail; + /** + * 小图路径 + */ + @ApiModelProperty(value = "小图路径") + private String small; + /** + * 原图路径 + */ + @ApiModelProperty(value = "原图路径") + private String original; + /** + * 店铺分类id + */ + @ApiModelProperty(value = "店铺分类id") + private String storeCategoryPath; + /** + * 评论数量 + */ + @ApiModelProperty(value = "评论数量") + private Integer commentNum; + /** + * 卖家id + */ + @ApiModelProperty(value = "卖家id") + private String storeId; + /** + * 卖家名字 + */ + @ApiModelProperty(value = "卖家名字") + private String storeName; + /** + * 运费模板id + */ + @ApiModelProperty(value = "运费模板id") + private String templateId; + /** + * 谁承担运费 BUYER:买家承担,STORE:卖家承担 + */ + @ApiModelProperty(value = "运费承担者 BUYER:买家承担,STORE:卖家承担") + private String freightPayer; + /** + * 审核状态 + * + * @see GoodsAuthEnum + */ + @ApiModelProperty(value = "审核状态") + private String isAuth; + /** + * 审核信息 + */ + @ApiModelProperty(value = "审核信息") + private String authMessage; + /** + * 下架原因 + */ + @ApiModelProperty(value = "下架原因") + private String underMessage; + /** + * 是否自营 + */ + @ApiModelProperty(value = "是否自营") + private Boolean selfOperated; + /** + * 商品移动端详情 + */ + @ApiModelProperty(value = "商品移动端详情") + private String mobileIntro; + /** + * 商品视频 + */ + @ApiModelProperty(value = "商品视频") + private String goodsVideo; + + + @ApiModelProperty(value = "是否为推荐商品", required = true) + private boolean recommend; + + @ApiModelProperty(value = "销售模式", required = true) + private String salesModel; + + public Goods() { + } + + public Goods(GoodsOperationDTO goodsOperationDTO) { + this.goodsName = goodsOperationDTO.getGoodsName(); + this.categoryPath = goodsOperationDTO.getCategoryPath(); + this.storeCategoryPath = goodsOperationDTO.getStoreCategoryPath(); + this.brandId = goodsOperationDTO.getBrandId(); + this.sn = goodsOperationDTO.getSn(); + this.price = goodsOperationDTO.getPrice(); + this.weight = goodsOperationDTO.getWeight(); + this.freightPayer = goodsOperationDTO.getFreightPayer(); + this.templateId = goodsOperationDTO.getTemplateId(); + this.recommend = goodsOperationDTO.isRecommend(); + this.sellingPoint = goodsOperationDTO.getSellingPoint(); + this.salesModel = goodsOperationDTO.getSalesModel(); + this.goodsUnit = goodsOperationDTO.getGoodsUnit(); + this.intro = goodsOperationDTO.getIntro(); + this.mobileIntro = goodsOperationDTO.getMobileIntro(); + this.cost = goodsOperationDTO.getCost(); + //如果立即上架则 + this.marketEnable = goodsOperationDTO.isRelease() ? GoodsStatusEnum.UPPER.name() : GoodsStatusEnum.DOWN.name(); + + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsGallery.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsGallery.java new file mode 100644 index 00000000..4063bdba --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsGallery.java @@ -0,0 +1,80 @@ +package cn.lili.modules.goods.entity.dos; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedBy; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; + +/** + * 商品相册 + * + * @author pikachu + * @date 2020-02-23 9:14:33 + */ +@Data +@Entity +@Table(name = "li_goods_gallery") +@TableName("li_goods_gallery") +@ApiModel(value = "商品相册") +public class GoodsGallery implements Serializable { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + /** + * 商品主键 + */ + @ApiModelProperty(value = "商品id") + private String goodsId; + + /** + * 缩略图路径 + */ + @ApiModelProperty(value = "缩略图路径") + private String thumbnail; + + /** + * 小图路径 + */ + @ApiModelProperty(value = "小图路径") + private String small; + + /** + * 原图路径 + */ + @ApiModelProperty(value = "原图路径", required = true) + private String original; + + /** + * 是否是默认图片1 0没有默认 + */ + @ApiModelProperty(value = "是否是默认图片1 0没有默认") + private Integer isDefault; + + /** + * 排序 + */ + @ApiModelProperty(value = "排序", required = true) + private Integer sort; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsParams.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsParams.java new file mode 100644 index 00000000..54d59883 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsParams.java @@ -0,0 +1,55 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 商品关联参数 + * + * @author pikachu + * @date 2020-02-23 9:14:33 + */ +@Data +@Entity +@Table(name = "li_goods_params") +@TableName("li_goods_params") +@ApiModel(value = "商品关联参数") +public class GoodsParams extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 商品id + */ + @TableField(value = "goods_id") + @ApiModelProperty(value = "商品id", hidden = true) + private String goodsId; + /** + * 参数id + */ + @TableField(value = "param_id") + @ApiModelProperty(value = "参数id", required = true) + private String paramId; + /** + * 参数名字 + */ + @TableField(value = "param_name") + @ApiModelProperty(value = "参数名字", required = true) + private String paramName; + /** + * 参数值 + */ + @TableField(value = "param_value") + @ApiModelProperty(value = "参数值", required = true) + @Length(max = 100, message = "参数值字符不能大于120") + private String paramValue; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsSku.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsSku.java new file mode 100644 index 00000000..f3ff55c2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsSku.java @@ -0,0 +1,172 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Max; +import java.util.Date; + +/** + * 商品sku + * + * @author pikachu + * @date 2020-02-23 9:14:33 + */ +@Data +@Entity +@Table(name = "li_goods_sku") +@TableName("li_goods_sku") +@ApiModel(value = "商品sku对象") +public class GoodsSku extends BaseEntity { + + private static final long serialVersionUID = 4865908658161118934L; + + @ApiModelProperty(value = "商品id") + private String goodsId; + + @ApiModelProperty(value = "规格信息json", hidden = true) + @Column(columnDefinition = "TEXT") + @JsonIgnore + private String specs; + + @ApiModelProperty(value = "规格信息") + private String simpleSpecs; + + @ApiModelProperty(value = "配送模版id") + private String freightTemplateId; + + @ApiModelProperty(value = "是否是促销商品") + private Boolean isPromotion; + + @ApiModelProperty(value = "促销价") + private Double promotionPrice; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @Length(max = 30, message = "商品规格编号太长,不能超过30个字符") + @ApiModelProperty(value = "商品编号") + private String sn; + + @ApiModelProperty(value = "品牌id") + private String brandId; + + @ApiModelProperty(value = "分类path") + private String categoryPath; + + @ApiModelProperty(value = "计量单位") + private String goodsUnit; + + @ApiModelProperty(value = "卖点") + private String sellingPoint; + + @ApiModelProperty(value = "重量") + @Max(value = 99999999, message = "重量不能超过99999999") + private Double weight; + /** + * @see GoodsStatusEnum + */ + @ApiModelProperty(value = "上架状态") + private String marketEnable; + + @ApiModelProperty(value = "商品详情") + @Column(columnDefinition = "TEXT") + private String intro; + + @Max(value = 99999999, message = "价格不能超过99999999") + @ApiModelProperty(value = "商品价格") + private Double price; + + @Max(value = 99999999, message = "成本价格99999999") + @ApiModelProperty(value = "成本价格") + private Double cost; + + @ApiModelProperty(value = "浏览数量") + private Integer viewCount; + + @ApiModelProperty(value = "购买数量") + private Integer buyCount; + + @Max(value = 99999999, message = "库存不能超过99999999") + @ApiModelProperty(value = "库存") + private Integer quantity; + + @ApiModelProperty(value = "商品好评率") + private Double grade; + + @ApiModelProperty(value = "缩略图路径") + private String thumbnail; + + @ApiModelProperty(value = "大图路径") + private String big; + + @ApiModelProperty(value = "小图路径") + private String small; + + @ApiModelProperty(value = "原图路径") + private String original; + + @ApiModelProperty(value = "店铺分类id") + private String storeCategoryPath; + + @ApiModelProperty(value = "评论数量") + private Integer commentNum; + + @ApiModelProperty(value = "卖家id") + private String storeId; + + @ApiModelProperty(value = "卖家名字") + private String storeName; + + @ApiModelProperty(value = "运费模板id") + private String templateId; + + @ApiModelProperty(value = "运费承担者") + private String freightPayer; + /** + * @see GoodsAuthEnum + */ + @ApiModelProperty(value = "审核状态") + private String isAuth; + + @ApiModelProperty(value = "审核信息") + private String authMessage; + + @ApiModelProperty(value = "下架原因") + private String underMessage; + + @ApiModelProperty(value = "是否自营") + private Boolean selfOperated; + + @ApiModelProperty(value = "商品移动端详情") + @Column(columnDefinition = "TEXT") + private String mobileIntro; + + @ApiModelProperty(value = "商品视频") + private String goodsVideo; + + @ApiModelProperty(value = "是否为推荐商品", required = true) + private boolean recommend; + + @ApiModelProperty(value = "销售模式", required = true) + private String salesModel; + + @Override + public Date getUpdateTime() { + if (super.getUpdateTime() == null) { + return new Date(1593571928); + } else { + return super.getUpdateTime(); + } + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsUnit.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsUnit.java new file mode 100644 index 00000000..f8d7d72a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsUnit.java @@ -0,0 +1,29 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; + +/** + * 商品计量单位 + * + * @author Bulbasaur + * @date: 2020/11/26 16:08 + */ +@Data +@Entity +@Table(name = "li_goods_unit") +@TableName("li_goods_unit") +@ApiModel(value = "商品计量单位") +public class GoodsUnit extends BaseEntity { + + @NotEmpty(message = "计量单位名称不能为空") + @ApiModelProperty(value = "计量单位名称") + private String name; +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsWords.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsWords.java new file mode 100644 index 00000000..02579a78 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/GoodsWords.java @@ -0,0 +1,61 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + + +/** + * 商品关键字 + * + * @author paulG + * @date 2020/10/15 + */ +@Data +@Entity +@Table(name = "li_goods_words") +@TableName("li_goods_words") +@ApiModel(value = "商品关键字") +@NoArgsConstructor +public class GoodsWords extends BaseEntity { + + private static final long serialVersionUID = 5709806638518675229L; + + /** + * 商品关键字 + */ + @ApiModelProperty(value = "商品关键字") + private String words; + + /** + * 全拼音 + */ + @ApiModelProperty(value = "全拼音") + private String wholeSpell; + + /** + * 缩写 + */ + @ApiModelProperty(value = "缩写") + private String abbreviate; + + /** + * 类型 + */ + @ApiModelProperty(value = "类型") + private String type; + + /** + * 排序 + */ + @ApiModelProperty(value = "排序") + private Integer sort; + + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/Parameters.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Parameters.java new file mode 100644 index 00000000..6b39f1ea --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Parameters.java @@ -0,0 +1,68 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 商品参数 + * + * @author pikachu + * @date 2020-02-23 9:14:33 + */ +@Data +@Entity +@Table(name = "li_parameters") +@TableName("li_parameters") +@ApiModel(value = "商品参数") +public class Parameters extends BaseEntity { + + private static final long serialVersionUID = -566510714456317006L; + + @ApiModelProperty(value = "参数名称", required = true) + @NotEmpty(message = "参数名称必填") + @Length(max = 50, message = "参数名称不能超过50字") + private String paramName; + + @ApiModelProperty(value = "参数类型1 输入项 2 选择项", required = true) + @NotNull(message = "参数类型必选") + @Min(value = 1, message = "参数类型传值不正确") + @Max(value = 2, message = "参数类型传值不正确") + private Integer paramType; + + @ApiModelProperty(value = "选择值,当参数类型是选择项2时,必填,逗号分隔") + private String options; + + @ApiModelProperty(value = "是否可索引,0 不显示 1 显示", required = true) + @NotNull(message = "是否可索引必选") + @Min(value = 0, message = "是否可索引传值不正确") + @Max(value = 1, message = "是否可索引传值不正确") + private Integer isIndex; + + @ApiModelProperty(value = "是否必填是 1 否 0", required = true) + @NotNull(message = "是否必填必选") + @Min(value = 0, message = "是否必填传值不正确") + @Max(value = 1, message = "是否必填传值不正确") + private Integer required; + + @ApiModelProperty(value = "参数分组id", required = true) + @NotNull(message = "所属参数组不能为空") + private String groupId; + + @ApiModelProperty(value = "分类id", hidden = true) + private String categoryId; + + @ApiModelProperty(value = "排序", hidden = true) + private Integer sort; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/SpecValues.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/SpecValues.java new file mode 100644 index 00000000..796610c9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/SpecValues.java @@ -0,0 +1,42 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 规格值 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@Data +@Entity +@Table(name = "li_spec_values") +@TableName("li_spec_values") +@ApiModel(value = "规格值") +public class SpecValues extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 规格项id + */ + @TableField(value = "spec_id") + @ApiModelProperty(value = "规格项id") + private String specId; + + /** + * 规格值名字 + */ + @TableField(value = "spec_value") + @ApiModelProperty(value = "规格值名字") + private String specValue; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dos/Specification.java b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Specification.java new file mode 100644 index 00000000..95245cf1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dos/Specification.java @@ -0,0 +1,42 @@ +package cn.lili.modules.goods.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; + +/** + * 商品规格项 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@Data +@Entity +@Table(name = "li_specification") +@TableName("li_specification") +@ApiModel(value = "规格项") +public class Specification extends BaseEntity { + + private static final long serialVersionUID = 147792597901239486L; + + /** + * 规格名称 + */ + @NotEmpty(message = "规格名称不能为空") + @ApiModelProperty(value = "规格名称", required = true) + private String specName; + + /** + * 所属卖家 0属于平台 + */ + @ApiModelProperty(hidden = true) + private String storeId; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/BrandPageDTO.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/BrandPageDTO.java new file mode 100644 index 00000000..05f85f94 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/BrandPageDTO.java @@ -0,0 +1,25 @@ +package cn.lili.modules.goods.entity.dto; + +import cn.lili.common.vo.PageVO; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 商品品牌dto + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@Data +@JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class) +@ApiModel(description = "商品品牌dto") +public class BrandPageDTO extends PageVO { + + private static final long serialVersionUID = 8906820486037326039L; + + @ApiModelProperty(value = "品牌名称") + private String name; +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/CategorySearchParams.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/CategorySearchParams.java new file mode 100644 index 00000000..03e6543c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/CategorySearchParams.java @@ -0,0 +1,35 @@ +package cn.lili.modules.goods.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 分类查询参数 + * + * @author paulG + * @date 2020/12/1 + **/ +@Data +public class CategorySearchParams { + + @ApiModelProperty(value = "分类名称") + private String name; + + @ApiModelProperty(value = "父id") + private String parentId; + + @ApiModelProperty(value = "层级") + private Integer level; + + @ApiModelProperty(value = "排序值") + private BigDecimal sortOrder; + + @ApiModelProperty(value = "佣金比例") + private BigDecimal commissionRate; + + @ApiModelProperty(value = "父节点名称") + private String parentTitle; + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/DraftGoodsDTO.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/DraftGoodsDTO.java new file mode 100644 index 00000000..607223b6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/DraftGoodsDTO.java @@ -0,0 +1,34 @@ +package cn.lili.modules.goods.entity.dto; + +import cn.lili.modules.goods.entity.dos.DraftGoods; +import cn.lili.modules.goods.entity.dos.GoodsParams; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.Valid; +import java.util.List; +import java.util.Map; + +/** + * 草稿商品DTO + * + * @author paulG + * @since 2020/12/22 + **/ +@Data +public class DraftGoodsDTO extends DraftGoods { + + private static final long serialVersionUID = 5255666163196674178L; + + @ApiModelProperty(value = "商品参数") + @Valid + private List goodsParamsList; + + @ApiModelProperty(value = "商品图片") + private List goodsGalleryList; + + @ApiModelProperty(value = "sku列表") + @Valid + private List> skuList; + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/DraftGoodsSearchParams.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/DraftGoodsSearchParams.java new file mode 100644 index 00000000..d146fa51 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/DraftGoodsSearchParams.java @@ -0,0 +1,34 @@ +package cn.lili.modules.goods.entity.dto; + +import cn.hutool.core.util.StrUtil; +import cn.lili.modules.goods.entity.enums.DraftGoodsSaveType; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 草稿商品搜索对象 + * + * @author paulG + * @date 2020/12/21 + **/ +@Data +public class DraftGoodsSearchParams extends GoodsSearchParams { + + private static final long serialVersionUID = -1057830772267228050L; + + /** + * @see DraftGoodsSaveType + */ + @ApiModelProperty(value = "草稿商品保存类型") + private String saveType; + + @Override + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = super.queryWrapper(); + if (StrUtil.isNotEmpty(saveType)) { + queryWrapper.eq("save_type", saveType); + } + return queryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsCompleteMessage.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsCompleteMessage.java new file mode 100644 index 00000000..0ff1826c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsCompleteMessage.java @@ -0,0 +1,28 @@ +package cn.lili.modules.goods.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 商品购买完成信息 + * + * @author paulG + * @date 2021/3/24 + **/ +@Data +public class GoodsCompleteMessage { + + + @ApiModelProperty(value = "商品id") + private String goodsId; + + @ApiModelProperty(value = "商品skuId") + private String skuId; + + @ApiModelProperty(value = "购买会员sn") + private String memberId; + + @ApiModelProperty(value = "购买数量") + private Integer buyNum; + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsOperationDTO.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsOperationDTO.java new file mode 100644 index 00000000..b6f9c4ab --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsOperationDTO.java @@ -0,0 +1,124 @@ +package cn.lili.modules.goods.entity.dto; + +import cn.lili.modules.goods.entity.dos.GoodsParams; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; +import org.hibernate.validator.constraints.Length; + +import javax.validation.Valid; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * 商品查询条件 + * + * @author pikachu + * @date 2020-02-24 19:27:20 + */ +@Data +@ToString +public class GoodsOperationDTO implements Serializable { + + private static final long serialVersionUID = -509667581371776913L; + + @ApiModelProperty(hidden = true) + private String goodsId; + + @ApiModelProperty(value = "分类path") + private String categoryPath; + + @ApiModelProperty(value = "店铺分类id", required = true) + @NotNull(message = "店铺分类不能为空") + @Min(value = 0, message = "店铺分类值不正确") + private String storeCategoryPath; + + @ApiModelProperty(value = "品牌id") + @Min(value = 0, message = "品牌值不正确") + private String brandId; + + @ApiModelProperty(value = "商品名称", required = true) + @NotEmpty(message = "商品名称不能为空") + private String goodsName; + + @ApiModelProperty(value = "商品编号", required = true) + @Length(max = 30, message = "商品编号太长,不能超过30个字符") + private String sn; + + @ApiModelProperty(value = "商品价格", required = true) + @NotNull(message = "商品价格不能为空") + @Min(value = 0, message = "商品价格不能为负数") + @Max(value = 99999999, message = "商品价格不能超过99999999") + private Double price; + + @ApiModelProperty(value = "市场价格", required = true) + @NotNull(message = "市场价格不能为空") + private Double cost; + + @ApiModelProperty(value = "重量", required = true) + @NotNull(message = "商品重量不能为空") + @Min(value = 0, message = "重量不能为负数") + @Max(value = 99999999, message = "重量不能超过99999999") + private Double weight; + + @ApiModelProperty(value = "谁承担运费0:买家承担,1:卖家承担", required = true) + @NotNull(message = "承担运费不能为空") + @Min(value = 0, message = "承担运费值不正确") + @Max(value = 1, message = "承担运费值不正确") + private String freightPayer; + + @ApiModelProperty(value = "详情") + private String intro; + + @ApiModelProperty(value = "商品移动端详情") + private String mobileIntro; + + @ApiModelProperty(value = "库存") + @Max(value = 99999999, message = "库存不能超过99999999") + private Integer quantity; + + @ApiModelProperty(value = "是否立即发布") + private boolean release; + + @ApiModelProperty(value = "是否是推荐商品") + private boolean recommend; + + @ApiModelProperty(value = "商品参数") + @Valid + private List goodsParamsList; + + @ApiModelProperty(value = "商品图片") + private List goodsGalleryList; + + @ApiModelProperty(value = "运费模板id,不需要运费模板时值是0", required = true) + @NotNull(message = "运费模板不能为空,没有运费模板时,传值0") + @Min(value = 0, message = "运费模板值不正确") + private String templateId; + + @ApiModelProperty(value = "sku列表") + @Valid + private List> skuList; + + @ApiModelProperty(value = "卖点") + private String sellingPoint; + + @ApiModelProperty(value = "销售模式", required = true) + private String salesModel; + + @ApiModelProperty(value = "是否有规格", hidden = true) + private String haveSpec; + + @ApiModelProperty(value = "销售模式", required = true) + private String goodsUnit; + + @ApiModelProperty(value = "商品描述") + private String info; + + @ApiModelProperty(value = "是否重新生成sku数据") + private Boolean regeneratorSkuFlag = true; +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsSearchParams.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsSearchParams.java new file mode 100644 index 00000000..6edfcb6b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsSearchParams.java @@ -0,0 +1,128 @@ +package cn.lili.modules.goods.entity.dto; + +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 商品查询条件 + * + * @author pikachu + * @date 2020-02-24 19:27:20 + */ +@Data +public class GoodsSearchParams extends PageVO { + + private static final long serialVersionUID = 2544015852728566887L; + + @ApiModelProperty(value = "商品编号") + private String goodsId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品编号") + private String sn; + + @ApiModelProperty(value = "商家ID") + private String storeId; + + @ApiModelProperty(value = "卖家名字") + private String storeName; + + @ApiModelProperty(value = "价格,可以为范围,如10_1000") + private String price; + + @ApiModelProperty(value = "分类path") + private String categoryPath; + + @ApiModelProperty(value = "是否是积分商品") + private Boolean isPoint; + + @ApiModelProperty(value = "店铺分类id") + private String storeCategoryPath; + + @ApiModelProperty(value = "是否自营") + private Boolean selfOperated; + + /** + * @see GoodsStatusEnum + */ + @ApiModelProperty(value = "上下架状态") + private String marketEnable; + + /** + * @see GoodsAuthEnum + */ + @ApiModelProperty(value = "审核状态") + private String isAuth; + + @ApiModelProperty(value = "库存数量") + private Integer quantity; + + @ApiModelProperty(value = "是否为推荐商品") + private Boolean recommend; + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StringUtils.isNotEmpty(goodsId)) { + queryWrapper.eq("goods_id", goodsId); + } + if (StringUtils.isNotEmpty(goodsName)) { + queryWrapper.like("goods_name", goodsName); + } + if (StringUtils.isNotEmpty(sn)) { + queryWrapper.eq("sn", sn); + } + if (StringUtils.isNotEmpty(storeId)) { + queryWrapper.eq("store_id", storeId); + } + if (StringUtils.isNotEmpty(storeName)) { + queryWrapper.like("store_name", storeName); + } + if (StringUtils.isNotEmpty(categoryPath)) { + queryWrapper.like("category_path", categoryPath); + } + if (isPoint != null) { + queryWrapper.eq("is_point", isPoint); + } + if (StringUtils.isNotEmpty(storeCategoryPath)) { + queryWrapper.like("store_category_path", storeCategoryPath); + } + if (selfOperated != null) { + queryWrapper.eq("self_operated", selfOperated); + } + if (StringUtils.isNotEmpty(marketEnable)) { + queryWrapper.eq("market_enable", marketEnable); + } + if (StringUtils.isNotEmpty(isAuth)) { + queryWrapper.eq("is_auth", isAuth); + } + if (quantity != null) { + queryWrapper.le("quantity", quantity); + } + if (recommend != null) { + queryWrapper.le("recommend", recommend); + } + queryWrapper.eq("delete_flag", false); + this.betweenWrapper(queryWrapper); + return queryWrapper; + } + + private void betweenWrapper(QueryWrapper queryWrapper) { + if (StringUtils.isNotEmpty(price)) { + String[] s = price.split("_"); + if (s.length > 1) { + queryWrapper.ge("price", s[1]); + } else { + queryWrapper.le("price", s[0]); + } + } + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsSkuSearchParams.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsSkuSearchParams.java new file mode 100644 index 00000000..7139e103 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsSkuSearchParams.java @@ -0,0 +1,30 @@ +package cn.lili.modules.goods.entity.dto; + +import cn.lili.common.utils.StringUtils; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 规格商品查询条件 + * + * @author paulG + * @date 2020/12/15 + **/ +@Data +public class GoodsSkuSearchParams extends GoodsSearchParams { + + private static final long serialVersionUID = -6235885068610635045L; + + @ApiModelProperty(value = "商品id") + private String goodsId; + + @Override + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = super.queryWrapper(); + queryWrapper.eq(StringUtils.isNotEmpty(goodsId), "goods_id", goodsId); + return queryWrapper; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsSkuStockDTO.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsSkuStockDTO.java new file mode 100644 index 00000000..d6d4841d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/GoodsSkuStockDTO.java @@ -0,0 +1,22 @@ +package cn.lili.modules.goods.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 商品库存DTO + * + * @author paulG + * @date 2020/12/23 + **/ +@Data +public class GoodsSkuStockDTO { + + @ApiModelProperty(value = "商品skuId") + private String skuId; + + @ApiModelProperty(value = "库存") + private Integer quantity; + + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/dto/SpecificationSearchParams.java b/framework/src/main/java/cn/lili/modules/goods/entity/dto/SpecificationSearchParams.java new file mode 100644 index 00000000..0b93badf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/dto/SpecificationSearchParams.java @@ -0,0 +1,34 @@ +package cn.lili.modules.goods.entity.dto; + +import cn.lili.common.utils.StringUtils; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 规格搜索参数 + * + * @author paulG + * @date 2020/12/19 + **/ +@Data +public class SpecificationSearchParams { + + + @ApiModelProperty(value = "规格名") + private String specName; + + @ApiModelProperty(value = "绑定分类") + private String categoryPath; + + @ApiModelProperty(value = "未删除 ") + private Boolean deleteFlag; + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.like(StringUtils.isNotEmpty(specName), "spec_name", specName); + queryWrapper.eq(deleteFlag != null, "delete_flag", deleteFlag); + return queryWrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/enums/DraftGoodsSaveType.java b/framework/src/main/java/cn/lili/modules/goods/entity/enums/DraftGoodsSaveType.java new file mode 100644 index 00000000..d4e0ae8b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/enums/DraftGoodsSaveType.java @@ -0,0 +1,25 @@ +package cn.lili.modules.goods.entity.enums; + +/** + * 草稿商品保存类型 + * + * @author paulG + * @date 2020/12/21 + **/ +public enum DraftGoodsSaveType { + + DRAFT("草稿"), + + TEMPLATE("模版"); + + private final String description; + + DraftGoodsSaveType(String description) { + this.description = description; + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsAuthEnum.java b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsAuthEnum.java new file mode 100644 index 00000000..ff5a9d63 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsAuthEnum.java @@ -0,0 +1,32 @@ +package cn.lili.modules.goods.entity.enums; + +/** + * 商品审核 + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +public enum GoodsAuthEnum { + /** + * 需要审核 并且待审核 + */ + TOBEAUDITED("待审核"), + /** + * 审核通过 + */ + PASS("审核通过"), + /** + * 审核通过 + */ + REFUSE("审核拒绝"); + + private final String description; + + GoodsAuthEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsFreightEnum.java b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsFreightEnum.java new file mode 100644 index 00000000..c89a56c2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsFreightEnum.java @@ -0,0 +1,28 @@ +package cn.lili.modules.goods.entity.enums; + +/** + * 商品运费承担者 + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +public enum GoodsFreightEnum { + /** + * 买家承担运费 + */ + BUYER("买家承担运费"), + /** + * 卖家承担运费 + */ + STORE("卖家承担运费"); + + private final String description; + + GoodsFreightEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsOperate.java b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsOperate.java new file mode 100644 index 00000000..73b11585 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsOperate.java @@ -0,0 +1,27 @@ +package cn.lili.modules.goods.entity.enums; + +/** + * 商品操作枚举 + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +public enum GoodsOperate { + /** + * 下架 + */ + DOWN, + /** + * 逻辑删除 + */ + DELETE, + /** + * 删除 + */ + CLEAR, + /** + * 还原 + */ + REDUCTION + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsSalesModeEnum.java b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsSalesModeEnum.java new file mode 100644 index 00000000..ed6361d6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsSalesModeEnum.java @@ -0,0 +1,30 @@ +package cn.lili.modules.goods.entity.enums; + +/** + * 商品审核 + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +public enum GoodsSalesModeEnum { + /** + * 需要审核 并且待审核 + */ + RETAIL("零售"), + /** + * 审核通过 + */ + WHOLESALE("批发"); + + private final String description; + + GoodsSalesModeEnum(String description) { + this.description = description; + + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsStatusEnum.java b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsStatusEnum.java new file mode 100644 index 00000000..7ea1a0cf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsStatusEnum.java @@ -0,0 +1,28 @@ +package cn.lili.modules.goods.entity.enums; + +/** + * 商品类型枚举 + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +public enum GoodsStatusEnum { + /** + * 上架 + */ + UPPER("上架"), + /** + * 下架 + */ + DOWN("下架"); + + private final String description; + + GoodsStatusEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsWordsTypeEnum.java b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsWordsTypeEnum.java new file mode 100644 index 00000000..7e9236bf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/enums/GoodsWordsTypeEnum.java @@ -0,0 +1,31 @@ +package cn.lili.modules.goods.entity.enums; + +/** + * 商品关键字类型 + * + * @author paulG + * @since 2020/10/15 + **/ +public enum GoodsWordsTypeEnum { + + /** + * 系统 + */ + SYSTEM("系统"), + + /** + * 平台 + */ + PLATFORM("平台"); + + private final String description; + + GoodsWordsTypeEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/BrandVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/BrandVO.java new file mode 100644 index 00000000..beab43a5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/BrandVO.java @@ -0,0 +1,21 @@ +package cn.lili.modules.goods.entity.vos; + +import cn.lili.modules.goods.entity.dos.Brand; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 品牌VO + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +@Data +public class BrandVO extends Brand { + + private static final long serialVersionUID = 3829199991161122317L; + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/CategoryBrandVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/CategoryBrandVO.java new file mode 100644 index 00000000..c30a694f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/CategoryBrandVO.java @@ -0,0 +1,25 @@ +package cn.lili.modules.goods.entity.vos; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 分类品牌VO + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +@Data +public class CategoryBrandVO { + /** + * 品牌id + */ + @ApiModelProperty(value = "品牌id", required = true) + private String id; + + /** + * 品牌名称 + */ + @ApiModelProperty(value = "品牌名称", required = true) + private String name; +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/CategorySpecificationVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/CategorySpecificationVO.java new file mode 100644 index 00000000..209cc5fc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/CategorySpecificationVO.java @@ -0,0 +1,25 @@ +package cn.lili.modules.goods.entity.vos; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 分类规格VO + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +@Data +public class CategorySpecificationVO { + /** + * 规格id + */ + @ApiModelProperty(value = "规格id", required = true) + private String id; + + /** + * 规格名称 + */ + @ApiModelProperty(value = "规格名称", required = true) + private String name; +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/CategoryVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/CategoryVO.java new file mode 100644 index 00000000..e03b16a5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/CategoryVO.java @@ -0,0 +1,59 @@ +package cn.lili.modules.goods.entity.vos; + +import cn.hutool.core.bean.BeanUtil; +import cn.lili.modules.goods.entity.dos.Brand; +import cn.lili.modules.goods.entity.dos.Category; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +/** + * 分类VO + * + * @author paulG + * @date 2020/12/1 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CategoryVO extends Category { + + private static final long serialVersionUID = 3775766246075838410L; + + @ApiModelProperty(value = "父节点名称") + private String parentTitle; + + @ApiModelProperty("子分类列表") + private List children; + + @ApiModelProperty("分类关联的品牌列表") + private List brandList; + + public CategoryVO(Category category) { + BeanUtil.copyProperties(category, this); + } + + public CategoryVO(String id, String createBy, Date createTime, String updateBy, Date updateTime, Boolean deleteFlag, String name, String parentId, Integer level, BigDecimal sortOrder, Double commissionRate, String image, Boolean supportChannel) { + super(id, createBy, createTime, updateBy, updateTime, deleteFlag, name, parentId, level, sortOrder, commissionRate, image, supportChannel); + } + + public List getChildren() { + + if (children != null) { + children.sort(new Comparator() { + @Override + public int compare(CategoryVO o1, CategoryVO o2) { + return o1.getSortOrder().compareTo(o2.getSortOrder()); + } + }); + return children; + } + return null; + } +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/DraftGoodsVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/DraftGoodsVO.java new file mode 100644 index 00000000..f6e67966 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/DraftGoodsVO.java @@ -0,0 +1,33 @@ +package cn.lili.modules.goods.entity.vos; + +import cn.lili.modules.goods.entity.dos.DraftGoods; +import cn.lili.modules.goods.entity.dos.GoodsParams; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 草稿商品VO + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +@Data +public class DraftGoodsVO extends DraftGoods { + + private static final long serialVersionUID = 6377623919990713567L; + + @ApiModelProperty(value = "分类名称") + private List categoryName; + + @ApiModelProperty(value = "商品参数") + private List goodsParamsList; + + @ApiModelProperty(value = "商品图片") + private List goodsGalleryList; + + @ApiModelProperty(value = "sku列表") + private List skuList; +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/ExchangeVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/ExchangeVO.java new file mode 100644 index 00000000..1be9128d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/ExchangeVO.java @@ -0,0 +1,31 @@ +package cn.lili.modules.goods.entity.vos; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 兑换VO + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +@Data +public class ExchangeVO { + + /** 是否允许兑换 */ + @ApiModelProperty(value="是否允许积分兑换") + private Integer enableExchange; + + /** 兑换所需金额 */ + @ApiModelProperty(value="兑换所需金额 ") + private Double exchangeMoney; + + /** 商品所属积分分类 */ + @ApiModelProperty(value="积分兑换所属分类 ") + private Integer categoryId; + + /** 兑换所需积分 */ + @ApiModelProperty(value="积分兑换使用的积分 ") + private Integer exchangePoint; +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsOperateAllowable.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsOperateAllowable.java new file mode 100644 index 00000000..7ea76081 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsOperateAllowable.java @@ -0,0 +1,91 @@ +package cn.lili.modules.goods.entity.vos; + +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 商品操作允许的范围 + * + * @author Bulbasaur + * @date 2020-02-26 23:24:13 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class GoodsOperateAllowable implements Serializable { + + /** + * 上下架状态 + * + * @see GoodsStatusEnum + */ + private String marketEnable; + + /** + * 删除状态 true 已删除 false 未删除 + */ + private Boolean deleteFlag; + + /** + * 是否允许下架 + */ + private Boolean allowDown; + /** + * 是否允许放入回收站 + */ + private Boolean allowDelete; + /** + * 是否允许回收站的商品还原 + */ + private Boolean allowReduction; + /** + * 是否允许回收站的商品彻底删除 + */ + private Boolean allowClear; + /** + * 是否允许上架 + */ + private Boolean allowUpper; + + /** + * 构造函数 + * + * @param marketEnable + * @param deleteFlag + */ + public GoodsOperateAllowable(String marketEnable, Boolean deleteFlag) { + this.marketEnable = marketEnable; + this.deleteFlag = deleteFlag; + } + + + public Boolean getAllowDown() { + //上架状态 不在回收站的商品可以下架 + return marketEnable == GoodsStatusEnum.UPPER.name() && deleteFlag == false; + } + + public Boolean getAllowReduction() { + //下架状态 在回收站的商品可以还原 + return marketEnable == GoodsStatusEnum.DOWN.name() && deleteFlag == true; + } + + public Boolean getAllowClear() { + //下架状态 在回收站的商品可以彻底删除 + return marketEnable == GoodsStatusEnum.DOWN.name() && deleteFlag == true; + } + + public Boolean getAllowUpper() { + //下架状态 未删除的商品可以上架 + return marketEnable == GoodsStatusEnum.DOWN.name() && deleteFlag == false; + } + + public Boolean getAllowDelete() { + //下架状态 未删除的商品可以删除 + return marketEnable == GoodsStatusEnum.DOWN.name() && deleteFlag == false; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsParamsGroupVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsParamsGroupVO.java new file mode 100644 index 00000000..18d0a9d5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsParamsGroupVO.java @@ -0,0 +1,26 @@ +package cn.lili.modules.goods.entity.vos; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 商品参数vo + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +@Data +public class GoodsParamsGroupVO implements Serializable { + private static final long serialVersionUID = 1450550797436233753L; + @ApiModelProperty("参数组关联的参数集合") + private List params; + @ApiModelProperty("参数组名称") + private String groupName; + @ApiModelProperty("参数组id") + private String groupId; + + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsParamsVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsParamsVO.java new file mode 100644 index 00000000..795a22c6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsParamsVO.java @@ -0,0 +1,43 @@ +package cn.lili.modules.goods.entity.vos; + + +import cn.lili.modules.goods.entity.dos.GoodsParams; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 商品关联参数的VO + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +@Data +public class GoodsParamsVO extends GoodsParams { + + private static final long serialVersionUID = -4904700751774005326L; + @ApiModelProperty("1 输入项 2 选择项") + private Integer paramType; + @ApiModelProperty(" 选择项的内容获取值,使用optionList") + private String options; + @ApiModelProperty("是否必填是 1 否 0") + private Integer required; + @ApiModelProperty("参数组id") + private String groupId; + @ApiModelProperty("是否可索引 1 可以 0不可以") + private Integer isIndex; + + private String[] optionList; + + public void setOptionList(String[] optionList) { + this.optionList = optionList; + } + + public String[] getOptionList() { + if (options != null) { + return options.replaceAll("\r|\n", "").split(","); + } + return optionList; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsSkuSpecVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsSkuSpecVO.java new file mode 100644 index 00000000..3fe8a9e7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsSkuSpecVO.java @@ -0,0 +1,27 @@ +package cn.lili.modules.goods.entity.vos; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 商品规格VO + * + * @author paulG + * @date 2020-02-26 23:24:13 + */ +@Data +public class GoodsSkuSpecVO { + + + @ApiModelProperty(value = "商品skuId") + private String skuId; + + @ApiModelProperty(value = "商品sku所包含规格") + private List specValues; + + @ApiModelProperty(value = "库存") + private Integer quantity; + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsSkuVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsSkuVO.java new file mode 100644 index 00000000..bd526f81 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsSkuVO.java @@ -0,0 +1,35 @@ +package cn.lili.modules.goods.entity.vos; + +import cn.hutool.core.bean.BeanUtil; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 商品规格VO + * + * @author paulG + * @date 2020-02-26 23:24:13 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class GoodsSkuVO extends GoodsSku { + + private static final long serialVersionUID = -7651149660489332344L; + + @ApiModelProperty(value = "规格列表") + private List specList; + + @ApiModelProperty(value = "商品图片") + private List goodsGalleryList; + + public GoodsSkuVO(GoodsSku goodsSku) { + BeanUtil.copyProperties(goodsSku, this); + } +} + diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsSpecValueVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsSpecValueVO.java new file mode 100644 index 00000000..ff4bdc63 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsSpecValueVO.java @@ -0,0 +1,32 @@ +package cn.lili.modules.goods.entity.vos; + +import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 规格值VO + * + * @author paulG + * @date 2020-02-26 23:24:13 + */ +@Data +public class GoodsSpecValueVO { + + /** + * 规格值名字 + */ + @TableField(value = "name") + @ApiModelProperty(value = "规格值名字") + private String name; + + /** + * 规格值名字 + */ + @TableField(value = "value") + @ApiModelProperty(value = "规格值") + private List value; + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsVO.java new file mode 100644 index 00000000..6207aac0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/GoodsVO.java @@ -0,0 +1,32 @@ +package cn.lili.modules.goods.entity.vos; + +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.dos.GoodsParams; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 商品VO + * + * @author pikachu + * @date 2020-02-26 23:24:13 + */ +@Data +public class GoodsVO extends Goods { + + private static final long serialVersionUID = 6377623919990713567L; + + @ApiModelProperty(value = "分类名称") + private List categoryName; + + @ApiModelProperty(value = "商品参数") + private List goodsParamsList; + + @ApiModelProperty(value = "商品图片") + private List goodsGalleryList; + + @ApiModelProperty(value = "sku列表") + private List skuList; +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/ParameterGroupVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/ParameterGroupVO.java new file mode 100644 index 00000000..52b9059e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/ParameterGroupVO.java @@ -0,0 +1,31 @@ +package cn.lili.modules.goods.entity.vos; + +import cn.lili.modules.goods.entity.dos.Parameters; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +import java.io.Serializable; +import java.util.List; + +/** + * 参数组vo + * + * @author pikachu + * @date 2020年03月02日 16:55:21 + */ +@ApiModel +@Data +public class ParameterGroupVO implements Serializable { + + private static final long serialVersionUID = 724427321881170297L; + @ApiModelProperty("参数组关联的参数集合") + private List params; + @ApiModelProperty(value = "参数组名称") + private String groupName; + @ApiModelProperty(value = "参数组id") + private String groupId; + + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/SpecValueVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/SpecValueVO.java new file mode 100644 index 00000000..7cf635b3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/SpecValueVO.java @@ -0,0 +1,60 @@ +package cn.lili.modules.goods.entity.vos; + +import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 规格值 + * + * @author Chopper + * @date 2020-02-26 23:24:13 + */ +@Data +public class SpecValueVO implements Serializable { + + private static final long serialVersionUID = -4433579132929428572L; + + @TableField(value = "spec_name_id") + @ApiModelProperty(value = "规格项ID") + private String specNameId; + + @TableField(value = "spec_name") + @ApiModelProperty(value = "规格项名字") + private String specName; + + @TableField(value = "spec_value_id") + @ApiModelProperty(value = "规格值") + private String specValueId; + + @TableField(value = "spec_value") + @ApiModelProperty(value = "规格值") + private String specValue; + + /** + * 规格类型,1图片 0 非图片 + */ + @ApiModelProperty(value = "该规格是否有图片,1 有 0 没有") + private Integer specType; + /** + * 规格图片 + */ + @ApiModelProperty(value = "规格的图片") + private List specImage; + + @Data + public static class SpecImages implements Serializable { + + private static final long serialVersionUID = 1816357809660916086L; + + private String url; + + private String name; + + private String status; + + } +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/SpecificationVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/SpecificationVO.java new file mode 100644 index 00000000..ba1b0c27 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/SpecificationVO.java @@ -0,0 +1,35 @@ +package cn.lili.modules.goods.entity.vos; + +import cn.lili.modules.goods.entity.dos.Specification; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 规格VO + * + * @author Chopper + * @date 2020-02-26 23:24:13 + */ +@Data +@NoArgsConstructor +public class SpecificationVO extends Specification { + + private static final long serialVersionUID = 5504602856844228350L; + + @ApiModelProperty(value = "规格项名称") + private String specValue; + + @ApiModelProperty(value = "分类path") + private String categoryPath; + + public SpecificationVO(String specName, String storeId, String categoryPath) { + + this.setSpecName(specName); + this.setStoreId(storeId); + this.categoryPath = categoryPath; + + + } + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/entity/vos/StockWarningVO.java b/framework/src/main/java/cn/lili/modules/goods/entity/vos/StockWarningVO.java new file mode 100644 index 00000000..0c8aa218 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/entity/vos/StockWarningVO.java @@ -0,0 +1,27 @@ +package cn.lili.modules.goods.entity.vos; + +import cn.lili.modules.goods.entity.dos.GoodsSku; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 库存警告封装类 + * + * @author paulG + * @date 2020/12/24 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class StockWarningVO { + + @ApiModelProperty(value = "库存警告数量") + private Integer stockWarningNum; + + @ApiModelProperty(value = "商品SKU列表") + private IPage goodsSkuIPage; + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/BrandMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/BrandMapper.java new file mode 100644 index 00000000..ded9fee8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/BrandMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.goods.mapper; + + +import cn.lili.modules.goods.entity.dos.Brand; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 商品品牌数据处理层 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface BrandMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/CategoryBrandMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/CategoryBrandMapper.java new file mode 100644 index 00000000..daee3eb8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/CategoryBrandMapper.java @@ -0,0 +1,26 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.CategoryBrand; +import cn.lili.modules.goods.entity.vos.CategoryBrandVO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 商品分类品牌数据处理层 + * + * @author pikachu + * @date 2020-02-26 18:12:56 + */ +public interface CategoryBrandMapper extends BaseMapper { + + /** + * 根据分类id查分类绑定品牌 + * + * @param categoryId 分类id + * @return 分类绑定的品牌列表 + */ + @Select("SELECT b.id,b.name FROM li_brand b INNER join li_category_brand cb on b.id = cb.brand_id and cb.category_id = #{categoryId} where b.delete_flag = 0") + List getCategoryBrandList(String categoryId); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/CategoryMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/CategoryMapper.java new file mode 100644 index 00000000..bb0b1934 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/CategoryMapper.java @@ -0,0 +1,24 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.Category; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + + +/** + * 商品分类数据处理层 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface CategoryMapper extends BaseMapper { + + @Select("SELECT name FROM li_category ${ew.customSqlSegment} ") + List getNamesByIds(@Param(Constants.WRAPPER) Wrapper queryWrapper); + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/CategoryParameterGroupMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/CategoryParameterGroupMapper.java new file mode 100644 index 00000000..7bedb837 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/CategoryParameterGroupMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.CategoryParameterGroup; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 分类绑定参数组数据处理层 + * + * @author pikachu + * @date 2020-03-02 16:44:56 + */ +public interface CategoryParameterGroupMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/CategorySpecificationMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/CategorySpecificationMapper.java new file mode 100644 index 00000000..26913b77 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/CategorySpecificationMapper.java @@ -0,0 +1,27 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.CategorySpecification; +import cn.lili.modules.goods.entity.vos.CategorySpecificationVO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 商品分类规格数据处理层 + * + * @author pikachu + * @date 2020-02-27 15:18:56 + */ +public interface CategorySpecificationMapper extends BaseMapper { + /** + * 根据分类id查分类绑定规格 + * + * @param categoryId 分类id + * @return 分类绑定规格列表 + */ + @Select("select s.id, s.spec_name as `name` from " + + "li_specification s INNER join li_category_specification cs on s.id = cs.specification_id and cs.category_id = #{categoryId} " + + "where s.delete_flag = 0") + List getCategorySpecList(String categoryId); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/DraftGoodsMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/DraftGoodsMapper.java new file mode 100644 index 00000000..0e1137f5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/DraftGoodsMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.DraftGoods; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 草稿商品数据处理层 + * + * @author paulG + * @date 2020/12/19 + **/ +public interface DraftGoodsMapper extends BaseMapper { +} diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsGalleryMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsGalleryMapper.java new file mode 100644 index 00000000..5ce5232f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsGalleryMapper.java @@ -0,0 +1,17 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.GoodsGallery; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + + +/** + * 商品相册数据处理层 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface GoodsGalleryMapper extends BaseMapper { + + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsMapper.java new file mode 100644 index 00000000..928248c7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsMapper.java @@ -0,0 +1,34 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.vos.GoodsVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +/** + * 规格项数据处理层 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface GoodsMapper extends BaseMapper { + + /** + * 下架所有商家商品 + * + * @param storeId + */ + @Update("update li_goods set market_enable = 0 WHERE store_id = #{storeId}") + void underStoreGoods(String storeId); + + @Update("UPDATE li_goods SET comment_num = comment_num + #{commentNum} WHERE id = #{goodsId}") + void addGoodsCommentNum(Integer commentNum, String goodsId); + + @Select("select g.* from li_goods as g ") + IPage queryByParams(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsParamsMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsParamsMapper.java new file mode 100644 index 00000000..37d136f4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsParamsMapper.java @@ -0,0 +1,22 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.GoodsParams; +import cn.lili.modules.goods.entity.vos.GoodsParamsVO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + + +/** + * 商品关联参数数据处理层 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface GoodsParamsMapper extends BaseMapper { + + @Select("select p.*,gp.param_value,p.group_id from li_parameters p left join li_goods_params gp on p.id=gp.param_id and gp.goods_id = #{goodsId} where p.category_id = #{categoryId} order by sort") + List paramList(String goodsId, String categoryId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsSkuMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsSkuMapper.java new file mode 100644 index 00000000..285ac23d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsSkuMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.GoodsSku; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 规格项数据处理层 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface GoodsSkuMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsUnitMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsUnitMapper.java new file mode 100644 index 00000000..912c5a52 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsUnitMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.goods.mapper; + + +import cn.lili.modules.goods.entity.dos.GoodsUnit; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 商品计量单位数据处理层 + * + * @author Bulbasaur + * @date 2020/11/26 16:11 + */ +public interface GoodsUnitMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsWordsMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsWordsMapper.java new file mode 100644 index 00000000..f34e4fa3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/GoodsWordsMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.GoodsWords; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 商品关键字数据处理层 + * + * @author paulG + * @date 2020/10/15 + **/ +public interface GoodsWordsMapper extends BaseMapper { + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/ParametersMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/ParametersMapper.java new file mode 100644 index 00000000..c57dc7d5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/ParametersMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.Parameters; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 商品参数数据处理层 + * + * @author pikachu + * @date 2020-03-02 17:27:56 + */ +public interface ParametersMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/SpecValuesMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/SpecValuesMapper.java new file mode 100644 index 00000000..18e37215 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/SpecValuesMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.goods.mapper; + +import cn.lili.modules.goods.entity.dos.SpecValues; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 规格项数据处理层 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface SpecValuesMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/mapper/SpecificationMapper.java b/framework/src/main/java/cn/lili/modules/goods/mapper/SpecificationMapper.java new file mode 100644 index 00000000..71ec8289 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/mapper/SpecificationMapper.java @@ -0,0 +1,25 @@ +package cn.lili.modules.goods.mapper; + + +import cn.lili.modules.goods.entity.dos.Specification; +import cn.lili.modules.goods.entity.vos.SpecificationVO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import java.util.List; +import java.util.Map; + +/** + * 规格数据处理层 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface SpecificationMapper extends BaseMapper { + + /** + * 查询规格信息列表 + * @param param + * @return + */ + List findSpecList(Map param); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/BrandService.java b/framework/src/main/java/cn/lili/modules/goods/service/BrandService.java new file mode 100644 index 00000000..45d3f2c3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/BrandService.java @@ -0,0 +1,61 @@ +package cn.lili.modules.goods.service; + + +import cn.lili.modules.goods.entity.dos.Brand; +import cn.lili.modules.goods.entity.dto.BrandPageDTO; +import cn.lili.modules.goods.entity.vos.BrandVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 商品品牌业务层 + * + * @author pikachu + * @date 2020-02-18 16:18:56 + */ +public interface BrandService extends IService { + + /** + * 根据条件分页获取品牌列表 + * + * @param page 条件参数 + * @return 品牌列表 + */ + IPage getBrandsByPage(BrandPageDTO page); + + /** + * 根据分类ID获取品牌列表 + * + * @param categoryId 分类ID + * @return 品牌列表 + */ + List getBrandsByCategory(String categoryId); + + /** + * 添加品牌 + * + * @param brandVO 品牌信息 + * @return 添加结果 + */ + boolean addBrand(BrandVO brandVO); + + /** + * 更新品牌 + * + * @param brandVO 品牌信息 + * @return 更新结果 + */ + boolean updateBrand(BrandVO brandVO); + + /** + * 更新品牌是否可用 + * + * @param brandId 品牌ID + * @param disable 是否不可用 + * @return 更新结果 + */ + boolean brandDisable(String brandId, boolean disable); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/CategoryBrandService.java b/framework/src/main/java/cn/lili/modules/goods/service/CategoryBrandService.java new file mode 100644 index 00000000..8ef40842 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/CategoryBrandService.java @@ -0,0 +1,39 @@ +package cn.lili.modules.goods.service; + +import cn.lili.modules.goods.entity.dos.CategoryBrand; +import cn.lili.modules.goods.entity.vos.CategoryBrandVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 商品分类品牌业务层 + * + * @author pikachu + * @date 2020-02-26 16:18:56 + */ +public interface CategoryBrandService extends IService { + /** + * 根据分类id查询品牌信息 + * + * @param categoryId 分类id + * @return 分类品牌关联信息列表 + */ + List getCategoryBrandList(String categoryId); + + /** + * 通过分类ID删除关联品牌 + * @param categoryId 品牌ID + */ + void deleteByCategoryId(String categoryId); + + + /** + * 根据品牌ID获取分类品牌关联信息 + * + * @param brandId 品牌ID + * @return 分类品牌关联信息 + */ + List getCategoryBrandListByBrandId(String brandId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/CategoryParameterGroupService.java b/framework/src/main/java/cn/lili/modules/goods/service/CategoryParameterGroupService.java new file mode 100644 index 00000000..a870b4b8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/CategoryParameterGroupService.java @@ -0,0 +1,34 @@ +package cn.lili.modules.goods.service; + +import cn.lili.modules.goods.entity.dos.CategoryParameterGroup; +import cn.lili.modules.goods.entity.vos.ParameterGroupVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 分类绑定参数组业务层 + * + * @author pikachu + * @date 2020-03-02 16:44:56 + */ +public interface CategoryParameterGroupService extends IService { + + /** + * 查询分类绑定参数集合 + * + * @param categoryId 分类Id + * @return 分类参数 + */ + List getCategoryParams(String categoryId); + + + /** + * 查询分类绑定参数组信息 + * + * @param categoryId 分类id + * @return 参数组列表 + */ + List getCategoryGroup(String categoryId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/CategoryService.java b/framework/src/main/java/cn/lili/modules/goods/service/CategoryService.java new file mode 100644 index 00000000..94216d9f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/CategoryService.java @@ -0,0 +1,105 @@ +package cn.lili.modules.goods.service; + + +import cn.lili.modules.goods.entity.dos.Category; +import cn.lili.modules.goods.entity.vos.CategoryVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 商品分类业务层 + * + * @author pikachu + * @date 2020-03-02 16:44:56 + */ +public interface CategoryService extends IService { + + + /** + * 管理端获取所有分类 + * 即获取的对象不管是否删除,都要展示,而且不从缓存获取,保证内容是最新的 + * + * @return 商品分类列表 + */ + List dbList(String parentId); + + /** + * 获取分类树 + * + * @return 分类树 + */ + List categoryTree(); + + /** + * 查询所有的分类,父子关系 + * + * @return 所有的分类,父子关系 + */ + List listAllChildren(String parentId); + + /** + * 查询所有的分类,父子关系 数据库获取 + * + * @return 所有的分类,父子关系 + */ + List listAllChildrenDB(); + + /** + * 获取指定分类的分类名称 + * + * @param ids 指定分类id集合 + * @return 分类名称集合 + */ + List getCategoryNameByIds(List ids); + + /** + * 获取商品分类list + * + * @return 商品分类list + */ + List findByAllBySortOrder(Category category); + + /** + * 添加商品分类 + * + * @param category 商品分类信息 + * @return 添加结果 + */ + boolean saveCategory(Category category); + + /** + * 修改商品分类 + * + * @param category 商品分类信息 + * @return 修改结果 + */ + void updateCategory(Category category); + + /** + * 批量删除分类 + * + * @param id 分类ID + */ + void delete(String id); + + /** + * 分类状态的更改 + * + * @param categoryId 商品分类ID + * @param enableOperations 是否可用 + */ + void updateCategoryStatus(String categoryId, Boolean enableOperations); + + /** + * 获取商家经营类目 + */ + List getStoreCategory(String[] categories); + + /** + * 获取一级分类列表 + * 用于商家入驻选择 + */ + List firstCategory(); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/CategorySpecificationService.java b/framework/src/main/java/cn/lili/modules/goods/service/CategorySpecificationService.java new file mode 100644 index 00000000..38725044 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/CategorySpecificationService.java @@ -0,0 +1,38 @@ +package cn.lili.modules.goods.service; + +import cn.lili.modules.goods.entity.dos.CategorySpecification; +import cn.lili.modules.goods.entity.vos.CategorySpecificationVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 商品分类规格业务层 + * + * @author pikachu + * @date 2020-02-26 16:18:56 + */ +public interface CategorySpecificationService extends IService { + /** + * 根据分类id查询规格信息 + * + * @param categoryId 分类id + * @return 分类规格关联信息 + */ + List getCategorySpecList(String categoryId); + + /*** + * 根据分类id查询规格信息 + * + * @param categoryId 分类id + * @return 分类规格关联信息 + */ + List getCategorySpecList(String[] categoryId); + + /** + * 通过分类ID删除关联规格 + * + * @param categoryId 分类ID + */ + void deleteByCategoryId(String categoryId); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/DraftGoodsService.java b/framework/src/main/java/cn/lili/modules/goods/service/DraftGoodsService.java new file mode 100644 index 00000000..c59300ce --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/DraftGoodsService.java @@ -0,0 +1,64 @@ +package cn.lili.modules.goods.service; + +import cn.lili.modules.goods.entity.dos.DraftGoods; +import cn.lili.modules.goods.entity.dto.DraftGoodsDTO; +import cn.lili.modules.goods.entity.dto.DraftGoodsSearchParams; +import cn.lili.modules.goods.entity.vos.DraftGoodsVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 草稿商品业务层 + * + * @author paulG + * @since 2020/12/19 + **/ +public interface DraftGoodsService extends IService { + + /** + * 添加草稿商品 + * + * @param draftGoods 草稿商品 + * @return 是否添加成功 + */ + boolean addGoodsDraft(DraftGoodsDTO draftGoods); + + /** + * 更新草稿商品 + * + * @param draftGoods 草稿商品 + * @return 是否更新成功 + */ + boolean updateGoodsDraft(DraftGoodsDTO draftGoods); + + /** + * 保存草稿商品 + * + * @param draftGoodsVO 草稿商品 + */ + void saveGoodsDraft(DraftGoodsDTO draftGoodsVO); + + /** + * 根据ID删除草稿商品 + * + * @param id 草稿商品ID + */ + void deleteGoodsDraft(String id); + + /** + * 获取草稿商品详情 + * + * @param id 草稿商品ID + * @return 草稿商品详情 + */ + DraftGoodsVO getDraftGoods(String id); + + /** + * 分页获取草稿商品 + * + * @param searchParams 查询参数 + * @return 草稿商品 + */ + IPage getDraftGoods(DraftGoodsSearchParams searchParams); + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/service/GoodsGalleryService.java b/framework/src/main/java/cn/lili/modules/goods/service/GoodsGalleryService.java new file mode 100644 index 00000000..501e22f1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/GoodsGalleryService.java @@ -0,0 +1,39 @@ +package cn.lili.modules.goods.service; + +import cn.lili.modules.goods.entity.dos.GoodsGallery; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 商品相册业务层 + * + * @author pikachu + * 2020-03-13 16:18:56 + */ +public interface GoodsGalleryService extends IService { + /** + * 添加商品相册 + * + * @param goodsGalleryList 商品相册列表 + * @param goodsId 商品ID + */ + void add(List goodsGalleryList, String goodsId); + + /** + * 根据原图获取缩略图 + * + * @param origin 原图地址 + * @return 商品相册 + */ + GoodsGallery getGoodsGallery(String origin); + + /** + * 根据商品 id查询商品相册原图 + * + * @param goodsId 商品ID + * @return 商品相册列表 + */ + List goodsGalleryList(String goodsId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/GoodsParamsService.java b/framework/src/main/java/cn/lili/modules/goods/service/GoodsParamsService.java new file mode 100644 index 00000000..c0ea0bd1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/GoodsParamsService.java @@ -0,0 +1,61 @@ +package cn.lili.modules.goods.service; + +import cn.lili.modules.goods.entity.dos.GoodsParams; +import cn.lili.modules.goods.entity.vos.GoodsParamsGroupVO; +import cn.lili.modules.goods.entity.vos.GoodsParamsVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + + +/** + * 商品关联参数业务层 + * + * @author pikachu + * @date 2020-03-13 16:18:56 + */ +public interface GoodsParamsService extends IService { + + /** + * 添加商品参数 + * @param paramList 参数列表 + * @param goodsId 商品ID + */ + void addParams(List paramList, String goodsId); + + /** + * 获取商品关联参数 + * + * @param goodsId 商品ID + * @return 商品关联参数 + */ + List getGoodsParamsByGoodsId(String goodsId); + + /** + * 添加商品参数 + * + * @param goodsParamsVO 商品参数 + * @return 添加是否成功 + */ + boolean addParams(GoodsParamsVO goodsParamsVO); + + /** + * 根据分类id查询绑定参数信息 + * + * @param categoryId 分类id + * @param goodsId 商品id + * @return 分类id + */ + List paramList(String categoryId, String goodsId); + + /** + * 查询商品参数信息 + * + * @param goodsId 商品id + * @param categoryId 分了id + * @return 商品参数信息 + */ + List queryGoodsParams(String goodsId, String categoryId); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/GoodsService.java b/framework/src/main/java/cn/lili/modules/goods/service/GoodsService.java new file mode 100644 index 00000000..c84f0a97 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/GoodsService.java @@ -0,0 +1,125 @@ +package cn.lili.modules.goods.service; + +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.dto.GoodsOperationDTO; +import cn.lili.modules.goods.entity.dto.GoodsSearchParams; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.entity.vos.GoodsVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 商品业务层 + * + * @author pikachu + * @date 2020-02-23 16:18:56 + */ +public interface GoodsService extends IService { + + + /** + * 下架所有商家商品 + * + * @param storeId 店铺ID + */ + void underStoreGoods(String storeId); + + /** + * 获取某分类下的商品数量 + * + * @param categoryId 分类ID + * @return 商品数量 + */ + Integer getGoodsCountByCategory(String categoryId); + + /** + * 添加商品 + * + * @param goodsOperationDTO 商品查询条件 + */ + void addGoods(GoodsOperationDTO goodsOperationDTO); + + /** + * 修改商品 + * + * @param goodsOperationDTO 商品查询条件 + */ + void editGoods(GoodsOperationDTO goodsOperationDTO, String goodsId); + + /** + * 查询商品VO + * + * @param goodsId 商品id + * @return 商品VO + */ + GoodsVO getGoodsVO(String goodsId); + + /** + * 商品查询 + * + * @param goodsSearchParams 查询参数 + */ + IPage queryByParams(GoodsSearchParams goodsSearchParams); + + /** + * 批量审核商品 + * + * @param goodsIds 商品id列表 + * @param goodsAuthEnum 审核操作 + * @return 审核结果 + */ + boolean auditGoods(List goodsIds, GoodsAuthEnum goodsAuthEnum); + + /** + * 获取所有的已上架的商品数量 + * + * @return 所有的已上架的商品数量 + */ + Integer goodsNum(GoodsStatusEnum goodsStatusEnum,GoodsAuthEnum goodsAuthEnum); + + /** + * 获取今天的已上架的商品数量 + * + * @return 今天的已上架的商品数量 + */ + Integer todayUpperNum(); + + /* + * 更新商品上架状态状态 + * + * @param goodsIds 商品ID集合 + * @param goodsStatusEnum 更新的商品状态 + * @param underReason 下架原因 + * @return 更新结果 + */ + Boolean updateGoodsMarketAble(List goodsIds, GoodsStatusEnum goodsStatusEnum, String underReason); + + /** + * 删除商品 + * + * @param goodsIds 商品ID + * @return 操作结果 + */ + Boolean deleteGoods(List goodsIds); + + /** + * 设置商品运费模板 + * + * @param goodsIds 商品列表 + * @param freightPayer 承担运费者 + * @param templateId 运费模板ID + * @return 操作结果 + */ + Boolean freight(List goodsIds, String freightPayer, String templateId); + + /** + * 修改商品库存数量 + * + * @param goodsId 商品ID + * @param quantity 库存数量 + */ + void updateStock(String goodsId, Integer quantity); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/GoodsSkuService.java b/framework/src/main/java/cn/lili/modules/goods/service/GoodsSkuService.java new file mode 100644 index 00000000..b7f14218 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/GoodsSkuService.java @@ -0,0 +1,164 @@ +package cn.lili.modules.goods.service; + +import cn.lili.common.cache.CachePrefix; +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.dto.GoodsSearchParams; +import cn.lili.modules.goods.entity.dto.GoodsSkuStockDTO; +import cn.lili.modules.goods.entity.vos.GoodsSkuSpecVO; +import cn.lili.modules.goods.entity.vos.GoodsSkuVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; +import java.util.Map; + +/** + * 商品sku业务层 + * + * @author pikachu + * @date 2020-02-24 16:18:56 + */ +public interface GoodsSkuService extends IService { + + static String getCacheKeys(String id) { + return CachePrefix.GOODS_SKU.getPrefix() + id; + } + + static String getStockCacheKey(String id) { + return CachePrefix.SKU_STOCK.getPrefix() + id; + } + + /** + * 添加商品sku + * + * @param skuList sku列表 + * @param goods 商品信息 + */ + void add(List> skuList, Goods goods); + + /** + * 更新商品sku + * + * @param skuList sku列表 + * @param goods 商品信息 + * @param regeneratorSkuFlag 是否是否重新生成sku + */ + void update(List> skuList, Goods goods, Boolean regeneratorSkuFlag); + + /** + * 更新商品sku + * + * @param goodsSku sku信息 + */ + void update(GoodsSku goodsSku); + + /** + * 从redis缓存中获取商品SKU信息 + * + * @param id SkuId + * @return 商品SKU信息 + */ + GoodsSku getGoodsSkuByIdFromCache(String id); + + /** + * 获取商品sku详情 + * + * @param goodsId 商品ID + * @param skuId skuID + * @return 商品sku详情 + */ + Map getGoodsSkuDetail(String goodsId, String skuId); + + /** + * 根据商品分组商品sku及其规格信息 + * + * @param goodsId 商品id + * @return 分组后的商品sku及其规格信息 + */ + List groupBySkuAndSpec(String goodsId); + + /** + * 批量从redis中获取商品SKU信息 + * + * @param ids SkuId集合 + * @return 商品SKU信息集合 + */ + List getGoodsSkuByIdFromCache(List ids); + + /** + * 获取goodsId下所有的goodsSku + * + * @param goodsId 商品id + * @return goodsSku列表 + */ + List getGoodsListByGoodsId(String goodsId); + + /** + * 根据goodsSku组装goodsSkuVO + * + * @param list 商品id + * @return goodsSku列表 + */ + List getGoodsSkuVOList(List list); + + /** + * 根据goodsSku组装goodsSkuVO + * + * @param goodsSku 商品规格 + * @return goodsSku列表 + */ + GoodsSkuVO getGoodsSkuVO(GoodsSku goodsSku); + + /** + * 分页查询商品sku信息 + * + * @param searchParams 查询参数 + * @return 商品sku信息 + */ + IPage getGoodsSkuByPage(GoodsSearchParams searchParams); + + /** + * 更新商品sku状态 + * + * @param goods 商品信息(Id,MarketEnable/IsAuth) + */ + void updateGoodsSkuStatus(Goods goods); + + /** + * 更新SKU库存 + * + * @param goodsSkuStockDTOS sku库存修改实体 + */ + void updateStocks(List goodsSkuStockDTOS); + + /** + * 更新SKU库存 + * + * @param skuId SKUId + * @param quantity 设置的库存数量 + */ + void updateStock(String skuId, Integer quantity); + + /** + * 获取商品sku库存 + * + * @param skuId 商品skuId + * @return 库存数量 + */ + Integer getStock(String skuId); + + /** + * 修改商品库存字段 + * + * @param goodsSkus + */ + void updateGoodsStuck(List goodsSkus); + + /** + * 更新SKU评价数量 + * + * @param skuId SKUId + */ + void updateGoodsSkuCommentNum(String skuId); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/GoodsUnitService.java b/framework/src/main/java/cn/lili/modules/goods/service/GoodsUnitService.java new file mode 100644 index 00000000..3b4e2cf3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/GoodsUnitService.java @@ -0,0 +1,15 @@ +package cn.lili.modules.goods.service; + + +import cn.lili.modules.goods.entity.dos.GoodsUnit; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 商品计量单位业务层 + * + * @author Bulbasaur + * @date: 2020/11/26 16:12 + */ +public interface GoodsUnitService extends IService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/GoodsWordsService.java b/framework/src/main/java/cn/lili/modules/goods/service/GoodsWordsService.java new file mode 100644 index 00000000..11f47b4d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/GoodsWordsService.java @@ -0,0 +1,13 @@ +package cn.lili.modules.goods.service; + +import cn.lili.modules.goods.entity.dos.GoodsWords; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 商品关键字业务层 + * + * @author paulG + * @since 2020/10/15 + **/ +public interface GoodsWordsService extends IService { +} diff --git a/framework/src/main/java/cn/lili/modules/goods/service/ParametersService.java b/framework/src/main/java/cn/lili/modules/goods/service/ParametersService.java new file mode 100644 index 00000000..6c438b22 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/ParametersService.java @@ -0,0 +1,14 @@ +package cn.lili.modules.goods.service; + +import cn.lili.modules.goods.entity.dos.Parameters; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 商品参数业务层 + * + * @author pikachu + * @date 2020-03-02 16:18:56 + */ +public interface ParametersService extends IService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/SpecValuesService.java b/framework/src/main/java/cn/lili/modules/goods/service/SpecValuesService.java new file mode 100644 index 00000000..c4947e5c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/SpecValuesService.java @@ -0,0 +1,63 @@ +package cn.lili.modules.goods.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.SpecValues; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + + +/** + * 规格项接口 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface SpecValuesService extends IService { + + /** + * 保存规格值 + * + * @param specId 规格Id + * @param valueList 规格值集合 + */ + List saveSpecValue(String specId, String[] valueList); + + /** + * 保存规格值 + * + * @param specId 规格Id + * @param valueList 规格值集合 + */ + List addSpecValue(String specId, String[] valueList); + + /** + * 根据规格id查询规格值信息 + * + * @param specIds 规格值ids + * @return + */ + List getSpecValues(List specIds); + + /** + * 根据值获取规格值信息 + * 如果不存在则自动创建 + * + * @param specValue 规格值 + * @param specId 规格ID + * @return 规格值信息 + */ + SpecValues getSpecValues(String specValue, String specId); + + /** + * 分页获取规格值 + * + * @param specId 规格项ID + * @param specVal 规格值 + * @param pageVo 分页参数 + * @return 规格值列表 + */ + IPage queryByParams(String specId, String specVal, PageVO pageVo); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/service/SpecificationService.java b/framework/src/main/java/cn/lili/modules/goods/service/SpecificationService.java new file mode 100644 index 00000000..2d86c38c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/service/SpecificationService.java @@ -0,0 +1,89 @@ +package cn.lili.modules.goods.service; + + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.Specification; +import cn.lili.modules.goods.entity.dto.SpecificationSearchParams; +import cn.lili.modules.goods.entity.vos.GoodsSpecValueVO; +import cn.lili.modules.goods.entity.vos.SpecificationVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; +import java.util.Map; + +/** + * 规格业务层 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +public interface SpecificationService extends IService { + + /** + * 查询规格信息列表 + * + * @param param 查询参数 + * @return 规格列表 + */ + List getSpecList(Map param); + + /** + * 根据分类id获取规格信息 + * + * @param categoryId 分类id + * @return 商品规格值列表 + */ + List getGoodsSpecValue(String categoryId); + + /** + * 获取规格详情 + * + * @param id 规格ID + * @return 规格详情 + */ + Specification getSpecification(String id); + + /** + * 获取规格分页 + * + * @param searchParams 搜索参数 + * @param pageVo 分页参数 + * @return 规格分页 + */ + IPage getSpecificationPage(SpecificationSearchParams searchParams, PageVO pageVo); + + /** + * 获取规格分页 + * + * @param searchParams 搜索参数 + * @param pageVo 分页参数 + * @return 规格分页 + */ + IPage getSpecificationByPage(SpecificationSearchParams searchParams, PageVO pageVo); + + /** + * 添加规格 + * + * @param specificationVO 规格信息 + * @return 是否添加成功 + */ + Specification addSpecification(SpecificationVO specificationVO); + + /** + * 修改规格 + * + * @param specificationVO 规格信息 + * @return 是否修改成功 + */ + boolean updateSpecification(SpecificationVO specificationVO); + + /** + * 删除规格 + * + * @param ids 规格ID + * @return 是否删除成功 + */ + boolean deleteSpecification(List ids); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/BrandServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/BrandServiceImpl.java new file mode 100644 index 00000000..3d567979 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/BrandServiceImpl.java @@ -0,0 +1,97 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.PageUtil; +import cn.lili.modules.goods.entity.dos.Brand; +import cn.lili.modules.goods.entity.dos.CategoryBrand; +import cn.lili.modules.goods.entity.dto.BrandPageDTO; +import cn.lili.modules.goods.entity.vos.BrandVO; +import cn.lili.modules.goods.mapper.BrandMapper; +import cn.lili.modules.goods.service.BrandService; +import cn.lili.modules.goods.service.CategoryBrandService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + + +/** + * 商品品牌业务层实现 + * + * @author pikachu + * @date 2020-02-18 16:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BrandServiceImpl extends ServiceImpl implements BrandService { + //分类品牌绑定 + private final CategoryBrandService categoryBrandService; + + @Override + public IPage getBrandsByPage(BrandPageDTO page) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (page.getName() != null) { + queryWrapper.like(Brand::getName, page.getName()); + } + return this.page(PageUtil.initPage(page), queryWrapper); + } + + @Override + public List getBrandsByCategory(String categoryId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("category_id", categoryId); + List list = categoryBrandService.list(queryWrapper); + if (list != null && !list.isEmpty()) { + List collect = list.stream().map(CategoryBrand::getBrandId).collect(Collectors.toList()); + return this.list(new LambdaQueryWrapper().in(Brand::getId, collect)); + } + return new ArrayList<>(); + } + + @Override + public boolean addBrand(BrandVO brandVO) { + + if (getOne(new LambdaQueryWrapper().eq(Brand::getName, brandVO.getName())) != null) { + throw new ServiceException("品牌名称重复!"); + } + return this.save(brandVO); + } + + @Override + public boolean updateBrand(BrandVO brandVO) { + this.checkExist(brandVO.getId()); + if (getOne(new LambdaQueryWrapper().eq(Brand::getName, brandVO.getName()).ne(Brand::getId, brandVO.getId())) != null) { + throw new ServiceException("品牌名称重复!"); + } + return this.updateById(brandVO); + } + + @Override + public boolean brandDisable(String brandId, boolean disable) { + Brand brand = this.checkExist(brandId); + if (Boolean.TRUE.equals(disable) && !categoryBrandService.getCategoryBrandListByBrandId(brandId).isEmpty()) { + throw new ServiceException("当前品牌下存在分类不可禁用"); + } + brand.setDeleteFlag(disable); + return updateById(brand); + } + + private Brand checkExist(String brandId) { + Brand brand = getById(brandId); + if (brand == null) { + log.error("品牌ID为" + brandId + "的品牌不存在"); + throw new ServiceException(); + } + return brand; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategoryBrandServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategoryBrandServiceImpl.java new file mode 100644 index 00000000..d4fe044b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategoryBrandServiceImpl.java @@ -0,0 +1,44 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.lili.modules.goods.entity.dos.CategoryBrand; +import cn.lili.modules.goods.entity.vos.CategoryBrandVO; +import cn.lili.modules.goods.mapper.CategoryBrandMapper; +import cn.lili.modules.goods.service.CategoryBrandService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 规格项业务层实现 + * + * @author pikachu + * @date 2020-02-18 16:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategoryBrandServiceImpl extends ServiceImpl implements CategoryBrandService { + //分类品牌绑定 + private final CategoryBrandMapper categoryBrandMapper; + + @Override + public List getCategoryBrandList(String categoryId) { + return categoryBrandMapper.getCategoryBrandList(categoryId); + } + + @Override + public void deleteByCategoryId(String categoryId) { + categoryBrandMapper.delete(new LambdaUpdateWrapper().eq(CategoryBrand::getCategoryId, categoryId)); + } + + @Override + public List getCategoryBrandListByBrandId(String brandId) { + return this.list(new LambdaQueryWrapper().eq(CategoryBrand::getBrandId, brandId)); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategoryParameterGroupServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategoryParameterGroupServiceImpl.java new file mode 100644 index 00000000..13ed9b7d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategoryParameterGroupServiceImpl.java @@ -0,0 +1,78 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.lili.modules.goods.entity.dos.CategoryParameterGroup; +import cn.lili.modules.goods.entity.dos.Parameters; +import cn.lili.modules.goods.entity.vos.ParameterGroupVO; +import cn.lili.modules.goods.mapper.CategoryParameterGroupMapper; +import cn.lili.modules.goods.service.CategoryParameterGroupService; +import cn.lili.modules.goods.service.ParametersService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 分类绑定参数组接口实现 + * + * @author pikachu + * @version v1.0 + * @since v1.0 + * 2020-03-02 16:45:03 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategoryParameterGroupServiceImpl extends ServiceImpl implements CategoryParameterGroupService { + //商品参数 + private final ParametersService parametersService; + + @Override + public List getCategoryParams(String categoryId) { + //根据id查询参数组 + List groups = this.getCategoryGroup(categoryId); + //查询参数 + List params = parametersService.list(new QueryWrapper().eq("category_id", categoryId)); + //组合参数vo + return convertParamList(groups, params); + } + + @Override + public List getCategoryGroup(String categoryId) { + return this.list(new QueryWrapper().eq("category_id", categoryId)); + } + + /** + * 拼装参数组和参数的返回值 + * + * @param groupList 参数组list + * @param paramList 商品参数list + * @return + */ + public List convertParamList(List groupList, List paramList) { + Map> map = new HashMap<>(paramList.size()); + for (Parameters param : paramList) { + List list = map.get(param.getGroupId()); + if (list == null) { + list = new ArrayList<>(); + } + list.add(param); + map.put(param.getGroupId(), list); + } + List resList = new ArrayList<>(); + for (CategoryParameterGroup group : groupList) { + ParameterGroupVO groupVo = new ParameterGroupVO(); + groupVo.setGroupId(group.getId()); + groupVo.setGroupName(group.getGroupName()); + groupVo.setParams(map.get(group.getId()) == null ? new ArrayList<>() : map.get(group.getId())); + resList.add(groupVo); + } + return resList; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategoryServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategoryServiceImpl.java new file mode 100644 index 00000000..dfd6f47e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategoryServiceImpl.java @@ -0,0 +1,322 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.goods.entity.dos.Category; +import cn.lili.modules.goods.entity.dos.CategoryParameterGroup; +import cn.lili.modules.goods.entity.vos.CategoryVO; +import cn.lili.modules.goods.entity.vos.GoodsParamsGroupVO; +import cn.lili.modules.goods.entity.vos.GoodsParamsVO; +import cn.lili.modules.goods.mapper.CategoryMapper; +import cn.lili.modules.goods.service.CategoryService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + + +/** + * 商品分类业务层实现 + * + * @author pikachu + * @date 2020-02-23 15:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategoryServiceImpl extends ServiceImpl implements CategoryService { + + private static final String DELETE_FLAG_COLUMN = "delete_flag"; + //缓存 + private final Cache cache; + //分类 + private final CategoryMapper categoryMapper; + + @Override + public List dbList(String parentId) { + return this.list(new LambdaQueryWrapper().eq(Category::getParentId, parentId)); + } + + @Override + public List categoryTree() { + if (cache.hasKey(CachePrefix.CATEGORY.getPrefix() + "tree")) { + return (List) cache.get(CachePrefix.CATEGORY.getPrefix() + "tree"); + } + + // 获取全部分类 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Category::getDeleteFlag, false); + List list = this.list(queryWrapper); + + // 构造分类树 + List categoryVOList = new ArrayList<>(); + for (Category category : list) { + if (category.getParentId().equals("0")) { + CategoryVO categoryVO = new CategoryVO(category); + categoryVO.setChildren(findChildren(list, categoryVO)); + categoryVOList.add(categoryVO); + } + } + categoryVOList.sort(new Comparator() { + @Override + public int compare(CategoryVO o1, CategoryVO o2) { + return o1.getSortOrder().compareTo(o2.getSortOrder()); + } + }); + if (categoryVOList.size() != 0) { + cache.put(CachePrefix.CATEGORY.getPrefix() + "tree", categoryVOList); + } + return categoryVOList; + } + + @Override + public List getStoreCategory(String[] categories) { + List arr = Arrays.asList(categories.clone()); + List categoryVOS = categoryTree().stream() + .filter(item -> arr.contains(item.getId())).collect(Collectors.toList()); + return categoryVOS; + + } + + @Override + public List firstCategory() { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq("level", 0); + return list(queryWrapper); + } + + @Override + public List listAllChildren(String parentId) { + if (parentId.equals("0")) { + return categoryTree(); + } + //循环代码,找到对象,把他的子分类返回 + List topCatList = categoryTree(); + for (CategoryVO item : topCatList) { + if (item.getId().equals(parentId)) { + return item.getChildren(); + } + return getChildren(parentId, item.getChildren()); + } + return new ArrayList<>(); + } + + @Override + public List listAllChildrenDB() { + + // 获取全部分类 + List list = this.list(); + + // 构造分类树 + List categoryVOList = new ArrayList<>(); + for (Category category : list) { + if (category.getParentId().equals("0")) { + CategoryVO categoryVO = new CategoryVO(category); + categoryVO.setChildren(findChildren(list, categoryVO)); + categoryVOList.add(categoryVO); + } + } + categoryVOList.sort(new Comparator() { + @Override + public int compare(CategoryVO o1, CategoryVO o2) { + return o1.getSortOrder().compareTo(o2.getSortOrder()); + } + }); + return categoryVOList; + } + + /** + * 获取指定分类的分类名称 + * + * @param ids 指定分类id集合 + * @return 分类名称集合 + */ + @Override + public List getCategoryNameByIds(List ids) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(Category::getId, ids); + return this.baseMapper.getNamesByIds(queryWrapper); + } + + @Override + public List findByAllBySortOrder(Category category) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq(category.getLevel() != null, "level", category.getLevel()) + .eq(StrUtil.isNotBlank(category.getName()), "name", category.getName()) + .eq(category.getParentId() != null, "parent_id", category.getParentId()) + .ne(category.getId() != null, "id", category.getId()) + .eq(DELETE_FLAG_COLUMN, false) + .orderByAsc("sort_order"); + return this.categoryMapper.selectList(queryWrapper); + } + + @Override + public boolean saveCategory(Category category) { + if (category.getParentId() != null && !category.getParentId().equals("0")) { + Category parentCategory = this.getById(category.getParentId()); + category.setDeleteFlag(parentCategory.getDeleteFlag()); + } + removeCache(); + return true; + } + + @Override + public void updateCategory(Category category) { + if (category.getParentId() != null && !category.getParentId().equals("0")) { + Category parentCategory = this.getById(category.getParentId()); + if (!parentCategory.getDeleteFlag().equals(category.getDeleteFlag())) { + throw new ServiceException("子类状态不能与父类不一致!"); + } + } + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", category.getId()) + .set("name", category.getName()) + .set("image", category.getImage()) + .set("sort_order", category.getSortOrder()) + .set(DELETE_FLAG_COLUMN, category.getDeleteFlag()) + .set("commission_rate", category.getCommissionRate()); + categoryMapper.update(category, updateWrapper); + removeCache(); + } + + + @Override + public void delete(String id) { + this.removeById(id); + removeCache(); + } + + @Override + public void updateCategoryStatus(String categoryId, Boolean enableOperations) { + // 禁用子分类 + CategoryVO categoryVO = new CategoryVO(this.getById(categoryId)); + List ids = new ArrayList<>(); + ids.add(categoryVO.getId()); + this.findAllChild(categoryVO); + this.findAllChildIds(categoryVO, ids); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.in(Category::getId, ids); + updateWrapper.set(Category::getDeleteFlag, enableOperations); + this.update(updateWrapper); + removeCache(); + } + + /** + * 递归树形VO + * + * @param categories 分类列表 + * @param categoryVO 分类VO + * @return 分类VO列表 + */ + private List findChildren(List categories, CategoryVO categoryVO) { + List children = new ArrayList<>(); + categories.forEach(item -> { + if (item.getParentId().equals(categoryVO.getId())) { + CategoryVO temp = new CategoryVO(item); + temp.setChildren(findChildren(categories, temp)); + children.add(temp); + } + }); + + return children; + } + + /** + * 条件查询分类 + * + * @param category 分类VO + */ + private void findAllChild(CategoryVO category) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Category::getParentId, category.getId()); + List categories = this.list(queryWrapper); + List categoryVOS = new ArrayList<>(); + for (Category category1 : categories) { + categoryVOS.add(new CategoryVO(category1)); + } + category.setChildren(categoryVOS); + if (!categoryVOS.isEmpty()) { + categoryVOS.forEach(this::findAllChild); + } + } + + /** + * 拼装返回值 + * + * @param paramList 参数列表 + * @return 拼装后的返回值 + */ + private List convertParamList(List groupList, List paramList) { + Map> map = new HashMap<>(16); + for (GoodsParamsVO param : paramList) { + if (map.get(param.getGroupId()) != null) { + map.get(param.getGroupId()).add(param); + } else { + List list = new ArrayList<>(); + list.add(param); + map.put(param.getGroupId(), list); + } + } + List resList = new ArrayList<>(); + for (CategoryParameterGroup group : groupList) { + GoodsParamsGroupVO list = new GoodsParamsGroupVO(); + list.setGroupName(group.getGroupName()); + list.setGroupId(group.getId()); + list.setParams(map.get(group.getId())); + resList.add(list); + } + return resList; + } + + /** + * 获取所有的子分类ID + * + * @param category 分类 + * @param ids ID列表 + */ + private void findAllChildIds(CategoryVO category, List ids) { + if (category.getChildren() != null && !category.getChildren().isEmpty()) { + for (CategoryVO child : category.getChildren()) { + ids.add(child.getId()); + this.findAllChildIds(child, ids); + } + } + } + + /** + * 递归自身,找到id等于parentId的对象,获取他的children 返回 + * + * @param parentId 父ID + * @param categoryVOS 分类VO + * @return 子分类列表VO + */ + private List getChildren(String parentId, List categoryVOS) { + for (CategoryVO item : categoryVOS) { + if (item.getId().equals(parentId)) { + return item.getChildren(); + } + if (item.getChildren() != null && item.getChildren().size() > 0) { + return getChildren(parentId, categoryVOS); + } + } + return null; + } + + /** + * 清除缓存 + */ + private void removeCache() { + cache.remove(CachePrefix.CATEGORY.getPrefix() + "tree"); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategorySpecificationServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategorySpecificationServiceImpl.java new file mode 100644 index 00000000..67f4d626 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/CategorySpecificationServiceImpl.java @@ -0,0 +1,44 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.lili.modules.goods.entity.dos.CategorySpecification; +import cn.lili.modules.goods.entity.vos.CategorySpecificationVO; +import cn.lili.modules.goods.mapper.CategorySpecificationMapper; +import cn.lili.modules.goods.service.CategorySpecificationService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; + +/** + * 商品分类规格业务层实现 + * + * @author pikachu + * @date 2020-02-23 15:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategorySpecificationServiceImpl extends ServiceImpl implements CategorySpecificationService { + + private final CategorySpecificationMapper categorySpecificationMapper; + + @Override + public List getCategorySpecList(String categoryId) { + return categorySpecificationMapper.getCategorySpecList(categoryId); + } + + @Override + public List getCategorySpecList(String[] categoryId) { + return this.list(new LambdaQueryWrapper().in(CategorySpecification::getCategoryId, Arrays.asList(categoryId))); + } + + @Override + public void deleteByCategoryId(String categoryId) { + categorySpecificationMapper.delete(new LambdaQueryWrapper().eq(CategorySpecification::getCategoryId,categoryId)); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/DraftGoodsServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/DraftGoodsServiceImpl.java new file mode 100644 index 00000000..6b674caf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/DraftGoodsServiceImpl.java @@ -0,0 +1,150 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.goods.entity.dos.*; +import cn.lili.modules.goods.entity.dto.DraftGoodsDTO; +import cn.lili.modules.goods.entity.dto.DraftGoodsSearchParams; +import cn.lili.modules.goods.entity.vos.DraftGoodsVO; +import cn.lili.modules.goods.mapper.DraftGoodsMapper; +import cn.lili.modules.goods.service.CategoryService; +import cn.lili.modules.goods.service.DraftGoodsService; +import cn.lili.modules.goods.service.GoodsGalleryService; +import cn.lili.modules.goods.service.GoodsSkuService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 草稿商品业务层实现 + * + * @author paulG + * @since 2020/12/19 + **/ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DraftGoodsServiceImpl extends ServiceImpl implements DraftGoodsService { + //分类 + private final CategoryService categoryService; + //商品相册 + private final GoodsGalleryService goodsGalleryService; + //规格商品 + private final GoodsSkuService goodsSkuService; + + @Override + public boolean addGoodsDraft(DraftGoodsDTO draftGoods) { + draftGoods.setGoodsGalleryListJson(JSONUtil.toJsonStr(draftGoods.getGoodsGalleryList())); + draftGoods.setSkuListJson(JSONUtil.toJsonStr(draftGoods.getSkuList())); + draftGoods.setGoodsParamsListJson(JSONUtil.toJsonStr(draftGoods.getGoodsParamsList())); + return this.save(draftGoods); + } + + @Override + public boolean updateGoodsDraft(DraftGoodsDTO draftGoods) { + draftGoods.setGoodsGalleryListJson(JSONUtil.toJsonStr(draftGoods.getGoodsGalleryList())); + draftGoods.setSkuListJson(JSONUtil.toJsonStr(draftGoods.getSkuList())); + draftGoods.setGoodsParamsListJson(JSONUtil.toJsonStr(draftGoods.getGoodsParamsList())); + return this.updateById(draftGoods); + } + + @Override + public void saveGoodsDraft(DraftGoodsDTO draftGoods) { + + if (draftGoods.getGoodsGalleryList() != null && !draftGoods.getGoodsGalleryList().isEmpty()) { + GoodsGallery goodsGallery = goodsGalleryService.getGoodsGallery(draftGoods.getGoodsGalleryList().get(0)); + draftGoods.setOriginal(goodsGallery.getOriginal()); + draftGoods.setSmall(goodsGallery.getSmall()); + draftGoods.setThumbnail(goodsGallery.getThumbnail()); + } + draftGoods.setGoodsGalleryListJson(JSONUtil.toJsonStr(draftGoods.getGoodsGalleryList())); + draftGoods.setSkuListJson(JSONUtil.toJsonStr(this.getGoodsSkuList(draftGoods.getSkuList()))); + draftGoods.setGoodsParamsListJson(JSONUtil.toJsonStr(draftGoods.getGoodsParamsList())); + this.saveOrUpdate(draftGoods); + } + + @Override + public void deleteGoodsDraft(String id) { + this.removeById(id); + } + + @Override + public DraftGoodsVO getDraftGoods(String id) { + + DraftGoods draftGoods = this.getById(id); + DraftGoodsVO draftGoodsVO = new DraftGoodsVO(); + BeanUtil.copyProperties(draftGoods, draftGoodsVO); + //商品分类名称赋值 + List categoryName = new ArrayList<>(); + String[] strArray = draftGoods.getCategoryPath().split(","); + List categories = categoryService.listByIds(Arrays.asList(strArray)); + for (Category category : categories) { + categoryName.add(category.getName()); + } + draftGoodsVO.setCategoryName(categoryName); + draftGoodsVO.setGoodsParamsList(JSONUtil.toList(JSONUtil.parseArray(draftGoods.getGoodsParamsListJson()), GoodsParams.class)); + draftGoodsVO.setGoodsGalleryList(JSONUtil.toList(JSONUtil.parseArray(draftGoods.getGoodsGalleryListJson()), String.class)); + JSONArray jsonArray = JSONUtil.parseArray(draftGoods.getSkuListJson()); + List list = JSONUtil.toList(jsonArray, GoodsSku.class); + draftGoodsVO.setSkuList(goodsSkuService.getGoodsSkuVOList(list)); + return draftGoodsVO; + } + + @Override + public IPage getDraftGoods(DraftGoodsSearchParams searchParams) { + return this.page(PageUtil.initPage(searchParams), searchParams.queryWrapper()); + } + + /** + * 获取sku集合 + * + * @param skuList sku列表 + * @return sku集合 + */ + private List getGoodsSkuList(List> skuList) { + List skus = new ArrayList<>(); + for (Map skuVO : skuList) { + GoodsSku add = this.add(skuVO); + skus.add(add); + } + return skus; + } + + private GoodsSku add(Map map) { + Map specMap = new HashMap<>(); + GoodsSku sku = new GoodsSku(); + for (Map.Entry m : map.entrySet()) { + switch (m.getKey()) { + case "sn": + sku.setSn(m.getValue() != null ? m.getValue().toString() : ""); + break; + case "cost": + sku.setCost(StringUtils.toDouble(m.getValue(), false)); + break; + case "price": + sku.setPrice(StringUtils.toDouble(m.getValue(), false)); + break; + case "quantity": + sku.setQuantity(StringUtils.toInt(m.getValue(), false)); + break; + case "weight": + sku.setWeight(StringUtils.toDouble(m.getValue(), false)); + break; + default: + specMap.put(m.getKey(), m.getValue()); + break; + } + } + sku.setSpecs(JSONUtil.toJsonStr(specMap)); + return sku; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsGalleryServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsGalleryServiceImpl.java new file mode 100644 index 00000000..40d9b220 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsGalleryServiceImpl.java @@ -0,0 +1,81 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.hutool.json.JSONUtil; +import cn.lili.modules.file.plugin.FileManagerPlugin; +import cn.lili.modules.goods.entity.dos.GoodsGallery; +import cn.lili.modules.goods.mapper.GoodsGalleryMapper; +import cn.lili.modules.goods.service.GoodsGalleryService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.GoodsSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 商品相册接口实现 + * + * @author pikachu + * @version v1.0 + * @since v1.0 + * 2020-02-23 15:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsGalleryServiceImpl extends ServiceImpl implements GoodsGalleryService { + //文件 + private final FileManagerPlugin fileManagerPlugin; + //商品相册数据层 + private final GoodsGalleryMapper goodsGalleryMapper; + //设置 + private final SettingService settingService; + + + @Override + public void add(List goodsGalleryList, String goodsId) { + //删除原来商品相册信息 + this.goodsGalleryMapper.delete(new UpdateWrapper().eq("goods_id", goodsId)); + //确定好图片选择器后进行处理 + int i = 0; + for (String origin : goodsGalleryList) { + // 获取带所有缩略的相册 + GoodsGallery galley = this.getGoodsGallery(origin); + galley.setGoodsId(goodsId); + // 默认第一个为默认图片 + galley.setIsDefault(i == 0 ? 1 : 0); + i++; + this.goodsGalleryMapper.insert(galley); + } + } + + @Override + public GoodsGallery getGoodsGallery(String origin) { + GoodsGallery goodsGallery = new GoodsGallery(); + //获取商品系统配置决定是否审核 + Setting setting = settingService.getById(SettingEnum.GOODS_SETTING.name()); + GoodsSetting goodsSetting = JSONUtil.toBean(setting.getSettingValue(), GoodsSetting.class); + //缩略图 + String thumbnail = fileManagerPlugin.getUrl(origin, goodsSetting.getAbbreviationPictureWidth(), goodsSetting.getAbbreviationPictureHeight()); + //小图 + String small = fileManagerPlugin.getUrl(origin, goodsSetting.getSmallPictureWidth(), goodsSetting.getSmallPictureHeight()); + //赋值 + goodsGallery.setSmall(small); + goodsGallery.setThumbnail(thumbnail); + goodsGallery.setOriginal(origin); + return goodsGallery; + } + + @Override + public List goodsGalleryList(String goodsId) { + //根据商品id查询商品相册 + return goodsGalleryMapper.selectList(new QueryWrapper().eq("goods_id", goodsId)); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsParamsServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsParamsServiceImpl.java new file mode 100644 index 00000000..144f1638 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsParamsServiceImpl.java @@ -0,0 +1,115 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.lili.modules.goods.entity.dos.CategoryParameterGroup; +import cn.lili.modules.goods.entity.dos.GoodsParams; +import cn.lili.modules.goods.entity.vos.GoodsParamsGroupVO; +import cn.lili.modules.goods.entity.vos.GoodsParamsVO; +import cn.lili.modules.goods.mapper.GoodsParamsMapper; +import cn.lili.modules.goods.service.CategoryParameterGroupService; +import cn.lili.modules.goods.service.GoodsParamsService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 商品关联参数接口实现 + * + * @author pikachu + * @version v1.0 + * @since v1.0 + * 2020-02-23 15:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsParamsServiceImpl extends ServiceImpl implements GoodsParamsService { + //分类-参数绑定 + private final CategoryParameterGroupService categoryParameterGroupService; + + @Override + public void addParams(List paramList, String goodsId) { + //先删除现有商品参数 + this.remove(new UpdateWrapper().eq("goods_id", goodsId)); + //循环添加参数 + if (paramList != null) { + for (GoodsParams param : paramList) { + GoodsParams goodsParams = new GoodsParams(); + goodsParams.setGoodsId(goodsId); + goodsParams.setParamName(param.getParamName()); + goodsParams.setParamValue(param.getParamValue()); + goodsParams.setParamId(param.getId()); + this.save(goodsParams); + } + } + } + + @Override + public List getGoodsParamsByGoodsId(String goodsId) { + return this.list(new LambdaQueryWrapper().eq(GoodsParams::getGoodsId, goodsId)); + } + + /** + * 添加商品参数 + * + * @param goodsParamsVO 商品参数 + * @return 添加是否成功 + */ + @Override + public boolean addParams(GoodsParamsVO goodsParamsVO) { + return this.save(goodsParamsVO); + } + + @Override + public List paramList(String goodsId, String categoryId) { + return this.baseMapper.paramList(goodsId, categoryId); + } + + @Override + public List queryGoodsParams(String categoryId, String goodsId) { + //查询分类关联参数组 + List groupList = categoryParameterGroupService.getCategoryGroup(categoryId); + //查询商品参数 + List paramList = this.paramList(goodsId, categoryId); + //拼装数据返回 + return this.convertParamList(groupList, paramList); + } + + + /** + * 拼装返回值 + * + * @param paramList + * @return + */ + private List convertParamList(List groupList, List paramList) { + Map> map = new HashMap<>(16); + for (GoodsParamsVO param : paramList) { + if (map.get(param.getGroupId()) != null) { + map.get(param.getGroupId()).add(param); + } else { + List list = new ArrayList<>(); + list.add(param); + map.put(param.getGroupId(), list); + } + } + List resList = new ArrayList<>(); + for (CategoryParameterGroup group : groupList) { + GoodsParamsGroupVO list = new GoodsParamsGroupVO(); + list.setGroupName(group.getGroupName()); + list.setGroupId(group.getId()); + list.setParams(map.get(group.getId())); + resList.add(list); + } + return resList; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsServiceImpl.java new file mode 100644 index 00000000..e4b355af --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsServiceImpl.java @@ -0,0 +1,363 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.GoodsTagsEnum; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.goods.entity.dos.Category; +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.dos.GoodsGallery; +import cn.lili.modules.goods.entity.dto.GoodsOperationDTO; +import cn.lili.modules.goods.entity.dto.GoodsSearchParams; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.entity.vos.GoodsSkuVO; +import cn.lili.modules.goods.entity.vos.GoodsVO; +import cn.lili.modules.goods.mapper.GoodsMapper; +import cn.lili.modules.goods.service.*; +import cn.lili.modules.store.entity.vos.StoreVO; +import cn.lili.modules.store.service.StoreService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.GoodsSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 商品业务层实现 + * + * @author pikachu + * @date 2020-02-23 15:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsServiceImpl extends ServiceImpl implements GoodsService { + + //商品 + private final GoodsMapper goodsMapper; + //商品属性 + private final GoodsParamsService goodsParamsService; + //分类 + private final CategoryService categoryService; + //设置 + private final SettingService settingService; + //商品相册 + private final GoodsGalleryService goodsGalleryService; + //商品规格 + private GoodsSkuService goodsSkuService; + //店铺详情 + private StoreService storeService; + //rocketMq + private final RocketMQTemplate rocketMQTemplate; + //rocketMq配置 + private final RocketmqCustomProperties rocketmqCustomProperties; + + @Override + public void underStoreGoods(String storeId) { + this.goodsMapper.underStoreGoods(storeId); + } + + @Override + public final Integer getGoodsCountByCategory(String categoryId) { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.like("category_path", categoryId); + return this.count(queryWrapper); + } + + @Override + public void addGoods(GoodsOperationDTO goodsOperationDTO) { + + Goods goods = new Goods(goodsOperationDTO); + + //判定商品是否需要审核 + this.checkNeedAuth(goods); + + // 向goods加入图片 + this.setGoodsGalleryParam(goodsOperationDTO.getGoodsGalleryList().get(0), goods); + + //商品添加卖家信息 + StoreVO storeDetail = this.storeService.getStoreDetail(); + goods.setStoreId(storeDetail.getId()); + goods.setStoreName(storeDetail.getStoreName()); + if (storeDetail.getSelfOperated() != null) { + goods.setSelfOperated(storeDetail.getSelfOperated()); + } + // 评论次数 + goods.setCommentNum(0); + // 购买次数 + goods.setBuyCount(0); + // 购买次数 + goods.setQuantity(0); + // 商品评分 + goods.setGrade(100.0); + + this.save(goods); + + // 添加商品参数 + if (goodsOperationDTO.getGoodsParamsList() != null && !goodsOperationDTO.getGoodsParamsList().isEmpty()) { + this.goodsParamsService.addParams(goodsOperationDTO.getGoodsParamsList(), goods.getId()); + } + + // 添加商品sku信息 + this.goodsSkuService.add(goodsOperationDTO.getSkuList(), goods); + + // 添加相册 + if (goodsOperationDTO.getGoodsGalleryList() != null && !goodsOperationDTO.getGoodsGalleryList().isEmpty()) { + this.goodsGalleryService.add(goodsOperationDTO.getGoodsGalleryList(), goods.getId()); + } + } + + + @Override + public void editGoods(GoodsOperationDTO goodsOperationDTO, String goodsId) { + this.checkExist(goodsId); + Goods goods = new Goods(goodsOperationDTO); + goods.setId(goodsId); + + //是否需要审核 + this.checkNeedAuth(goods); + + // 向goods加入图片 + this.setGoodsGalleryParam(goodsOperationDTO.getGoodsGalleryList().get(0), goods); + + //商品添加卖家信息 + StoreVO storeDetail = this.storeService.getStoreDetail(); + if (storeDetail.getSelfOperated() != null) { + goods.setSelfOperated(storeDetail.getSelfOperated()); + } + goods.setStoreId(storeDetail.getId()); + goods.setStoreName(storeDetail.getStoreName()); + + //修改商品 + this.updateById(goods); + + // 添加商品参数 + this.goodsParamsService.addParams(goodsOperationDTO.getGoodsParamsList(), goods.getId()); + + //修改商品规格 + this.goodsSkuService.update(goodsOperationDTO.getSkuList(), goods, goodsOperationDTO.getRegeneratorSkuFlag()); + + // 添加相册 + this.goodsGalleryService.add(goodsOperationDTO.getGoodsGalleryList(), goods.getId()); + } + + @Override + public GoodsVO getGoodsVO(String goodsId) { + //查询商品信息 + Goods goods = this.getById(goodsId); + if (goods == null) { + log.error("商品ID为" + goodsId + "的商品不存在"); + throw new ServiceException(ResultCode.GOODS_NOT_EXIST); + } + GoodsVO goodsVO = new GoodsVO(); + //赋值 + BeanUtils.copyProperties(goods, goodsVO); + //商品id + goodsVO.setId(goods.getId()); + //商品相册赋值 + List images = new ArrayList<>(); + List galleryList = goodsGalleryService.goodsGalleryList(goodsId); + for (GoodsGallery goodsGallery : galleryList) { + images.add(goodsGallery.getOriginal()); + } + goodsVO.setGoodsGalleryList(images); + // 商品sku赋值 + List goodsListByGoodsId = goodsSkuService.getGoodsListByGoodsId(goodsId); + if (goodsListByGoodsId != null && !goodsListByGoodsId.isEmpty()) { + goodsVO.setSkuList(goodsListByGoodsId); + } + //商品分类名称赋值 + List categoryName = new ArrayList<>(); + String categoryPath = goods.getCategoryPath(); + String[] strArray = categoryPath.split(","); + List categories = categoryService.listByIds(Arrays.asList(strArray)); + for (Category category : categories) { + categoryName.add(category.getName()); + } + goodsVO.setCategoryName(categoryName); + + goodsVO.setGoodsParamsList(goodsParamsService.getGoodsParamsByGoodsId(goodsId)); + + return goodsVO; + } + + @Override + public IPage queryByParams(GoodsSearchParams goodsSearchParams) { + return this.page(PageUtil.initPage(goodsSearchParams), goodsSearchParams.queryWrapper()); + } + + @Override + public boolean auditGoods(List goodsIds, GoodsAuthEnum goodsAuthEnum) { + boolean result = false; + for (String goodsId : goodsIds) { + Goods goods = this.checkExist(goodsId); + goods.setIsAuth(goodsAuthEnum.name()); + result = this.updateById(goods); + goodsSkuService.updateGoodsSkuStatus(goods); + //商品审核消息 + String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.GOODS_AUDIT.name(); + //发送mq消息 + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(goods.getStoreId()), RocketmqSendCallbackBuilder.commonCallback()); + } + return result; + } + + @Override + public Integer goodsNum(GoodsStatusEnum goodsStatusEnum, GoodsAuthEnum goodsAuthEnum) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + if (goodsStatusEnum != null) { + queryWrapper.eq(Goods::getMarketEnable, goodsStatusEnum.name()); + } + if (goodsAuthEnum != null) { + queryWrapper.eq(Goods::getIsAuth, goodsAuthEnum.name()); + } + queryWrapper.eq(StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), + Goods::getStoreId, UserContext.getCurrentUser().getStoreId()); + + return this.count(queryWrapper); + } + + @Override + public Integer todayUpperNum() { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(Goods::getMarketEnable, GoodsStatusEnum.UPPER.name()); + queryWrapper.gt(Goods::getCreateTime, DateUtil.beginOfDay(new DateTime())); + return this.count(queryWrapper); + } + + @Override + public Boolean updateGoodsMarketAble(List goodsIds, GoodsStatusEnum goodsStatusEnum, String underReason) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); + updateWrapper.set(Goods::getMarketEnable, goodsStatusEnum.name()); + updateWrapper.set(Goods::getUnderMessage, underReason); + updateWrapper.in(Goods::getId, goodsIds); + //商品审核通过的才可以上架 + updateWrapper.eq(Goods::getMarketEnable, GoodsStatusEnum.UPPER.name()); + this.update(updateWrapper); + + //修改规格商品 + List goodsList = this.list(new LambdaQueryWrapper().in(Goods::getId, goodsIds)); + for (Goods goods : goodsList) { + goodsSkuService.updateGoodsSkuStatus(goods); + } + return true; + + } + + @Override + public Boolean deleteGoods(List goodsIds) { + + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); + updateWrapper.set(Goods::getMarketEnable, GoodsStatusEnum.DOWN.name()); + updateWrapper.set(Goods::getDeleteFlag, true); + updateWrapper.in(Goods::getId, goodsIds); + this.update(updateWrapper); + + //修改规格商品 + List goodsList = this.list(new LambdaQueryWrapper().in(Goods::getId, goodsIds)); + for (Goods goods : goodsList) { + //修改SKU状态 + goodsSkuService.updateGoodsSkuStatus(goods); + //商品删除消息 + String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.GOODS_DELETE.name(); + //发送mq消息 + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(goods.getStoreId()), RocketmqSendCallbackBuilder.commonCallback()); + } + + return true; + } + + @Override + public Boolean freight(List goodsIds, String freightPayer, String templateId) { + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.set(Goods::getFreightPayer, freightPayer); + lambdaUpdateWrapper.set(Goods::getTemplateId, templateId); + lambdaUpdateWrapper.in(Goods::getId, goodsIds); + return this.update(lambdaUpdateWrapper); + } + + @Override + public void updateStock(String goodsId, Integer quantity) { + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.set(Goods::getQuantity, quantity); + lambdaUpdateWrapper.eq(Goods::getId, goodsId); + this.update(lambdaUpdateWrapper); + } + + /** + * 添加商品默认图片 + * + * @param origin 图片 + * @param goods 商品 + */ + private void setGoodsGalleryParam(String origin, Goods goods) { + GoodsGallery goodsGallery = goodsGalleryService.getGoodsGallery(origin); + goods.setOriginal(goodsGallery.getOriginal()); + goods.setSmall(goodsGallery.getSmall()); + goods.setThumbnail(goodsGallery.getThumbnail()); + } + + /** + * 商品是否需要审核 + * + * @param goods 商品 + */ + private void checkNeedAuth(Goods goods) { + //获取商品系统配置决定是否审核 + Setting setting = settingService.get(SettingEnum.GOODS_SETTING.name()); + GoodsSetting goodsSetting = JSONUtil.toBean(setting.getSettingValue(), GoodsSetting.class); + //是否需要审核 + goods.setIsAuth(Boolean.TRUE.equals(goodsSetting.getGoodsCheck()) ? GoodsAuthEnum.TOBEAUDITED.name() : GoodsAuthEnum.PASS.name()); + //自动下架 + goods.setMarketEnable(Boolean.TRUE.equals(goodsSetting.getGoodsCheck()) ? GoodsStatusEnum.DOWN.name() : GoodsStatusEnum.UPPER.name()); + } + + /** + * 判断商品是否存在 + * + * @param goodsId + * @return + */ + private Goods checkExist(String goodsId) { + Goods goods = getById(goodsId); + if (goods == null) { + log.error("商品ID为" + goodsId + "的商品不存在"); + throw new ServiceException(ResultCode.GOODS_NOT_EXIST); + } + return goods; + } + + @Autowired + public void setGoodsSkuService(GoodsSkuService goodsSkuService) { + this.goodsSkuService = goodsSkuService; + } + + @Autowired + public void setStoreService(StoreService storeService) { + this.storeService = storeService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsSkuServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsSkuServiceImpl.java new file mode 100644 index 00000000..762dbbc4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsSkuServiceImpl.java @@ -0,0 +1,623 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.GoodsTagsEnum; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.dos.SpecValues; +import cn.lili.modules.goods.entity.dos.Specification; +import cn.lili.modules.goods.entity.dto.GoodsSearchParams; +import cn.lili.modules.goods.entity.dto.GoodsSkuStockDTO; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.entity.vos.*; +import cn.lili.modules.goods.mapper.GoodsSkuMapper; +import cn.lili.modules.goods.service.*; +import cn.lili.modules.member.entity.dos.FootPrint; +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import cn.lili.modules.member.entity.enums.EvaluationGradeEnum; +import cn.lili.modules.member.service.MemberEvaluationService; +import cn.lili.modules.search.entity.dos.EsGoodsAttribute; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.service.EsGoodsIndexService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 商品sku业务层实现 + * + * @author pikachu + * @date 2020-02-23 15:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsSkuServiceImpl extends ServiceImpl implements GoodsSkuService { + + //缓存 + private final Cache cache; + //分类 + private final CategoryService categoryService; + //商品相册 + private final GoodsGalleryService goodsGalleryService; + //规格 + private final SpecificationService specificationService; + //规格项 + private final SpecValuesService specValuesService; + //缓存 + private final StringRedisTemplate stringRedisTemplate; + //rocketMq + private final RocketMQTemplate rocketMQTemplate; + //rocketMq配置 + private final RocketmqCustomProperties rocketmqCustomProperties; + //会员评价 + @Autowired + private MemberEvaluationService memberEvaluationService; + //商品 + private GoodsService goodsService; + //商品索引 + private EsGoodsIndexService goodsIndexService; + + @Override + public void add(List> skuList, Goods goods) { + List newSkuList; + // 如果有规格 + if (skuList != null && !skuList.isEmpty()) { + // 添加商品sku + newSkuList = this.addGoodsSku(skuList, goods); + } else { + throw new ServiceException("规格必须要有一个!"); + } + + this.updateStock(newSkuList); + generateEsCheck(goods); + } + + @Override + public void update(List> skuList, Goods goods, Boolean regeneratorSkuFlag) { + // 是否存在规格 + if (skuList == null || skuList.isEmpty()) { + throw new ServiceException("规格必须要有一个!"); + } + List newSkuList; + //删除旧的sku信息 + if (Boolean.TRUE.equals(regeneratorSkuFlag)) { + List goodsListByGoodsId = getGoodsListByGoodsId(goods.getId()); + List oldSkuIds = new ArrayList<>(); + for (GoodsSkuVO goodsSkuVO : goodsListByGoodsId) { + oldSkuIds.add(goodsSkuVO.getId()); + goodsIndexService.deleteIndexById(goodsSkuVO.getId()); + cache.remove(GoodsSkuService.getCacheKeys(goodsSkuVO.getId())); + } + this.removeByIds(oldSkuIds); + //删除sku相册 + goodsGalleryService.removeByIds(oldSkuIds); + // 添加商品sku + newSkuList = this.addGoodsSku(skuList, goods); + + //发送mq消息 + String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.SKU_DELETE.name(); + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(oldSkuIds), RocketmqSendCallbackBuilder.commonCallback()); + } else { + newSkuList = new ArrayList<>(); + for (Map map : skuList) { + GoodsSku sku = new GoodsSku(); + //设置商品信息 + goodsInfo(sku, goods); + //设置商品规格信息 + skuInfo(sku, goods, map, null); + newSkuList.add(sku); + } + this.updateBatchById(newSkuList); + } + + + this.updateStock(newSkuList); + generateEsCheck(goods); + } + + /** + * 更新商品sku + * + * @param goodsSku sku信息 + */ + @Override + public void update(GoodsSku goodsSku) { + this.updateById(goodsSku); + cache.remove(GoodsSkuService.getCacheKeys(goodsSku.getId())); + cache.put(GoodsSkuService.getCacheKeys(goodsSku.getId()), goodsSku); + } + + @Override + public GoodsSku getGoodsSkuByIdFromCache(String id) { + GoodsSku goodsSku = cache.get(GoodsSkuService.getCacheKeys(id)); + if (goodsSku == null) { + goodsSku = this.getById(id); + if (goodsSku == null) { + return null; + } + cache.put(GoodsSkuService.getCacheKeys(id), goodsSku); + } + String quantity = stringRedisTemplate.opsForValue().get(GoodsSkuService.getStockCacheKey(id)); + if (quantity != null) { + if (goodsSku.getQuantity() != Integer.parseInt(quantity)) { + goodsSku.setQuantity(Integer.parseInt(quantity)); + this.updateById(goodsSku); + } + } else { + stringRedisTemplate.opsForValue().set(GoodsSkuService.getStockCacheKey(id), goodsSku.getQuantity().toString()); + } + + return goodsSku; + } + + @Override + public Map getGoodsSkuDetail(String goodsId, String skuId) { + Map map = new HashMap<>(); + GoodsSku goodsSku = this.getGoodsSkuByIdFromCache(skuId); + + //如果规格为空则使用商品ID进行查询 + if (goodsSku == null) { + GoodsVO goodsVO = goodsService.getGoodsVO(goodsId); + skuId = goodsVO.getSkuList().get(0).getId(); + goodsSku = this.getGoodsSkuByIdFromCache(skuId); + //如果使用商品ID无法查询SKU则返回错误 + if (goodsSku == null) { + throw new ServiceException("商品已下架"); + } + + } + + // 获取当前商品的索引信息 + EsGoodsIndex goodsIndex = goodsIndexService.findById(skuId); + if (goodsIndex == null) { + goodsIndex = goodsIndexService.resetEsGoodsIndex(goodsSku); + } + //商品规格 + GoodsSkuVO goodsSkuDetail = this.getGoodsSkuVO(goodsSku); + + // 设置当前商品的促销价格 + if (goodsIndex.getPromotionMap() != null && !goodsIndex.getPromotionMap().isEmpty() && goodsIndex.getPromotionPrice() != null) { + goodsSkuDetail.setPromotionPrice(goodsIndex.getPromotionPrice()); + } + map.put("data", goodsSkuDetail); + + //获取分类 + String[] split = goodsSkuDetail.getCategoryPath().split(","); + map.put("categoryName", categoryService.getCategoryNameByIds(Arrays.asList(split))); + + //获取规格信息 + map.put("specs", this.groupBySkuAndSpec(goodsSkuDetail.getGoodsId())); + map.put("promotionMap", goodsIndex.getPromotionMap()); + + //记录用户足迹 + if (UserContext.getCurrentUser() != null) { + FootPrint footPrint = new FootPrint(UserContext.getCurrentUser().getId(), goodsId, skuId); + String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.VIEW_GOODS.name(); + rocketMQTemplate.asyncSend(destination, footPrint, RocketmqSendCallbackBuilder.commonCallback()); + } + return map; + } + + @Override + public List groupBySkuAndSpec(String goodsId) { + List goodsListByGoodsId = this.getGoodsListByGoodsId(goodsId); + List skuSpecVOList = new ArrayList<>(); + for (GoodsSkuVO goodsSkuVO : goodsListByGoodsId) { + GoodsSkuSpecVO specVO = new GoodsSkuSpecVO(); + specVO.setSkuId(goodsSkuVO.getId()); + specVO.setSpecValues(goodsSkuVO.getSpecList()); + specVO.setQuantity(goodsSkuVO.getQuantity()); + skuSpecVOList.add(specVO); + } + return skuSpecVOList; + } + + /** + * 更新商品sku状态 + * + * @param goods 商品信息(Id,MarketEnable/IsAuth) + */ + @Override + public void updateGoodsSkuStatus(Goods goods) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(GoodsSku::getGoodsId, goods.getId()); + updateWrapper.set(GoodsSku::getMarketEnable, goods.getMarketEnable()); + updateWrapper.set(GoodsSku::getIsAuth, goods.getIsAuth()); + this.update(updateWrapper); + generateEsCheck(goods); + } + + @Override + public List getGoodsSkuByIdFromCache(List ids) { + List keys = new ArrayList<>(); + for (String id : ids) { + keys.add(GoodsSkuService.getCacheKeys(id)); + } + List list = cache.multiGet(keys); + if (list == null || list.isEmpty()) { + list = new ArrayList<>(); + List goodsSkus = listByIds(ids); + for (GoodsSku skus : goodsSkus) { + cache.put(GoodsSkuService.getCacheKeys(skus.getId()), skus); + list.add(skus); + } + } + return list; + } + + @Override + public List getGoodsListByGoodsId(String goodsId) { + List list = this.list(new LambdaQueryWrapper().eq(GoodsSku::getGoodsId, goodsId)); + return this.getGoodsSkuVOList(list); + } + + @Override + public List getGoodsSkuVOList(List list) { + List goodsSkuVOS = new ArrayList<>(); + for (GoodsSku goodsSku : list) { + GoodsSkuVO goodsSkuVO = this.getGoodsSkuVO(goodsSku); + goodsSkuVOS.add(goodsSkuVO); + } + return goodsSkuVOS; + } + + @Override + public GoodsSkuVO getGoodsSkuVO(GoodsSku goodsSku) { + GoodsSkuVO goodsSkuVO = new GoodsSkuVO(goodsSku); + JSONObject jsonObject = JSONUtil.parseObj(goodsSku.getSpecs()); + List specValueVOS = new ArrayList<>(); + List goodsGalleryList = new ArrayList<>(); + for (Map.Entry entry : jsonObject.entrySet()) { + SpecValueVO s = new SpecValueVO(); + if (entry.getKey().equals("images")) { + s.setSpecName(entry.getKey()); + if (entry.getValue().toString().contains("url")) { + List specImages = JSONUtil.toList(JSONUtil.parseArray(entry.getValue()), SpecValueVO.SpecImages.class); + s.setSpecImage(specImages); + goodsGalleryList = specImages.stream().map(SpecValueVO.SpecImages::getUrl).collect(Collectors.toList()); + } + } else { + SpecificationVO specificationVO = new SpecificationVO(); + specificationVO.setSpecName(entry.getKey()); + specificationVO.setStoreId(goodsSku.getStoreId()); + specificationVO.setCategoryPath(goodsSku.getCategoryPath()); + Specification specification = specificationService.addSpecification(specificationVO); + s.setSpecNameId(specification.getId()); + SpecValues specValues = specValuesService.getSpecValues(entry.getValue().toString(), specification.getId()); + s.setSpecValueId(specValues.getId()); + s.setSpecName(entry.getKey()); + s.setSpecValue(entry.getValue().toString()); + } + specValueVOS.add(s); + } + goodsSkuVO.setGoodsGalleryList(goodsGalleryList); + goodsSkuVO.setSpecList(specValueVOS); + return goodsSkuVO; + } + + @Override + public IPage getGoodsSkuByPage(GoodsSearchParams searchParams) { + return this.page(PageUtil.initPage(searchParams), searchParams.queryWrapper()); + } + + @Override + public void updateStocks(List goodsSkuStockDTOS) { + for (GoodsSkuStockDTO goodsSkuStockDTO : goodsSkuStockDTOS) { + this.updateStock(goodsSkuStockDTO.getSkuId(), goodsSkuStockDTO.getQuantity()); + } + } + + @Override + public void updateStock(String skuId, Integer quantity) { + GoodsSku goodsSku = getGoodsSkuByIdFromCache(skuId); + if (goodsSku != null) { + if (quantity <= 0) { + goodsIndexService.deleteIndexById(goodsSku.getId()); + } + goodsSku.setQuantity(quantity); + this.update(new LambdaUpdateWrapper().eq(GoodsSku::getId, skuId).set(GoodsSku::getQuantity, quantity)); + cache.put(GoodsSkuService.getCacheKeys(skuId), goodsSku); + stringRedisTemplate.opsForValue().set(GoodsSkuService.getStockCacheKey(skuId), quantity.toString()); + + //更新商品库存 + List goodsSkus = new ArrayList<>(); + goodsSkus.add(goodsSku); + this.updateGoodsStuck(goodsSkus); + } + } + + @Override + public Integer getStock(String skuId) { + String cacheKeys = GoodsSkuService.getStockCacheKey(skuId); + String stockStr = stringRedisTemplate.opsForValue().get(cacheKeys); + if (stockStr != null) { + return Integer.parseInt(stockStr); + } else { + GoodsSku goodsSku = getGoodsSkuByIdFromCache(skuId); + stringRedisTemplate.opsForValue().set(cacheKeys, goodsSku.getQuantity().toString()); + return goodsSku.getQuantity(); + } + } + + @Override + public void updateGoodsStuck(List goodsSkus) { + //商品id集合 hashset 去重复 + Set goodsIds = new HashSet<>(); + for (GoodsSku sku : goodsSkus) { + goodsIds.add(sku.getGoodsId()); + } + //获取相关的sku集合 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.in(GoodsSku::getGoodsId, goodsIds); + List goodsSkuList = this.list(lambdaQueryWrapper); + + //统计每个商品的库存 + for (String goodsId : goodsIds) { + //库存 + Integer quantity = 0; + for (GoodsSku goodsSku : goodsSkuList) { + if (goodsId.equals(goodsSku.getGoodsId())) { + quantity += goodsSku.getQuantity(); + } + } + //保存商品库存结果 这里在for循环中调用数据库保存不太好,需要优化 + goodsService.updateStock(goodsId, quantity); + } + + + } + + @Override + public void updateGoodsSkuCommentNum(String skuId) { + //获取商品信息 + GoodsSku goodsSku = this.getGoodsSkuByIdFromCache(skuId); + + LambdaQueryWrapper goodEvaluationQueryWrapper = new LambdaQueryWrapper<>(); + goodEvaluationQueryWrapper.eq(MemberEvaluation::getSkuId, goodsSku.getId()); + goodEvaluationQueryWrapper.eq(MemberEvaluation::getGrade, EvaluationGradeEnum.GOOD.name()); + + // 好评数量 + double highPraiseNum = memberEvaluationService.count(goodEvaluationQueryWrapper); + + // 更新商品评价数量 + goodsSku.setCommentNum(goodsSku.getCommentNum() + 1); + + // 好评率 + double grade = NumberUtil.div(highPraiseNum, goodsSku.getCommentNum().doubleValue(), 2) * 100; + goodsSku.setGrade(grade); + //修改规格 + this.update(goodsSku); + //修改规格索引 + goodsIndexService.updateIndexCommentNum(goodsSku.getId(), goodsSku.getCommentNum(), (int) highPraiseNum, grade); + } + + /** + * 生成ES商品索引 + * + * @param goods 商品信息 + */ + private void generateEsCheck(Goods goods) { + //如果商品通过审核&&并且已上架 + if (goods.getIsAuth().equals(GoodsAuthEnum.PASS.name()) && goods.getMarketEnable().equals(GoodsStatusEnum.UPPER.name())) { + List goodsSkuList = this.list(new LambdaQueryWrapper().eq(GoodsSku::getGoodsId, goods.getId())); + for (GoodsSku goodsSku : goodsSkuList) { + EsGoodsIndex byId = goodsIndexService.findById(goodsSku.getId()); + EsGoodsIndex goodsIndex = new EsGoodsIndex(goodsSku); + if (goodsSku.getQuantity() > 0 && byId == null) { + goodsIndexService.addIndex(goodsIndex); + } else if (goodsSku.getQuantity() > 0 && byId != null) { + goodsIndexService.updateIndex(goodsIndex); + } else if (goodsSku.getQuantity() <= 0 && byId != null) { + goodsIndexService.deleteIndexById(goodsSku.getId()); + } + cache.remove(GoodsSkuService.getCacheKeys(goodsSku.getId())); + } + } + } + + /** + * 修改库存 + * + * @param goodsSkus 商品SKU + */ + private void updateStock(List goodsSkus) { + //总库存数量 + Integer quantity = 0; + for (GoodsSku sku : goodsSkus) { + this.updateStock(sku.getId(), sku.getQuantity()); + quantity += sku.getQuantity(); + } + //修改商品库存 + goodsService.updateStock(goodsSkus.get(0).getGoodsId(), quantity); + } + + + /** + * 增加sku集合 + * + * @param skuList sku列表 + * @param goods 商品信息 + */ + private List addGoodsSku(List> skuList, Goods goods) { + List skus = new ArrayList<>(); + List goodsIndices = new ArrayList<>(); + for (Map skuVO : skuList) { + Map resultMap = this.add(skuVO, goods); + GoodsSku goodsSku = (GoodsSku) resultMap.get("goodsSku"); + if (goods.getSelfOperated() != null) { + goodsSku.setSelfOperated(goods.getSelfOperated()); + } + EsGoodsIndex goodsIndex = (EsGoodsIndex) resultMap.get("goodsIndex"); + skus.add(goodsSku); + goodsIndices.add(goodsIndex); + stringRedisTemplate.opsForValue().set(GoodsSkuService.getStockCacheKey(goodsSku.getId()), goodsSku.getQuantity().toString()); + } + this.saveBatch(skus); + String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.GENERATOR_GOODS_INDEX.name(); + //发送mq消息 + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(goodsIndices), RocketmqSendCallbackBuilder.commonCallback()); + return skus; + } + + /** + * 添加商品规格 + * + * @param map 规格属性 + * @param goods 商品 + * @return 规格商品 + */ + private Map add(Map map, Goods goods) { + Map resultMap = new HashMap<>(); + GoodsSku sku = new GoodsSku(); + + //商品索引 + EsGoodsIndex esGoodsIndex = new EsGoodsIndex(); + + //设置商品信息 + goodsInfo(sku, goods); + //设置商品规格信息 + skuInfo(sku, goods, map, esGoodsIndex); + + esGoodsIndex.setGoodsSku(sku); + resultMap.put("goodsSku", sku); + resultMap.put("goodsIndex", esGoodsIndex); + return resultMap; + } + + /** + * 设置规格商品的商品信息 + * + * @param sku 规格 + * @param goods 商品 + */ + private void goodsInfo(GoodsSku sku, Goods goods) { + //商品基本信息 + sku.setGoodsId(goods.getId()); + + sku.setSellingPoint(goods.getSellingPoint()); + sku.setCategoryPath(goods.getCategoryPath()); + sku.setBrandId(goods.getBrandId()); + sku.setMarketEnable(goods.getMarketEnable()); + sku.setIntro(goods.getIntro()); + sku.setMobileIntro(goods.getMobileIntro()); + sku.setGoodsUnit(goods.getGoodsUnit()); + //运费 + sku.setFreightPayer(goods.getFreightPayer()); + //商品状态 + sku.setIsAuth(goods.getIsAuth()); + sku.setSalesModel(goods.getSalesModel()); + //卖家信息 + sku.setStoreId(goods.getStoreId()); + sku.setStoreName(goods.getStoreName()); + sku.setStoreCategoryPath(goods.getStoreCategoryPath()); + sku.setFreightTemplateId(goods.getTemplateId()); + } + + /** + * 设置商品规格信息 + * + * @param sku 规格商品 + * @param goods 商品 + * @param map 规格信息 + * @param esGoodsIndex 商品索引 + */ + private void skuInfo(GoodsSku sku, Goods goods, Map map, EsGoodsIndex esGoodsIndex) { + + //规格简短信息 + StringBuilder simpleSpecs = new StringBuilder(); + //商品名称 + StringBuilder goodsName = new StringBuilder(goods.getGoodsName()); + //规格商品缩略图 + String thumbnail = ""; + //规格值 + Map specMap = new HashMap<>(); + //商品属性 + List attributes = new ArrayList<>(); + + //获取规格信息 + for (Map.Entry m : map.entrySet()) { + //保存规格信息 + if (m.getKey().equals("id") || m.getKey().equals("sn") || m.getKey().equals("cost") || m.getKey().equals("price") || m.getKey().equals("quantity") || m.getKey().equals("weight")) { + continue; + } else { + specMap.put(m.getKey(), m.getValue()); + if (m.getKey().equals("images")) { + //设置规格商品缩略图 + List> images = (List>) m.getValue(); + if (images == null || images.isEmpty()) { + throw new ServiceException("sku图片至少为一个"); + } + thumbnail = goodsGalleryService.getGoodsGallery(images.get(0).get("url")).getThumbnail(); + } else { + //设置商品名称 + goodsName.append(" ").append(m.getValue()); + + //规格简短信息 + simpleSpecs.append(" ").append(m.getValue()); + + //保存规格项 + SpecificationVO specificationVO = new SpecificationVO(m.getKey(), goods.getStoreId(), goods.getCategoryPath()); + Specification specification = specificationService.addSpecification(specificationVO); + + //保存规格值 + SpecValues specValues = specValuesService.getSpecValues(m.getValue().toString(), specification.getId()); + + //添加属性索引 + EsGoodsAttribute attribute = new EsGoodsAttribute(0, specification.getId(), m.getKey(), specValues.getId(), m.getValue().toString()); + attributes.add(attribute); + } + } + } + + //设置规格信息 + sku.setGoodsName(goodsName.toString()); + sku.setThumbnail(thumbnail); + + //规格信息 + sku.setId(map.getOrDefault("id", "").toString()); + sku.setSn(Convert.toStr(map.get("sn"))); + sku.setWeight(Convert.toDouble(map.get("weight"), 0D)); + sku.setPrice(Convert.toDouble(map.get("price"), 0D)); + sku.setCost(Convert.toDouble(map.get("cost"), 0D)); + sku.setQuantity(Convert.toInt(map.get("quantity"), 0)); + sku.setSpecs(JSONUtil.toJsonStr(specMap)); + sku.setSimpleSpecs(simpleSpecs.toString()); + + if (esGoodsIndex != null) { + //商品索引 + esGoodsIndex.setAttrList(attributes); + } + } + + + @Autowired + public void setGoodsService(GoodsService goodsService) { + this.goodsService = goodsService; + } + + @Autowired + public void setGoodsIndexService(EsGoodsIndexService goodsIndexService) { + this.goodsIndexService = goodsIndexService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsUnitServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsUnitServiceImpl.java new file mode 100644 index 00000000..499638fb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsUnitServiceImpl.java @@ -0,0 +1,24 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.lili.modules.goods.entity.dos.GoodsUnit; +import cn.lili.modules.goods.mapper.GoodsUnitMapper; +import cn.lili.modules.goods.service.GoodsUnitService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +/** + * 计量单位业务层实现 + * + * @author Bulbasaur + * @date 2020/11/26 16:13 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsUnitServiceImpl extends ServiceImpl implements GoodsUnitService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsWordsServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsWordsServiceImpl.java new file mode 100644 index 00000000..09dd8815 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/GoodsWordsServiceImpl.java @@ -0,0 +1,19 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.lili.modules.goods.entity.dos.GoodsWords; +import cn.lili.modules.goods.mapper.GoodsWordsMapper; +import cn.lili.modules.goods.service.GoodsWordsService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 商品关键字业务层实现 + * + * @author paulG + * @since 2020/10/15 + **/ +@Service +@Transactional(rollbackFor = Exception.class) +public class GoodsWordsServiceImpl extends ServiceImpl implements GoodsWordsService { +} diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/ParametersServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/ParametersServiceImpl.java new file mode 100644 index 00000000..27ee4e58 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/ParametersServiceImpl.java @@ -0,0 +1,22 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.lili.modules.goods.entity.dos.Parameters; +import cn.lili.modules.goods.mapper.ParametersMapper; +import cn.lili.modules.goods.service.ParametersService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 商品参数业务层实现 + * + * @author pikachu + * @date 2020-03-02 16:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ParametersServiceImpl extends ServiceImpl implements ParametersService { +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/SpecValuesServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/SpecValuesServiceImpl.java new file mode 100644 index 00000000..aceaf4c5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/SpecValuesServiceImpl.java @@ -0,0 +1,118 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.SpecValues; +import cn.lili.modules.goods.entity.dos.Specification; +import cn.lili.modules.goods.mapper.SpecValuesMapper; +import cn.lili.modules.goods.service.SpecValuesService; +import cn.lili.modules.goods.service.SpecificationService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 规格项接口实现 + * + * @author pikachu + * @date 2020-02-18 16:18:56 + */ + +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SpecValuesServiceImpl extends ServiceImpl implements SpecValuesService { + + //规格 + private SpecificationService specificationService; + + @Override + public List saveSpecValue(String specId, String[] valueList) { + //校验是否存在 + Specification specification = specificationService.getById(specId); + List res = new ArrayList<>(); + if (specification != null) { + //先删除原有规格值 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("spec_id", specId); + this.remove(queryWrapper); + //添加新的规格值 + for (String value : valueList) { + SpecValues specValues = new SpecValues(); + specValues.setSpecValue(value); + specValues.setSpecId(specification.getId()); + this.save(specValues); + res.add(specValues); + } + return res; + } + return res; + } + + @Override + public List addSpecValue(String specId, String[] valueList) { + List specValuesList = new ArrayList<>(); + for (String value : valueList) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("spec_id", specId); + queryWrapper.eq("spec_value", value); + if (this.getOne(queryWrapper) == null) { + SpecValues specValues = new SpecValues(); + specValues.setSpecValue(value); + specValues.setSpecId(specId); + this.save(specValues); + specValuesList.add(specValues); + } + } + return specValuesList; + } + + @Override + public List getSpecValues(List specIds) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(SpecValues::getSpecValue, specIds); + return this.list(queryWrapper); + } + + @Override + public SpecValues getSpecValues(String specValue, String specId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SpecValues::getSpecValue, specValue); + queryWrapper.eq(SpecValues::getSpecId, specId); + + SpecValues specValues = this.getOne(queryWrapper); + if (specValues == null) { + specValues = new SpecValues(); + specValues.setSpecValue(specValue); + specValues.setSpecId(specId); + this.save(specValues); + } + return specValues; + } + + @Override + public IPage queryByParams(String specId, String specVal, PageVO pageVo) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotEmpty(specId)) { + queryWrapper.eq(SpecValues::getSpecId, specId); + } + if (StrUtil.isNotEmpty(specVal)) { + queryWrapper.like(SpecValues::getSpecValue, specVal); + } + return this.page(PageUtil.initPage(pageVo), queryWrapper); + } + + @Autowired + public void setSpecificationService(SpecificationService specificationService) { + this.specificationService = specificationService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/goods/serviceimpl/SpecificationServiceImpl.java b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/SpecificationServiceImpl.java new file mode 100644 index 00000000..9793fade --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/goods/serviceimpl/SpecificationServiceImpl.java @@ -0,0 +1,169 @@ +package cn.lili.modules.goods.serviceimpl; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.StrUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.CategorySpecification; +import cn.lili.modules.goods.entity.dos.SpecValues; +import cn.lili.modules.goods.entity.dos.Specification; +import cn.lili.modules.goods.entity.dto.SpecificationSearchParams; +import cn.lili.modules.goods.entity.vos.CategorySpecificationVO; +import cn.lili.modules.goods.entity.vos.GoodsSpecValueVO; +import cn.lili.modules.goods.entity.vos.SpecificationVO; +import cn.lili.modules.goods.mapper.SpecificationMapper; +import cn.lili.modules.goods.service.CategorySpecificationService; +import cn.lili.modules.goods.service.SpecValuesService; +import cn.lili.modules.goods.service.SpecificationService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 商品规格业务层实现 + * + * @author pikachu + * @date 2020-02-18 16:18:56 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SpecificationServiceImpl extends ServiceImpl implements SpecificationService { + //分类-规格绑定 + private final CategorySpecificationService categorySpecificationService; + //规格值 + private SpecValuesService specValuesService; + + @Override + public List getSpecList(Map param) { + return this.baseMapper.findSpecList(param); + } + + @Override + public List getGoodsSpecValue(String categoryId) { + List categorySpecificationVOS = categorySpecificationService.getCategorySpecList(categoryId); + Map map = new HashMap<>(); + if (!categorySpecificationVOS.isEmpty()) { + //循环组织查询规格值数据 + List valueId = new ArrayList<>(); + for (CategorySpecificationVO categorySpecification : categorySpecificationVOS) { + map.put(categorySpecification.getId(), categorySpecification.getName()); + valueId.add(categorySpecification.getId()); + } + //使用valueId去查询规格值 + List specValues = specValuesService.getSpecValues(valueId); + //循环组织数据 + List goodsSpecValueVOS = new ArrayList<>(); + for (Map.Entry m : map.entrySet()) { + GoodsSpecValueVO goodsSpecValueVO = new GoodsSpecValueVO(); + goodsSpecValueVO.setName(m.getValue().toString()); + List list = new ArrayList<>(); + for (SpecValues spec : specValues) { + if (spec.getSpecId().equals(m.getKey())) { + list.add(spec.getSpecValue()); + } + } + goodsSpecValueVO.setValue(list); + goodsSpecValueVOS.add(goodsSpecValueVO); + } + return goodsSpecValueVOS; + } + return new ArrayList<>(); + } + + @Override + public Specification getSpecification(String id) { + Specification specification = this.getById(id); + if (specification == null) { + throw new ServiceException("当前商品已下架"); + } + return specification; + } + + @Override + public IPage getSpecificationPage(SpecificationSearchParams searchParams, PageVO pageVo) { + Map param = new HashMap<>(); + param.put("specName", searchParams.getSpecName()); + List specList = this.getSpecList(param); + IPage page = new Page<>(pageVo.getPageNumber(), pageVo.getPageSize(), specList.size()); + page.setRecords(PageUtil.listToPage(pageVo, specList)); + return page; + } + + @Override + public IPage getSpecificationByPage(SpecificationSearchParams searchParams, PageVO pageVo) { + List specIds = new ArrayList<>(); + if (StrUtil.isNotEmpty(searchParams.getCategoryPath())) { + String categoryPath = searchParams.getCategoryPath(); + List categorySpecList = categorySpecificationService.getCategorySpecList(categoryPath.split(",")); + categorySpecList.forEach(i -> specIds.add(i.getSpecificationId())); + } + QueryWrapper queryWrapper = searchParams.queryWrapper(); + queryWrapper.in("id", specIds); + return this.page(PageUtil.initPage(pageVo), queryWrapper); + } + + @Override + public Specification addSpecification(SpecificationVO specificationVO) { + Specification specification = this.getOne(new LambdaQueryWrapper().eq(Specification::getSpecName, specificationVO.getSpecName())); + if (specification == null) { + this.save(specificationVO); + specification = specificationVO; + } + + CategorySpecification categorySpecification = categorySpecificationService.getOne(new LambdaQueryWrapper().eq(CategorySpecification::getSpecificationId, specification.getId())); + if (categorySpecification == null) { + categorySpecification = new CategorySpecification(); + categorySpecification.setSpecificationId(specification.getId()); + String categoryPath = specificationVO.getCategoryPath(); + if (CharSequenceUtil.isNotEmpty(categoryPath)) { + categorySpecification.setCategoryId(categoryPath.substring(categoryPath.lastIndexOf(",") + 1)); + categorySpecificationService.save(categorySpecification); + } + } + if (CharSequenceUtil.isNotEmpty(specificationVO.getSpecValue())) { + specValuesService.saveSpecValue(specificationVO.getId(), new String[]{specificationVO.getSpecValue()}); + } + return specification; + } + + @Override + public boolean updateSpecification(SpecificationVO specificationVO) { + this.getSpecification(specificationVO.getId()); + return this.updateById(specificationVO); + } + + @Override + public boolean deleteSpecification(List ids) { + for (String id : ids) { + //如果此规格绑定分类则不允许删除 + List list = categorySpecificationService.list(new QueryWrapper().eq("specification_id", id)); + if (!list.isEmpty()) { + throw new ServiceException(ResultCode.SPEC_DELETE_ERROR); + } + //删除规格 + this.removeById(id); + //删除规格值 + specValuesService.remove(new QueryWrapper().eq("spec_id", id)); + } + return true; + } + + @Autowired + public void setSpecValuesService(SpecValuesService specValuesService) { + this.specValuesService = specValuesService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/aop/annotation/PointLogPoint.java b/framework/src/main/java/cn/lili/modules/member/entity/aop/annotation/PointLogPoint.java new file mode 100644 index 00000000..61b3d1a6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/aop/annotation/PointLogPoint.java @@ -0,0 +1,17 @@ +package cn.lili.modules.member.entity.aop.annotation; + + +import java.lang.annotation.*; + +/** + * 会员积分操作aop + * + * @author pikachu + * @date 2020/11/17 7:22 下午 + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface PointLogPoint { + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/aop/interceptor/PointLogInterceptor.java b/framework/src/main/java/cn/lili/modules/member/entity/aop/interceptor/PointLogInterceptor.java new file mode 100644 index 00000000..8e16afd5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/aop/interceptor/PointLogInterceptor.java @@ -0,0 +1,71 @@ +package cn.lili.modules.member.entity.aop.interceptor; + +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberPointsHistory; +import cn.lili.modules.member.service.MemberPointsHistoryService; +import cn.lili.modules.member.service.MemberService; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 积分操作切面 + * + * @author Chopper + * @date 2020/11/17 7:22 下午 + */ +@Aspect +@Component +public class PointLogInterceptor { + + @Autowired + private MemberPointsHistoryService memberPointsHistoryService; + + @Autowired + private MemberService memberService; + + @After("@annotation(cn.lili.modules.member.entity.aop.annotation.PointLogPoint)") + public void doAfter(JoinPoint pjp) { + //参数 + Object[] obj = pjp.getArgs(); + try { + //变动积分 + Long point = 0L; + if (obj[0] != null) { + point = Long.valueOf(obj[0].toString()); + } + //变动类型 + Integer type = 0; + if (obj[1] != null) { + type = Integer.valueOf(obj[1].toString()); + } + //会员ID + String memberId = ""; + if (obj[2] != null) { + memberId = obj[2].toString(); + } + //根据会员id查询会员信息 + Member member = memberService.getById(memberId); + if (member != null) { + MemberPointsHistory memberPointsHistory = new MemberPointsHistory(); + memberPointsHistory.setMemberId(member.getId()); + memberPointsHistory.setMemberName(member.getUsername()); + memberPointsHistory.setPointType(type); + memberPointsHistory.setVariablePoint(point); + memberPointsHistory.setBeforePoint(new Double(CurrencyUtil.sub(member.getPoint(), point)).longValue()); + memberPointsHistory.setPoint(member.getPoint()); + memberPointsHistory.setContent(obj[3] == null ? "" : obj[3].toString()); + memberPointsHistory.setCreateBy("系统"); + memberPointsHistoryService.save(memberPointsHistory); + } + } catch (Exception e) { + e.printStackTrace(); + } + + + } + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/FootPrint.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/FootPrint.java new file mode 100644 index 00000000..9c68a425 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/FootPrint.java @@ -0,0 +1,41 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 浏览历史 + * + * @author Chopper + * @date 2020/11/17 7:22 下午 + */ +@Data +@Entity +@Table(name = "li_foot_print") +@TableName("li_foot_print") +@ApiModel(value = "浏览历史") +@NoArgsConstructor +@AllArgsConstructor +public class FootPrint extends BaseEntity { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "商品ID") + private String goodsId; + + @ApiModelProperty(value = "规格ID") + private String skuId; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/GoodsCollection.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/GoodsCollection.java new file mode 100644 index 00000000..749a97c4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/GoodsCollection.java @@ -0,0 +1,60 @@ +package cn.lili.modules.member.entity.dos; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 会员商品收藏 + * + * @author Chopper + * @date 2020/11/18 3:31 下午 + */ +@Data +@Entity +@NoArgsConstructor +@ApiModel(value = "会员商品收藏") +@TableName("li_goods_collection") +@Table(name = "li_goods_collection") +public class GoodsCollection { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "商品id") + private String skuId; + + public GoodsCollection(String memberId, String goodsId) { + this.memberId = memberId; + this.skuId = goodsId; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/Member.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/Member.java new file mode 100644 index 00000000..ea2aaa44 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/Member.java @@ -0,0 +1,125 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import java.util.Date; + +/** + * 会员 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member") +@TableName("li_member") +@ApiModel(value = "会员") +@NoArgsConstructor +public class Member extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "会员用户名") + private String username; + + @ApiModelProperty(value = "会员密码") + private String password; + + @ApiModelProperty(value = "昵称") + private String nickName; + + @Min(message = "会员性别参数错误", value = 0) + @ApiModelProperty(value = "会员性别,1为男,0为女") + private Integer sex; + + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "会员生日") + private Date birthday; + + @ApiModelProperty(value = "会员地址ID") + private String regionId; + + @ApiModelProperty(value = "会员地址") + private String region; + + @NotEmpty(message = "手机号码不能为空") + @ApiModelProperty(value = "手机号码", required = true) + private String mobile; + + @Min(message = "必须为数字", value = 0) + @ApiModelProperty(value = "积分数量") + private Long point; + + @ApiModelProperty(value = "会员头像") + private String face; + + @ApiModelProperty(value = "会员状态") + private Boolean disabled; + + @ApiModelProperty(value = "是否开通店铺") + private Boolean haveStore; + + @ApiModelProperty(value = "店铺ID") + private String storeId; + + /** + * @see cn.lili.modules.base.entity.enums.ClientTypeEnum + */ + @ApiModelProperty(value = "客户端") + private String clientEnum; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "最后一次登录时间") + private Date lastLoginDate; + + public Member(String username, String password, String mobile) { + this.username = username; + this.password = password; + this.mobile = mobile; + this.nickName = mobile; + this.disabled = true; + this.haveStore = false; + this.sex = 0; + this.point = 0L; + this.lastLoginDate = new Date(); + } + + public Member(String username, String password, String mobile, String nickName, String face) { + this.username = username; + this.password = password; + this.mobile = mobile; + this.nickName = nickName; + this.disabled = true; + this.haveStore = false; + this.face = face; + this.sex = 0; + this.point = 0L; + this.lastLoginDate = new Date(); + } + + public Member(String username, String password, String face, String nickName, Integer sex) { + this.username = username; + this.password = password; + this.mobile = ""; + this.nickName = nickName; + this.disabled = true; + this.haveStore = false; + this.face = face; + this.sex = sex; + this.point = 0L; + this.lastLoginDate = new Date(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberAddress.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberAddress.java new file mode 100644 index 00000000..32ef3871 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberAddress.java @@ -0,0 +1,66 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.common.validation.Mobile; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 会员地址 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_address") +@TableName("li_member_address") +@ApiModel(value = "会员地址") +public class MemberAddress extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "会员ID", hidden = true) + private String memberId; + + @NotEmpty(message = "收货人姓名不能为空") + @ApiModelProperty(value = "收货人姓名") + private String name; + + @Mobile + @ApiModelProperty(value = "手机号码") + private String mobile; + + @NotBlank(message = "地址不能为空") + @ApiModelProperty(value = "地址名称, ','分割") + private String consigneeAddressPath; + + @NotBlank(message = "地址不能为空") + @ApiModelProperty(value = "地址id,','分割 ") + private String consigneeAddressIdPath; + + @NotEmpty(message = "详细地址不能为空") + @ApiModelProperty(value = "详细地址") + private String detail; + + @NotNull(message = "是否默认不能为空") + @ApiModelProperty(value = "是否为默认收货地址") + private Boolean isDefault; + + @ApiModelProperty(value = "地址别名") + private String alias; + + @ApiModelProperty(value = "经度") + private String lon; + + @ApiModelProperty(value = "纬度") + private String lat; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberEvaluation.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberEvaluation.java new file mode 100644 index 00000000..0cf904b7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberEvaluation.java @@ -0,0 +1,139 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.member.entity.dto.MemberEvaluationDTO; +import cn.lili.modules.order.order.entity.dos.Order; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.beans.BeanUtils; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; + +/** + * 会员商品评价 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_evaluation") +@TableName("li_member_evaluation") +@ApiModel(value = "会员商品评价") +@NoArgsConstructor +public class MemberEvaluation extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @NotNull + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @NotNull + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @NotNull + @ApiModelProperty(value = "商品ID") + private String goodsId; + + @NotNull + @ApiModelProperty(value = " SKU ID") + private String skuId; + + @NotNull + @ApiModelProperty(value = "会员名称") + private String memberName; + + @NotNull + @ApiModelProperty(value = "会员头像") + private String memberProfile; + + @NotNull + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @NotNull + @ApiModelProperty(value = "商品图片") + private String goodsImage; + + @NotNull + @ApiModelProperty(value = "订单号") + private String orderNo; + + @NotNull + @ApiModelProperty(value = "好中差评 , GOOD:好评,MODERATE:中评,WORSE:差评", allowableValues = "GOOD,MODERATE,WORSE") + private String grade; + + @NotNull + @ApiModelProperty(value = " 评价内容") + private String content; + + @ApiModelProperty(value = "评价图片") + private String image; + + @NotNull + @ApiModelProperty(value = "状态 OPEN 正常 ,CLOSE 关闭 ") + private String status; + + @ApiModelProperty(value = "评价回复") + private String reply; + + @ApiModelProperty(value = "评价回复图片") + private String replyImage; + + @ApiModelProperty(value = "评论是否有图片 1 有 ,0 没有") + private boolean haveImage; + + @ApiModelProperty(value = "回复是否有图片 1 有 ,0 没有") + private boolean haveReplyImage; + + @ApiModelProperty(value = "回复状态") + private boolean replyStatus; + + @ApiModelProperty(value = "物流评分") + private Integer deliveryScore; + + @ApiModelProperty(value = "服务评分") + private Integer serviceScore; + + @ApiModelProperty(value = "描述评分") + private Integer descriptionScore; + + + public MemberEvaluation(MemberEvaluationDTO memberEvaluationDTO, GoodsSku goodsSku, Member member,Order order){ + //复制评价信息 + BeanUtils.copyProperties(memberEvaluationDTO, this); + //设置会员 + this.memberId=member.getId(); + //会员名称 + this.memberName=member.getNickName(); + //设置会员头像 + this.memberProfile=member.getFace(); + //商品名称 + this.goodsName=goodsSku.getGoodsName(); + //商品图片 + this.goodsImage=goodsSku.getThumbnail(); + //设置店铺ID + this.storeId=order.getStoreId(); + //设置店铺名称 + this.storeName=order.getStoreName(); + //设置订单编号 + this.orderNo=order.getSn(); + //是否包含图片 + this.haveImage=StringUtils.isNotEmpty(memberEvaluationDTO.getImages()); + //默认开启评价 + this.status=SwitchEnum.OPEN.name(); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberMessage.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberMessage.java new file mode 100644 index 00000000..d0ce5fa6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberMessage.java @@ -0,0 +1,46 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.message.entity.enums.MessageStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 会员消息 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_message") +@TableName("li_member_message") +@ApiModel(value = "会员消息") +public class MemberMessage extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "消息标题") + private String title; + + @ApiModelProperty(value = "消息内容") + private String content; + + /** + * @see MessageStatusEnum + */ + @ApiModelProperty(value = "状态 0默认未读 1已读 2回收站") + private String status = MessageStatusEnum.UN_READY.name(); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberNotice.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberNotice.java new file mode 100644 index 00000000..12fdb810 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberNotice.java @@ -0,0 +1,43 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 会员站内信 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_notice") +@TableName("li_member_notice") +@ApiModel(value = "会员站内信") +public class MemberNotice extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "是否已读") + private Boolean isRead; + + @ApiModelProperty(value = "阅读时间") + private Long receiveTime; + + @ApiModelProperty(value = "标题") + private String title; + + @ApiModelProperty(value = "站内信内容") + private String content; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberNoticeLog.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberNoticeLog.java new file mode 100644 index 00000000..44dfe402 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberNoticeLog.java @@ -0,0 +1,76 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; + +/** + * 会员消息 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_notice_log") +@TableName("li_member_notice_log") +@ApiModel(value = "会员消息") +public class MemberNoticeLog extends BaseEntity { + + private static final long serialVersionUID = 1L; + /** + * 标题 + */ + @Column(name = "title") + @ApiModelProperty(value = "标题") + private String title; + /** + * 消息内容 + */ + @Column(name = "content") + @ApiModelProperty(value = "消息内容") + private String content; + /** + * 会员id + */ + @Column(name = "member_ids") + @ApiModelProperty(value = "会员id") + private String memberIds; + /** + * 管理员id + */ + @Column(name = "admin_id") + @ApiModelProperty(value = "管理员id") + private String adminId; + /** + * 管理员名称 + */ + @Column(name = "admin_name") + @ApiModelProperty(value = "管理员名称") + private String adminName; + /** + * 发送时间 + */ + @Column(name = "send_time") + @ApiModelProperty(value = "发送时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date sendTime; + /** + * 发送类型 + */ + @Column(name = "send_type") + @ApiModelProperty(value = "发送类型,0全站,1指定会员") + private Integer sendType; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberNoticeSenter.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberNoticeSenter.java new file mode 100644 index 00000000..e7c8d4c8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberNoticeSenter.java @@ -0,0 +1,50 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 会员消息 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_notice_senter") +@TableName("li_member_notice_senter") +@ApiModel(value = "会员消息") +public class MemberNoticeSenter extends BaseEntity { + /** + * 标题 + */ + @Column(name = "title") + @ApiModelProperty(value = "标题") + private String title; + /** + * 消息内容 + */ + @Column(name = "content") + @ApiModelProperty(value = "消息内容") + private String content; + /** + * 会员id + */ + @Column(name = "member_ids") + @ApiModelProperty(value = "会员id") + private String memberIds; + /** + * 发送类型 + */ + @Column(name = "send_type") + @ApiModelProperty(value = "发送类型,ALL 全站,SELECT 指定会员") + private String sendType; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberPointsHistory.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberPointsHistory.java new file mode 100644 index 00000000..7b11e00b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberPointsHistory.java @@ -0,0 +1,81 @@ +package cn.lili.modules.member.entity.dos; + + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import java.util.Date; + +/** + * 会员积分历史 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_points_history") +@TableName("li_member_points_history") +@ApiModel(value = "会员积分历史") +public class MemberPointsHistory { + + private static final long serialVersionUID = 1L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "当前积分") + private Long point; + + @ApiModelProperty(value = "消费之前积分") + private Long beforePoint; + + @ApiModelProperty(value = "消费积分") + private Long variablePoint; + + @ApiModelProperty(value = "content") + private String content; + + @Min(message = "最小值为0", value = 0) + @Max(message = "最大值为1", value = 1) + @ApiModelProperty(value = "消费积分类型,1为增加,0为消费") + private Integer pointType; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberReceipt.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberReceipt.java new file mode 100644 index 00000000..b8ef4538 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberReceipt.java @@ -0,0 +1,79 @@ +package cn.lili.modules.member.entity.dos; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 会员发票 + * + * @author Chopper + * @date 2021-03-29 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_receipt") +@TableName("li_member_receipt") +@ApiModel(value = "会员发票") +public class MemberReceipt { + + private static final long serialVersionUID = -8210927482915675995L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @ApiModelProperty(value = "发票抬头") + private String receiptTitle; + + @ApiModelProperty(value = "纳税人识别号") + private String taxpayerId; + + @ApiModelProperty(value = "发票内容") + private String receiptContent; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + /** + * @see cn.lili.modules.member.entity.enums.MemberReceiptEnum + */ + @ApiModelProperty(value = "发票类型") + private String receiptType; + + @ApiModelProperty(value = "是否为默认选项 0:否,1:是") + private Integer isDefault; + + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "删除标志 true/false 删除/未删除", hidden = true) + private Boolean deleteFlag; + + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberSign.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberSign.java new file mode 100644 index 00000000..2691fedd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberSign.java @@ -0,0 +1,58 @@ +package cn.lili.modules.member.entity.dos; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 会员签到 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_sign") +@TableName("li_member_sign") +@ApiModel(value = "会员签到") +public class MemberSign { + + private static final long serialVersionUID = 1L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "会员用户名") + private String memberName; + + @ApiModelProperty(value = "会员用户ID") + private String memberId; + + @ApiModelProperty(value = "连续签到天数") + private Integer signDay; + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberWallet.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberWallet.java new file mode 100644 index 00000000..cf7c3a35 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberWallet.java @@ -0,0 +1,42 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 会员预存款 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_wallet") +@TableName("li_member_wallet") +@ApiModel(value = "会员预存款") +public class MemberWallet extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "会员用户名") + private String memberName; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "会员预存款") + private Double memberWallet; + + @ApiModelProperty(value = "会员预存款冻结金额,提现使用") + private Double memberFrozenWallet; + + @ApiModelProperty(value = "预存款密码") + private String walletPassword; + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberWithdrawApply.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberWithdrawApply.java new file mode 100644 index 00000000..5d3a779c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/MemberWithdrawApply.java @@ -0,0 +1,56 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; + +/** + * 会员提现申请 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_member_withdraw_apply") +@TableName("li_member_withdraw_apply") +@ApiModel(value = "会员提现申请") +public class MemberWithdrawApply extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "申请提现金额") + private Double applyMoney; + + @ApiModelProperty(value = "提现状态") + private String applyStatus; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "审核备注") + private String inspectRemark; + + @ApiModelProperty(value="审核时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + private Date inspectTime; + + @ApiModelProperty(value="sn") + private String sn; + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dos/StoreCollection.java b/framework/src/main/java/cn/lili/modules/member/entity/dos/StoreCollection.java new file mode 100644 index 00000000..09aba9b8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dos/StoreCollection.java @@ -0,0 +1,38 @@ +package cn.lili.modules.member.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 会员店铺收藏 + * + * @author Chopper + * @date 2020/11/18 3:32 下午 + */ +@Data +@Entity +@Table(name = "li_store_collection") +@TableName("li_store_collection") +@ApiModel(value = "会员收藏") +@NoArgsConstructor +@AllArgsConstructor +public class StoreCollection extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "店铺id") + private String storeId; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/EvaluationQueryParams.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/EvaluationQueryParams.java new file mode 100644 index 00000000..aa150722 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/EvaluationQueryParams.java @@ -0,0 +1,85 @@ +package cn.lili.modules.member.entity.dto; + +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 评价查询条件 + * + * @author Bulbasaur + * @date 2020/11/30 14:52 + */ +@Data +public class EvaluationQueryParams extends PageVO { + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "卖家名称") + private String storeName; + + @ApiModelProperty(value = "卖家ID") + private String storeId; + + @ApiModelProperty(value = "买家ID", hidden = true) + private String memberId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品ID") + private String goodsId; + + @ApiModelProperty(value = "好中差评 , GOOD:好评,MODERATE:中评,WORSE:差评", allowableValues = "GOOD,MODERATE,WORSE") + private String grade; + + @ApiModelProperty(value = "是否有图") + private String haveImage; + + @ApiModelProperty(value = "评论日期--开始时间") + private String startTime; + + @ApiModelProperty(value = "评论日期--结束时间") + private String endTime; + + public EvaluationQueryParams() { + + } + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime)) { + queryWrapper.between("create_time", startTime, endTime); + } + if (StringUtils.isNotEmpty(grade)) { + queryWrapper.eq("grade", grade); + } + if (StringUtils.isNotEmpty(goodsName)) { + queryWrapper.like("goods_name", goodsName); + } + if (StringUtils.isNotEmpty(storeName)) { + queryWrapper.like("store_name", storeName); + } + if (StringUtils.isNotEmpty(memberName)) { + queryWrapper.like("member_name", memberName); + } + if (StringUtils.isNotEmpty(goodsId)) { + queryWrapper.eq("goods_id", goodsId); + } + if (StringUtils.isNotEmpty(storeId)) { + queryWrapper.eq("store_id", storeId); + } + if (StringUtils.isNotEmpty(memberId)) { + queryWrapper.eq("member_id", memberId); + } + if (StringUtils.isNotEmpty(haveImage)) { + queryWrapper.eq("have_image", haveImage); + } + queryWrapper.eq("delete_flag", false); + queryWrapper.orderByDesc("create_time"); + return queryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/ManagerMemberEditDTO.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/ManagerMemberEditDTO.java new file mode 100644 index 00000000..1e2987a7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/ManagerMemberEditDTO.java @@ -0,0 +1,54 @@ +package cn.lili.modules.member.entity.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** + * 会员修改 + * 用于后台的用户信息修改 + * + * @author Bulbasaur + * @date 2020/12/15 9:57 + */ +@Data +public class ManagerMemberEditDTO { + + @ApiModelProperty(value = "会员用户名,用户名不能进行修改", required = true) + @NotNull(message = "会员用户名不能为空") + private String username; + + @ApiModelProperty(value = "会员密码") + private String password; + + @ApiModelProperty(value = "昵称") + @Length(min = 2, max = 20, message = "会员昵称必须为2到20位之间") + private String nickName; + + @ApiModelProperty(value = "地区") + private String region; + + @ApiModelProperty(value = "地区ID") + private String regionId; + + @Min(message = "必须为数字且1为男,0为女", value = 0) + @Max(message = "必须为数字且1为男,0为女", value = 1) + @NotNull(message = "会员性别不能为空") + @ApiModelProperty(value = "会员性别,1为男,0为女") + private Integer sex; + + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "会员生日") + private Date birthday; + + @ApiModelProperty(value = "会员头像") + private String face; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberAddDTO.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberAddDTO.java new file mode 100644 index 00000000..30a4074c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberAddDTO.java @@ -0,0 +1,31 @@ +package cn.lili.modules.member.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * 添加会员DTO + * + * @author Bulbasaur + * @date 2020/12/14 16:31 + */ +@Data +public class MemberAddDTO { + + @NotEmpty(message = "会员用户名必填") + @Size(min = 6, max = 30) + @ApiModelProperty(value = "会员用户名") + private String username; + + @ApiModelProperty(value = "会员密码") + private String password; + + @NotEmpty(message = "手机号码不能为空") + @ApiModelProperty(value = "手机号码", required = true) + @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误") + private String mobile; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberAddressDTO.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberAddressDTO.java new file mode 100644 index 00000000..3fe583f9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberAddressDTO.java @@ -0,0 +1,38 @@ +package cn.lili.modules.member.entity.dto; + +import cn.lili.common.validation.Mobile; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; + +/** + * 会员地址DTO + * + * @author Bulbasaur + * @date 2020/12/14 16:31 + */ +@Data +public class MemberAddressDTO { + + @NotEmpty(message = "收货人姓名不能为空") + @ApiModelProperty(value = "收货人姓名") + private String consigneeName; + + @Mobile + @ApiModelProperty(value = "手机号码") + private String consigneeMobile; + + @NotBlank(message = "地址不能为空") + @ApiModelProperty(value = "地址名称, ','分割") + private String consigneeAddressPath; + + @NotBlank(message = "地址不能为空") + @ApiModelProperty(value = "地址id,','分割 ") + private String consigneeAddressIdPath; + + @NotEmpty(message = "详细地址不能为空") + @ApiModelProperty(value = "详细地址") + private String consigneeDetail; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberEditDTO.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberEditDTO.java new file mode 100644 index 00000000..f07866cd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberEditDTO.java @@ -0,0 +1,51 @@ +package cn.lili.modules.member.entity.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.util.Date; + +/** + * 会员信息修改DTO + * + * @author Bulbasaur + * @date 2020/12/11 14:39 + */ +@Data +public class MemberEditDTO { + + + @ApiModelProperty(value = "昵称", required = true) + @Size(min = 2, max = 20, message = "会员昵称必须为2到20位之间") + private String nickName; + + @ApiModelProperty(value = "会员地址ID") + private String regionId; + + @ApiModelProperty(value = "会员地址") + private String region; + + @Min(message = "必须为数字且1为男,0为女", value = 0) + @Max(message = "必须为数字且1为男,0为女", value = 1) + @NotNull(message = "会员性别不能为空") + @ApiModelProperty(value = "会员性别,1为男,0为女", required = true) + private Integer sex; + + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "会员生日") + private Date birthday; + + @ApiModelProperty(value = "详细地址") + private String address; + + @ApiModelProperty(value = "会员头像") + private String face; + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberEvaluationDTO.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberEvaluationDTO.java new file mode 100644 index 00000000..825944a3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberEvaluationDTO.java @@ -0,0 +1,52 @@ +package cn.lili.modules.member.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; + +/** + * 会员评价DTO + * + * @author Chopper + * @date 2020/11/29 11:13 下午 + */ +@Data +public class MemberEvaluationDTO { + + @ApiModelProperty(value = "子订单编号") + @NotEmpty(message = "订单异常") + private String orderItemSn; + + @ApiModelProperty(value = "商品ID") + @NotEmpty(message = "订单商品异常不能为空") + private String goodsId; + + @ApiModelProperty(value = "规格ID") + @NotEmpty(message = "订单商品不能为空") + private String skuId; + + @ApiModelProperty(value = "好中差评价") + @NotEmpty(message = "请评价") + private String grade; + + @ApiModelProperty(value = "评论内容") + @NotEmpty(message = "评论内容不能为空") + @Length(max = 500, message = "评论内容不能超过500字符") + private String content; + + @ApiModelProperty(value = "评论图片") + private String images; + + @ApiModelProperty(value = "物流评分") + private Integer deliveryScore; + + @ApiModelProperty(value = "服务评分") + private Integer serviceScore; + + @ApiModelProperty(value = "描述评分") + private Integer descriptionScore; + + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberPointMessage.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberPointMessage.java new file mode 100644 index 00000000..a1eddc2a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberPointMessage.java @@ -0,0 +1,23 @@ +package cn.lili.modules.member.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 会员积分 + * + * @author Bulbasaur + * @date 2020/12/14 16:31 + */ +@Data +public class MemberPointMessage { + + @ApiModelProperty(value = "积分") + private Long point; + + @ApiModelProperty(value = "类型 1为增加") + private Integer type; + + @ApiModelProperty(value = "会员id") + private String memberId; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberWithdrawalMessage.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberWithdrawalMessage.java new file mode 100644 index 00000000..c12fc573 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/MemberWithdrawalMessage.java @@ -0,0 +1,26 @@ +package cn.lili.modules.member.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 会员提现消息 + * + * @author Bulbasaur + * @date 2020/12/14 16:31 + */ +@Data +public class MemberWithdrawalMessage { + + @ApiModelProperty(value = "金额") + private Double price; + + @ApiModelProperty(value = "会员id") + private String memberId; + + /** + * @see cn.lili.modules.member.entity.enums.MemberWithdrawalDestinationEnum + */ + @ApiModelProperty(value = "提现到哪里") + private String destination; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/dto/StoreEvaluationQueryParams.java b/framework/src/main/java/cn/lili/modules/member/entity/dto/StoreEvaluationQueryParams.java new file mode 100644 index 00000000..d1d76775 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/dto/StoreEvaluationQueryParams.java @@ -0,0 +1,59 @@ +package cn.lili.modules.member.entity.dto; + +import cn.hutool.core.date.DateUtil; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 店铺评价查询参数 + * + * @author Chopper + * @date 2021/3/20 10:43 + */ + +@Data +public class StoreEvaluationQueryParams extends PageVO { + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "好中差评 good:好评,neutral:中评,bad:差评", allowableValues = "GOOD,NEUTRAL,BAD") + private String grade; + + @ApiModelProperty(value = "评论日期--开始时间") + private String startDate; + + @ApiModelProperty(value = "评论日期--结束时间") + private String endDate; + + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + + queryWrapper.eq("store_id", UserContext.getCurrentUser().getStoreId()); + + if (StringUtils.isNotEmpty(startDate) && StringUtils.isNotEmpty(endDate)) { + queryWrapper.between("create_time", DateUtil.parse(startDate), DateUtil.parse(endDate)); + } + + if (StringUtils.isNotEmpty(grade)) { + queryWrapper.eq("grade", grade); + } + + if (StringUtils.isNotEmpty(goodsName)) { + queryWrapper.eq("goods_name", goodsName); + } + + if (StringUtils.isNotEmpty(memberName)) { + queryWrapper.eq("member_name", memberName); + } + return queryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/enums/EvaluationGradeEnum.java b/framework/src/main/java/cn/lili/modules/member/entity/enums/EvaluationGradeEnum.java new file mode 100644 index 00000000..a6a0758b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/enums/EvaluationGradeEnum.java @@ -0,0 +1,18 @@ +package cn.lili.modules.member.entity.enums; + +/** + * 评价枚举 + * + * @author Chopper + * @date 2021/3/20 10:44 + */ + +public enum EvaluationGradeEnum { + /** + * 好,中,差 + */ + GOOD, MODERATE, WORSE; + + EvaluationGradeEnum() { + } +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/enums/MemberReceiptEnum.java b/framework/src/main/java/cn/lili/modules/member/entity/enums/MemberReceiptEnum.java new file mode 100644 index 00000000..9e846658 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/enums/MemberReceiptEnum.java @@ -0,0 +1,28 @@ +package cn.lili.modules.member.entity.enums; + +/** + * 发票类型 + * + * @author Chopper + * @date 2021-03-29 14:10:16 + */ +public enum MemberReceiptEnum { + + /** + * 发票类型 + */ + ELECTRONIC_INVOICE("电子发票"), + ORDINARY_INVOICE("普通发票"); + + private String description; + + MemberReceiptEnum(String str) { + this.description = str; + + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/enums/MemberWithdrawalDestinationEnum.java b/framework/src/main/java/cn/lili/modules/member/entity/enums/MemberWithdrawalDestinationEnum.java new file mode 100644 index 00000000..bdbbedee --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/enums/MemberWithdrawalDestinationEnum.java @@ -0,0 +1,27 @@ +package cn.lili.modules.member.entity.enums; + +/** + * 会员提现到哪里 枚举 + * + * @author Chopper + * @date 2021/3/20 10:44 + */ + +public enum MemberWithdrawalDestinationEnum { + /** + * 提现目的地 + */ + WECHAT("微信账户"), + WALLET("余额账户"); + + private String description; + + MemberWithdrawalDestinationEnum(String str) { + this.description = str; + + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/enums/SendTypeEnum.java b/framework/src/main/java/cn/lili/modules/member/entity/enums/SendTypeEnum.java new file mode 100644 index 00000000..f34f30a1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/enums/SendTypeEnum.java @@ -0,0 +1,28 @@ +package cn.lili.modules.member.entity.enums; + +/** + * 发送类型 + * + * @author Chopper + * @date 2020-03-10 11:35 上午 + */ +public enum SendTypeEnum { + + /** + * 消息类型 + */ + ALL("全部"), + SELECT("指定会员"); + + private String description; + + SendTypeEnum(String str) { + this.description = str; + + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/enums/WithdrawStatusEnum.java b/framework/src/main/java/cn/lili/modules/member/entity/enums/WithdrawStatusEnum.java new file mode 100755 index 00000000..bb4ea569 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/enums/WithdrawStatusEnum.java @@ -0,0 +1,33 @@ +package cn.lili.modules.member.entity.enums; + +/** + * 提现申请状态枚举类 + * + * @author pikachu + * @date 2020-11-06 + */ +public enum WithdrawStatusEnum { + /** + * 申请中 + */ + APPLY("申请中"), + /** + * 审核成功即提现成功 + */ + VIA_AUDITING("审核通过"), + /** + * 审核未通过 + */ + FAIL_AUDITING("审核未通过"); + + private String description; + + public String description() { + return description; + } + + WithdrawStatusEnum(String description) { + this.description = description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/EvaluationNumberVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/EvaluationNumberVO.java new file mode 100644 index 00000000..9993b449 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/EvaluationNumberVO.java @@ -0,0 +1,29 @@ +package cn.lili.modules.member.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 评价数量VO + * + * @author Chopper + * @date 2021/1/27 10:41 上午 + */ +@Data +public class EvaluationNumberVO { + + @ApiModelProperty(value = "全部商品") + private Integer all; + + @ApiModelProperty(value = "好评数量") + private Integer good; + + @ApiModelProperty(value = "中评数量") + private Integer moderate; + + @ApiModelProperty(value = "差评数量") + private Integer worse; + + @ApiModelProperty(value = "有图数量") + private Integer haveImage; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/GoodsCollectionVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/GoodsCollectionVO.java new file mode 100644 index 00000000..e0478568 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/GoodsCollectionVO.java @@ -0,0 +1,35 @@ +package cn.lili.modules.member.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 会员商品收藏VO + * + * @author Chopper + * @date 2021/1/27 10:41 上午 + */ +@Data +public class GoodsCollectionVO { + + @ApiModelProperty(value = "id") + private String id; + + @ApiModelProperty(value = "商品ID") + private String goodsId; + + @ApiModelProperty(value = "规格ID") + private String skuId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品图片") + private String image; + + @ApiModelProperty(value = "商品价格") + private Double price; + + @ApiModelProperty(value = "已失效") + private String marketEnable; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberDistributionVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberDistributionVO.java new file mode 100644 index 00000000..3b02e2dc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberDistributionVO.java @@ -0,0 +1,24 @@ +package cn.lili.modules.member.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 会员分布VO + * + * @author Chopper + * @date 2021-02-26 17:25 + */ +@Data +public class MemberDistributionVO { + + @ApiModelProperty(value = "客户端类型") + private String clientEnum; + + @ApiModelProperty(value = "数量") + private Integer num; + + @ApiModelProperty(value = "比例") + private Double proportion; + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberEvaluationListVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberEvaluationListVO.java new file mode 100644 index 00000000..a9688392 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberEvaluationListVO.java @@ -0,0 +1,46 @@ +package cn.lili.modules.member.entity.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 会员评价VO + * + * @author Bulbasaur + * @date 2020/11/30 15:00 + */ +@Data +public class MemberEvaluationListVO { + + @ApiModelProperty(value = "评论ID") + private String id; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "好中差评", allowableValues = "GOOD,NEUTRAL,BAD") + private String grade; + + @ApiModelProperty(value = "评价内容") + private String content; + + @ApiModelProperty(value = "状态 ", allowableValues = " OPEN 正常 ,CLOSE 关闭") + private String status; + + @ApiModelProperty(value = "回复状态") + private boolean replyStatus; + + @ApiModelProperty(value = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberEvaluationVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberEvaluationVO.java new file mode 100644 index 00000000..1806d0e4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberEvaluationVO.java @@ -0,0 +1,32 @@ +package cn.lili.modules.member.entity.vo; + +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 会员评价VO + * + * @author Bulbasaur + * @date 2020/11/30 15:00 + */ +@Data +@NoArgsConstructor +public class MemberEvaluationVO extends MemberEvaluation { + + private static final long serialVersionUID = 6696978796248845481L; + + @ApiModelProperty(value = "评论图片") + private List evaluationImages; + + @ApiModelProperty(value = "回复评论图片") + private List replyEvaluationImages; + + public MemberEvaluationVO(MemberEvaluation memberEvaluation) { + BeanUtil.copyProperties(memberEvaluation, this); + } +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberMessageQueryVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberMessageQueryVO.java new file mode 100644 index 00000000..4264facf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberMessageQueryVO.java @@ -0,0 +1,31 @@ +package cn.lili.modules.member.entity.vo; + +import cn.lili.modules.message.entity.enums.MessageStatusEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + + +/** + * 会员消息查询 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Data +public class MemberMessageQueryVO { + + private static final long serialVersionUID = 1L; + + /** + * @see MessageStatusEnum + */ + @ApiModelProperty(value = "状态") + private String status; + + @ApiModelProperty(value = "消息标题") + private String title; + + @ApiModelProperty(value = "会员id") + private String memberId; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberPointsHistoryVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberPointsHistoryVO.java new file mode 100644 index 00000000..04b43ada --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberPointsHistoryVO.java @@ -0,0 +1,22 @@ +package cn.lili.modules.member.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 会员积分VO + * + * @author Chopper + * @date 2021/2/25 9:52 上午 + */ +@Data +public class MemberPointsHistoryVO { + + @ApiModelProperty(value = "积分总数") + private Long point; + + @ApiModelProperty(value = "未使用积分总数") + private Long variablePoint; + + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberReceiptAddVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberReceiptAddVO.java new file mode 100644 index 00000000..4c3d030f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberReceiptAddVO.java @@ -0,0 +1,41 @@ +package cn.lili.modules.member.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + + +/** + * 会员发票添加VO + * + * @author Chopper + * @date 2021-03-29 14:10:16 + */ +@Data +@ApiModel(value = "会员发票") +public class MemberReceiptAddVO { + + private static final long serialVersionUID = -8267092982915677995L; + + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @ApiModelProperty(value = "发票抬头") + private String receiptTitle; + + @ApiModelProperty(value = "纳税人识别号") + private String taxpayerId; + + @ApiModelProperty(value = "发票内容") + private String receiptContent; + + /** + * @see cn.lili.modules.member.entity.enums.MemberReceiptEnum + */ + @ApiModelProperty(value = "发票类型") + private String receiptType; + + @ApiModelProperty(value = "是否为默认选项 0:否,1:是") + private Integer isDefault; + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberReceiptVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberReceiptVO.java new file mode 100644 index 00000000..9de46587 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberReceiptVO.java @@ -0,0 +1,55 @@ +package cn.lili.modules.member.entity.vo; + +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.member.entity.dos.MemberReceipt; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + + +/** + * 会员发票查询VO + * + * @author Chopper + * @date 2021-03-29 14:10:16 + */ +@Data +@ApiModel(value = "会员发票") +public class MemberReceiptVO { + + private static final long serialVersionUID = -8210927982915677995L; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + /** + * @see cn.lili.modules.member.entity.enums.MemberReceiptEnum + */ + @ApiModelProperty(value = "发票类型") + private String receiptType; + + public LambdaQueryWrapper lambdaQueryWrapper() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + //会员名称查询 + if (StringUtils.isNotEmpty(memberName)) { + queryWrapper.like(MemberReceipt::getMemberName, memberName); + } + //会员id查询 + if (StringUtils.isNotEmpty(memberId)) { + queryWrapper.eq(MemberReceipt::getMemberId, memberId); + } + //会员id查询 + if (StringUtils.isNotEmpty(receiptType)) { + queryWrapper.eq(MemberReceipt::getReceiptType, receiptType); + } + queryWrapper.eq(MemberReceipt::getDeleteFlag, true); + queryWrapper.orderByDesc(MemberReceipt::getCreateTime); + return queryWrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberSearchVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberSearchVO.java new file mode 100644 index 00000000..f40e5eba --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberSearchVO.java @@ -0,0 +1,30 @@ +package cn.lili.modules.member.entity.vo; + +import cn.lili.common.enums.SwitchEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 会员搜索VO + * + * @author Bulbasaur + * @date 2020/12/15 10:48 + */ +@Data +public class MemberSearchVO { + + @ApiModelProperty(value = "用户名") + private String username; + + @ApiModelProperty(value = "昵称") + private String nickName; + + @ApiModelProperty(value = "用户手机号码") + private String mobile; + + /** + * @see SwitchEnum + */ + @ApiModelProperty(value = "会员状态") + private String disabled; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberWalletVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberWalletVO.java new file mode 100644 index 00000000..257d7cbe --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberWalletVO.java @@ -0,0 +1,29 @@ +package cn.lili.modules.member.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + + +/** + * 会员预存款VO + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +@ApiModel(value = "会员预存款") +@NoArgsConstructor +@AllArgsConstructor +public class MemberWalletVO { + + @ApiModelProperty(value = "会员预存款") + private Double memberWallet; + + @ApiModelProperty(value = "会员预存款冻结金额,提现使用") + private Double memberFrozenWallet; + + +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberWithdrawApplyQueryVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberWithdrawApplyQueryVO.java new file mode 100644 index 00000000..c3fd1e1c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/MemberWithdrawApplyQueryVO.java @@ -0,0 +1,59 @@ +package cn.lili.modules.member.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 余额提现记录查询条件 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +@ApiModel(value = "余额提现记录查询条件") +@AllArgsConstructor +@NoArgsConstructor +public class MemberWithdrawApplyQueryVO implements Serializable { + + + private static final long serialVersionUID = 4735408873104054674L; + + /** + * 充值订单编号 + */ + @ApiModelProperty(value = "充值订单编号") + private String sn; + + /** + * 会员ID + */ + @ApiModelProperty(value = "会员Id") + private String memberId; + /** + * 会员名称 + */ + @ApiModelProperty(value = "会员名称") + private String memberName; + /** + * 提现申请状态 + */ + @ApiModelProperty(value = "提现申请状态") + private String applyStatus; + /** + * 提现申请时间 + */ + @ApiModelProperty(value = "提现申请时间起始日期") + private String startDate; + /** + * 提现申请时间 + */ + @ApiModelProperty(value = "提现申请时间结束日期") + private String endDate; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/StoreCollectionVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/StoreCollectionVO.java new file mode 100644 index 00000000..d34f14e4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/StoreCollectionVO.java @@ -0,0 +1,26 @@ +package cn.lili.modules.member.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 会员店铺收藏VO + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +public class StoreCollectionVO { + + @ApiModelProperty(value = "店铺id") + private String storeId; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "店铺Logo") + private String logo; + + @ApiModelProperty(value = "是否自营") + private boolean selfOperated; +} diff --git a/framework/src/main/java/cn/lili/modules/member/entity/vo/StoreRatingVO.java b/framework/src/main/java/cn/lili/modules/member/entity/vo/StoreRatingVO.java new file mode 100644 index 00000000..e455e1f7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/entity/vo/StoreRatingVO.java @@ -0,0 +1,24 @@ +package cn.lili.modules.member.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 评分VO + * + * @author Chopper + * @date 2021/3/15 5:55 下午 + */ +@Data +public class StoreRatingVO { + + @ApiModelProperty(value = "物流评分") + private String deliveryScore; + + @ApiModelProperty(value = "服务评分") + private String serviceScore; + + @ApiModelProperty(value = "描述评分") + private String descriptionScore; + +} diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/FootprintMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/FootprintMapper.java new file mode 100644 index 00000000..741d1c40 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/FootprintMapper.java @@ -0,0 +1,33 @@ +package cn.lili.modules.member.mapper; + +import cn.lili.modules.member.entity.dos.FootPrint; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 浏览历史数据处理层 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +public interface FootprintMapper extends BaseMapper { + + @Select("select sku_id from li_foot_print ${ew.customSqlSegment} ") + List footprintSkuIdList(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 删除超过100条后的记录 + * @param memberId 会员ID + */ + @Delete("DELETE FROM li_foot_print WHERE (SELECT COUNT(b.id) FROM ( SELECT * FROM li_foot_print WHERE member_id = #{memberId} ) b) >100 " + + " AND id =(SELECT a.id FROM ( SELECT * FROM li_foot_print WHERE member_id = #{memberId} ORDER BY create_time ASC LIMIT 1 ) AS a)") + void deleteLastFootPrint(String memberId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/GoodsCollectionMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/GoodsCollectionMapper.java new file mode 100644 index 00000000..72007cd7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/GoodsCollectionMapper.java @@ -0,0 +1,23 @@ +package cn.lili.modules.member.mapper; + +import cn.lili.modules.member.entity.dos.GoodsCollection; +import cn.lili.modules.member.entity.vo.GoodsCollectionVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 会员收藏数据处理层 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +public interface GoodsCollectionMapper extends BaseMapper { + + @Select("select gc.id AS id,gs.id as sku_id,gs.goods_id as goods_id,gs.goods_name as goods_name,gs.thumbnail as image,gs.price,gs.market_enable AS market_enable from li_goods_collection gc INNER JOIN li_goods_sku gs ON gc.sku_id=gs.id ${ew.customSqlSegment} ") + IPage goodsCollectionVOList(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberAddressMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberAddressMapper.java new file mode 100644 index 00000000..525d768d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberAddressMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.member.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import cn.lili.modules.member.entity.dos.MemberAddress; + +/** + * 会员地址数据处理层 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +public interface MemberAddressMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberEvaluationMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberEvaluationMapper.java new file mode 100644 index 00000000..829b1b44 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberEvaluationMapper.java @@ -0,0 +1,39 @@ +package cn.lili.modules.member.mapper; + +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import cn.lili.modules.member.entity.vo.MemberEvaluationListVO; +import cn.lili.modules.member.entity.vo.StoreRatingVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; +import java.util.Map; + +/** + * 会员商品评价数据处理层 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +public interface MemberEvaluationMapper extends BaseMapper { + + + @Select("select me.* from li_member_evaluation as me ${ew.customSqlSegment}") + IPage getMemberEvaluationList(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Select("select grade,count(1) as num from li_member_evaluation Where goods_id=#{goodsId} GROUP BY grade") + List> getEvaluationNumber(String goodsId); + + @Select("SELECT round( AVG( delivery_score ), 2 ) AS delivery_score" + + ",round( AVG( description_score ), 2 ) AS description_score" + + ",round( AVG( service_score ), 2 ) AS service_score " + + "FROM li_member_evaluation ${ew.customSqlSegment}") + StoreRatingVO getStoreRatingVO(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Select("SELECT goods_id,COUNT(goods_id) AS num FROM li_member_evaluation GROUP BY goods_id") + List> memberEvaluationNum(@Param(Constants.WRAPPER) Wrapper queryWrapper); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberMapper.java new file mode 100644 index 00000000..78b3217f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberMapper.java @@ -0,0 +1,24 @@ +package cn.lili.modules.member.mapper; + + +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.vo.MemberDistributionVO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 会员数据处理层 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +public interface MemberMapper extends BaseMapper { + + @Select("select m.mobile from li_member m") + List getAllMemberMobile(); + + @Select("select client_enum,count(0) as num from li_member group by client_enum") + List distribution(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberMessageMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberMessageMapper.java new file mode 100644 index 00000000..a727b6e2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberMessageMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.member.mapper; + + +import cn.lili.modules.member.entity.dos.MemberMessage; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + + +/** + * 会员消息数据处理层 + * + * @author lili + * @date 2020-02-25 14:10:16 + */ +public interface MemberMessageMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberNoticeLogMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberNoticeLogMapper.java new file mode 100644 index 00000000..4c806c27 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberNoticeLogMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.member.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import cn.lili.modules.member.entity.dos.MemberNoticeLog; + +/** + * 会员消息数据处理层 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +public interface MemberNoticeLogMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberNoticeMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberNoticeMapper.java new file mode 100644 index 00000000..a13a70cd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberNoticeMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.member.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import cn.lili.modules.member.entity.dos.MemberNotice; + +/** + * 会员站内信数据处理层 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +public interface MemberNoticeMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberNoticeSenterMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberNoticeSenterMapper.java new file mode 100644 index 00000000..44ed0c07 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberNoticeSenterMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.member.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import cn.lili.modules.member.entity.dos.MemberNoticeSenter; + +/** + * 会员消息数据处理层 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +public interface MemberNoticeSenterMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberPointsHistoryMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberPointsHistoryMapper.java new file mode 100644 index 00000000..ed90642b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberPointsHistoryMapper.java @@ -0,0 +1,22 @@ +package cn.lili.modules.member.mapper; + +import cn.lili.modules.member.entity.dos.MemberPointsHistory; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; + +/** + * 会员积分历史数据处理层 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +public interface MemberPointsHistoryMapper extends BaseMapper { + + @Select("SELECT SUM( variable_point ) FROM li_member_points_history WHERE point_type = #{pointType}") + Long getALLMemberPointsHistoryVO(Integer pointType); + + @Select("SELECT SUM( variable_point ) FROM li_member_points_history WHERE point_type = #{pointType} AND member_id=#{memberId}") + Long getMemberPointsHistoryVO(Integer pointType, String memberId); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberReceiptMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberReceiptMapper.java new file mode 100644 index 00000000..c197f018 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberReceiptMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.member.mapper; + +import cn.lili.modules.member.entity.dos.MemberReceipt; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + + +/** + * 会员发票数据层 + * + * @author Chopper + * @date 2021-03-29 14:10:16 + */ +public interface MemberReceiptMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberSignMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberSignMapper.java new file mode 100644 index 00000000..e9e24fc3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberSignMapper.java @@ -0,0 +1,31 @@ +package cn.lili.modules.member.mapper; + + +import cn.lili.modules.member.entity.dos.MemberSign; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 会员签到数据处理层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface MemberSignMapper extends BaseMapper { + + @Select("SELECT * FROM li_member_sign WHERE TO_DAYS( NOW( ) ) - TO_DAYS( create_time) = 1 and member_id = #{memberId}") + List getBeforeMemberSign(String memberId); + + + @Select("select * from li_member_sign ${ew.customSqlSegment}") + List getTodayMemberSign(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Select("SELECT * FROM li_member_sign WHERE DATE_FORMAT(create_time,'%Y%m') = #{time} and member_id = #{memberId}") + List getMonthMemberSign(String memberId, String time); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberWalletMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberWalletMapper.java new file mode 100644 index 00000000..32290f8a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberWalletMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.member.mapper; + + +import cn.lili.modules.member.entity.dos.MemberWallet; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 会员预存款数据处理层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface MemberWalletMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/MemberWithdrawApplyMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/MemberWithdrawApplyMapper.java new file mode 100644 index 00000000..7ebbf646 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/MemberWithdrawApplyMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.member.mapper; + + +import cn.lili.modules.member.entity.dos.MemberWithdrawApply; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 会员提现申请数据处理层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface MemberWithdrawApplyMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/mapper/StoreCollectionMapper.java b/framework/src/main/java/cn/lili/modules/member/mapper/StoreCollectionMapper.java new file mode 100644 index 00000000..c4a8206c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/mapper/StoreCollectionMapper.java @@ -0,0 +1,22 @@ +package cn.lili.modules.member.mapper; + +import cn.lili.modules.member.entity.dos.StoreCollection; +import cn.lili.modules.member.entity.vo.StoreCollectionVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 会员收藏数据处理层 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +public interface StoreCollectionMapper extends BaseMapper { + + @Select("select s.id,s.store_name,s.store_logo,s.self_operated from li_store s INNER JOIN li_store_collection sc ON s.id=sc.store_id ${ew.customSqlSegment} ") + IPage storeCollectionVOList(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java b/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java new file mode 100644 index 00000000..3dd22c24 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java @@ -0,0 +1,55 @@ +package cn.lili.modules.member.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.FootPrint; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 会员浏览历史业务层 + * + * @author Chopper + * @date 2020/11/18 10:46 上午 + */ +public interface FootprintService extends IService { + + /** + * 保存浏览历史 + * + * @param footPrint 用户足迹 + * @return 浏览历史 + */ + FootPrint saveFootprint(FootPrint footPrint); + + /** + * 清空当前会员的足迹 + * + * @return 处理结果 + */ + boolean clean(); + + /** + * 根据ID进行清除会员的历史足迹 + * + * @param ids 商品ID列表 + * @return 处理结果 + */ + boolean deleteByIds(List ids); + + /** + * 获取会员浏览历史分页 + * + * @param pageVO 分页 + * @return 会员浏览历史列表 + */ + List footPrintPage(PageVO pageVO); + + /** + * 获取当前会员的浏览记录数量 + * + * @return 当前会员的浏览记录数量 + */ + Integer getFootprintNum(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/GoodsCollectionService.java b/framework/src/main/java/cn/lili/modules/member/service/GoodsCollectionService.java new file mode 100644 index 00000000..9847f86f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/GoodsCollectionService.java @@ -0,0 +1,64 @@ +package cn.lili.modules.member.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.GoodsCollection; +import cn.lili.modules.member.entity.vo.GoodsCollectionVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 商品收藏业务层 + * + * @author Chopper + * @date 2020/11/18 2:25 下午 + */ +public interface GoodsCollectionService extends IService { + + /** + * 获取商品搜索分页 + * + * @param pageVo 查询参数 + * @return 商品搜索分页 + */ + IPage goodsCollection(PageVO pageVo); + + /** + * 是否收藏商品 + * + * @param skuId 规格ID + * @return 是否收藏 + */ + boolean isCollection(String skuId); + + /** + * 添加商品收藏 + * + * @param skuId 规格ID + * @return 操作状态 + */ + GoodsCollection addGoodsCollection(String skuId); + + /** + * 商品收藏 + * + * @param skuId 规格ID + * @return 操作状态 + */ + boolean deleteGoodsCollection(String skuId); + /** + * 删除商品收藏 + * + * @param goodsIds 规格ID + * @return 操作状态 + */ + boolean deleteGoodsCollection(List goodsIds); + /** + * 删除商品SKU收藏 + * + * @param skuIds 规格ID + * @return 操作状态 + */ + boolean deleteSkuCollection(List skuIds); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberEvaluationService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberEvaluationService.java new file mode 100644 index 00000000..0e1d44f2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberEvaluationService.java @@ -0,0 +1,111 @@ +package cn.lili.modules.member.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import cn.lili.modules.member.entity.dto.EvaluationQueryParams; +import cn.lili.modules.member.entity.dto.MemberEvaluationDTO; +import cn.lili.modules.member.entity.dto.StoreEvaluationQueryParams; +import cn.lili.modules.member.entity.vo.EvaluationNumberVO; +import cn.lili.modules.member.entity.vo.MemberEvaluationListVO; +import cn.lili.modules.member.entity.vo.MemberEvaluationVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 会员商品评价业务层 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +public interface MemberEvaluationService extends IService { + + /** + * 查询会员的评价分页列表 + * + * @param evaluationQueryParams 评价查询 + * @return 评价分页 + */ + IPage queryByParams(EvaluationQueryParams evaluationQueryParams); + + /** + * 商家查询会员的评价分页列表 + * + * @param storeEvaluationQueryParams 评价查询 + * @return 会员的评价分页 + */ + IPage queryByParams(StoreEvaluationQueryParams storeEvaluationQueryParams); + + /** + * 查询评价分页列表 + * @param evaluationQueryParams 评价查询条件 + * @param page 分页查询参数 + * @return 评价分页列表 + */ + IPage queryPage(EvaluationQueryParams evaluationQueryParams, PageVO page); + + /** + * 添加会员评价 + * 1.检测用户是否重复评价 + * 2.获取评价相关信息添加评价 + * 3.修改子订单为已评价状态 + * 4.发送用户评价消息修改商品的评价数量以及好评率 + * + * @param memberEvaluationDTO 评论 + * @return 操作状态 + */ + MemberEvaluationDTO addMemberEvaluation(MemberEvaluationDTO memberEvaluationDTO); + /** + * 根据ID查询会员评价 + * + * @param id 评价ID + * @return 会员评价 + */ + MemberEvaluationVO queryById(String id); + + /** + * 更改评论状态 + * + * @param id 评价ID + * @param status 状态 + * @return 会员评价 + */ + boolean updateStatus(String id, String status); + + /** + * 删除评论 + * + * @param id 评论ID + * @return 操作状态 + */ + boolean delete(String id); + + /** + * 商家回复评价 + * + * @param id 评价ID + * @param reply 回复内容 + * @param replyImage 回复图片 + * + */ + boolean reply(String id, String reply, String replyImage); + + /** + * 获取商品评价数量 + * @param goodsId 商品ID + * @return 评价数量数据 + */ + EvaluationNumberVO getEvaluationNumber(String goodsId); + + /** + * 获取今天新增的评价数量 + * @return 今日评价数量 + */ + Integer todayMemberEvaluation(); + + /** + * 获取等待回复评价数量 + * @return 等待回复评价数量 + */ + Integer getWaitReplyNum(); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberMessageService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberMessageService.java new file mode 100644 index 00000000..b271110f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberMessageService.java @@ -0,0 +1,44 @@ +package cn.lili.modules.member.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.MemberMessage; +import cn.lili.modules.member.entity.vo.MemberMessageQueryVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 消息发送业务层 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +public interface MemberMessageService extends IService { + + /** + * 会员消息查询接口 + * + * @param memberMessageQueryVO 会员查询条件 + * @param pageVO 分页条件 + * @return 会员消息分页 + */ + IPage getPage(MemberMessageQueryVO memberMessageQueryVO, PageVO pageVO); + + /** + * 修改会员消息状态 + * + * @param status 状态 + * @param messageId 消息id + * @return 操作状态 + */ + Boolean editStatus(String status, String messageId); + + /** + * 删除消息 + * + * @param messageId 消息id + * @return 操作状态 + */ + Boolean deleteMessage(String messageId); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberNoticeLogService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberNoticeLogService.java new file mode 100644 index 00000000..89902c0c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberNoticeLogService.java @@ -0,0 +1,14 @@ +package cn.lili.modules.member.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import cn.lili.modules.member.entity.dos.MemberNoticeLog; + +/** + * 会员消息业务层 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +public interface MemberNoticeLogService extends IService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberNoticeSenterService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberNoticeSenterService.java new file mode 100644 index 00000000..e4372c75 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberNoticeSenterService.java @@ -0,0 +1,22 @@ +package cn.lili.modules.member.service; + +import cn.lili.modules.member.entity.dos.MemberNoticeSenter; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 会员消息业务层 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +public interface MemberNoticeSenterService extends IService { + + /** + * 自定义保存方法 + * + * @param memberNoticeSenter 会员消息 + * @return 操作状态 + */ + boolean customSave(MemberNoticeSenter memberNoticeSenter); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberNoticeService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberNoticeService.java new file mode 100644 index 00000000..045e47f2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberNoticeService.java @@ -0,0 +1,15 @@ +package cn.lili.modules.member.service; + +import cn.lili.modules.member.entity.dos.MemberNotice; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 会员站内信业务层 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +public interface MemberNoticeService extends IService { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberPointsHistoryService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberPointsHistoryService.java new file mode 100644 index 00000000..6f9e25a8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberPointsHistoryService.java @@ -0,0 +1,24 @@ +package cn.lili.modules.member.service; + +import cn.lili.modules.member.entity.dos.MemberPointsHistory; +import cn.lili.modules.member.entity.vo.MemberPointsHistoryVO; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 会员积分历史业务层 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +public interface MemberPointsHistoryService extends IService { + + /** + * 获取会员积分VO + * + * @param memberId 会员ID + * @return 会员积分VO + */ + MemberPointsHistoryVO getMemberPointsHistoryVO(String memberId); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberReceiptService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberReceiptService.java new file mode 100644 index 00000000..601178e9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberReceiptService.java @@ -0,0 +1,55 @@ +package cn.lili.modules.member.service; + + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.MemberReceipt; +import cn.lili.modules.member.entity.vo.MemberReceiptAddVO; +import cn.lili.modules.member.entity.vo.MemberReceiptVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + + +/** + * 会员发票业务层 + * + * @author Chopper + * @date 2021-03-29 14:10:16 + */ +public interface MemberReceiptService extends IService { + + /** + * 查询会员发票列表 + * + * @param memberReceiptVO 会员发票信息 + * @param pageVO 分页信息 + * @return 会员发票分页 + */ + IPage getPage(MemberReceiptVO memberReceiptVO, PageVO pageVO); + + /** + * 添加会员发票信息 + * + * @param memberReceiptAddVO 会员发票信息 + * @param memberId 会员ID + * @return 操作状态 + */ + Boolean addMemberReceipt(MemberReceiptAddVO memberReceiptAddVO, String memberId); + + /** + * 修改会员发票信息 + * + * @param memberReceiptAddVO 会员发票信息 + * @param memberId 会员ID + * @return 操作状态 + */ + Boolean editMemberReceipt(MemberReceiptAddVO memberReceiptAddVO, String memberId); + + /** + * 删除会员发票信息 + * + * @param id 发票ID + * @return 操作状态 + */ + Boolean deleteMemberReceipt(String id); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberService.java new file mode 100644 index 00000000..7395dc04 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberService.java @@ -0,0 +1,209 @@ +package cn.lili.modules.member.service; + + +import cn.lili.common.token.Token; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dto.ManagerMemberEditDTO; +import cn.lili.modules.member.entity.dto.MemberAddDTO; +import cn.lili.modules.member.entity.dto.MemberEditDTO; +import cn.lili.modules.member.entity.vo.MemberDistributionVO; +import cn.lili.modules.member.entity.vo.MemberSearchVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 会员业务层 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +public interface MemberService extends IService { + + /** + * 获取当前登录的用户信息 + * + * @return 会员信息 + */ + Member getUserInfo(); + + /** + * 是否可以通过手机获取用户 + * + * @param uuid UUID + * @param mobile 手机号 + * @return 操作状态 + */ + boolean findByMobile(String uuid, String mobile); + + /** + * 通过用户名获取用户 + * + * @param username 用户名 + * @return 会员信息 + */ + Member findByUsername(String username); + + /** + * 登录:用户名、密码登录 + * + * @param username 用户名 + * @param password 密码 + * @return token + */ + Token usernameLogin(String username, String password); + + /** + * 商家登录:用户名、密码登录 + * + * @param username 用户名 + * @param password 密码 + * @return token + */ + Token usernameStoreLogin(String username, String password); + + /** + * 注册:手机号、验证码登录 + * + * @return 是否登录成功 + */ + Token mobilePhoneLogin(String mobilePhone); + + /** + * 修改会员信息 + * + * @param memberEditDTO 会员修改信息 + * @return 修改后的会员 + */ + Member editOwn(MemberEditDTO memberEditDTO); + + /** + * 修改用户密码 + * + * @param oldPassword 旧密码 + * @param newPassword 新密码 + * @return 操作结果 + */ + Member modifyPass(String oldPassword, String newPassword); + + /** + * 注册会员 + * + * @param userName 会员 + * @param password 密码 + * @param mobilePhone mobilePhone + * @return 处理结果 + */ + Token register(String userName, String password, String mobilePhone); + + /** + * 修改当前会员的手机号 + * + * @param mobile 手机号 + * @return 操作结果 + */ + boolean changeMobile(String mobile); + + + /** + * 通过手机号修改密码 + * + * @param mobile 手机号 + * @param password 密码 + * @return + */ + boolean resetByMobile(String mobile, String password); + + /** + * 后台-添加会员 + * + * @param memberAddDTO 会员 + * @return 会员 + */ + Member addMember(MemberAddDTO memberAddDTO); + + /** + * 后台-修改会员 + * + * @param managerMemberEditDTO 后台修改会员参数 + * @return 会员 + */ + Member updateMember(ManagerMemberEditDTO managerMemberEditDTO); + + /** + * 获取会员分页 + * + * @param memberSearchVO 会员搜索VO + * @param page 分页 + * @return 会员分页 + */ + IPage getMemberPage(MemberSearchVO memberSearchVO, PageVO page); + + /** + * 一键注册会员 + * + * @return + */ + Token autoRegister(); + + /** + * 一键注册会员 + * + * @return Token + */ + Token autoRegister(ConnectAuthUser authUser); + + /** + * 刷新token + * + * @param refreshToken + * @return Token + */ + Token refreshToken(String refreshToken); + + /** + * 刷新token + * + * @param refreshToken + * @return Token + */ + Token refreshStoreToken(String refreshToken); + + /** + * 会员积分变动 + * + * @param point 变动积分 + * @param type 变动类型 1为增加 0为消费 + * @param memberId 会员id + * @param content 变动详细 + * @return 操作结果 + */ + Boolean updateMemberPoint(Long point, Integer type, String memberId, String content); + + /** + * 修改会员状态 + * + * @param memberIds 会员id集合 + * @param status 状态 + * @return 修改结果 + */ + Boolean updateMemberStatus(List memberIds, Boolean status); + + /** + * 查看会员数据分布 + * + * @return 会员数据分布 + */ + List distribution(); + + /** + * 根据条件查询会员总数 + * + * @param memberSearchVO + * @return 会员总数 + */ + Integer getMemberNum(MemberSearchVO memberSearchVO); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberSignService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberSignService.java new file mode 100644 index 00000000..268aa9c3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberSignService.java @@ -0,0 +1,41 @@ +package cn.lili.modules.member.service; + +import cn.lili.modules.member.entity.dos.MemberSign; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 会员签到业务层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface MemberSignService extends IService { + + /** + * 会员签到 + * + * @return 是否签到成功 + */ + Boolean memberSign(); + + /** + * 根据时间查询签到数据 + * 主要是根据年月查询,给会员端日历展示数据 + * + * @param time 时间 格式 YYYYmm + * @return 会员签到列表 + */ + List getMonthSignDay(String time); + + /** + * 会员签到赠送积分 + * + * @param memberId 会员id + * @param day 签到天数 + */ + void memberSignSendPoint(String memberId, Integer day); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberWalletService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberWalletService.java new file mode 100644 index 00000000..e669e985 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberWalletService.java @@ -0,0 +1,120 @@ +package cn.lili.modules.member.service; + + +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberWallet; +import cn.lili.modules.member.entity.dos.MemberWithdrawApply; +import cn.lili.modules.member.entity.vo.MemberWalletVO; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 会员预存款业务层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface MemberWalletService extends IService { + + /** + * 查询会员的预存款 + * + * @param memberId 会员id + * @return 会员预存款VO + */ + MemberWalletVO getMemberWallet(String memberId); + + /** + * 增加用户预存款余额 + * + * @param money 金额 + * @param memberId 会员id + * @param serviceType 业务类型 @see DepositServiceTypeEnum + * @param detail 操作描述 + * @return 返回增加结果 true:增加成功 false:增加失败 + */ + Boolean increase(Double money, String memberId, String detail, String serviceType); + + /** + * 从冻结金额到余额 + * + * @param money 金额 + * @param memberId 会员id + * @param serviceType 业务类型 @see DepositServiceTypeEnum + * @param detail 操作描述 + * @return 返回增加结果 true:增加成功 false:增加失败 + */ + Boolean increaseWithdrawal(Double money, String memberId, String detail, String serviceType); + + /** + * 扣减用户预存款余额 + * + * @param money 金额 + * @param memberId 会员id + * @param detail 操作描述 + * @param serviceType 业务类型 @see DepositServiceTypeEnum + * @return 操作状态 + */ + Boolean reduce(Double money, String memberId, String detail, String serviceType); + + /** + * 提现扣减余额到冻结金额 + * + * @param money 金额 + * @param memberId 会员id + * @param detail 操作描述 + * @param serviceType 业务类型 @see DepositServiceTypeEnum + * @return 操作状态 + */ + Boolean reduceWithdrawal(Double money, String memberId, String detail, String serviceType); + + /** + * 提现扣减冻结金额 + * + * @param money 金额 + * @param memberId 会员id + * @param detail 操作描述 + * @return 操作状态 + */ + Boolean reduceFrozen(Double money, String memberId, String detail, String serviceType); + + /** + * 设置支付密码 + * + * @param member 会员id + * @param password 支付密码 + */ + void setMemberWalletPassword(Member member, String password); + + /** + * 检查当前会员是否设置过预存款密码 + * + * @return 操作状态 + */ + Boolean checkPassword(); + + /** + * 会员注册添加会员预存款 + * + * @param memberId 会员id + * @param memberName 会员名称 + * @return 操作结果 + */ + MemberWallet save(String memberId, String memberName); + + /** + * 用户提现 + * + * @param price 提现金额 + * @return 是否提现成功 + */ + Boolean applyWithdrawal(Double price); + + /** + * 提现公共方法,此方法供前端用户提现和后端提现使用 + * + * @param memberWithdrawApply 会员零钱提现申请 + * @return 操作状态 + */ + Boolean withdrawal(MemberWithdrawApply memberWithdrawApply); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/MemberWithdrawApplyService.java b/framework/src/main/java/cn/lili/modules/member/service/MemberWithdrawApplyService.java new file mode 100644 index 00000000..cc4fecf6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/MemberWithdrawApplyService.java @@ -0,0 +1,37 @@ +package cn.lili.modules.member.service; + + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.MemberWithdrawApply; +import cn.lili.modules.member.entity.vo.MemberWithdrawApplyQueryVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 会员提现申请业务层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface MemberWithdrawApplyService extends IService { + + /** + * 平台审核提现申请,申请成功后直接扣款 + * + * @param applyId 审核id + * @param result 审核结构 + * @param remark 备注 + * @return 操作状态 + */ + Boolean audit(String applyId, Boolean result, String remark); + + /** + * 提现记录列表 + * + * @param pageVO 分页条件 + * @param memberWithdrawApplyQueryVO 提现记录查询条件 + * @return 提现记录分页 + */ + IPage getMemberWithdrawPage(PageVO pageVO, MemberWithdrawApplyQueryVO memberWithdrawApplyQueryVO); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/service/StoreCollectionService.java b/framework/src/main/java/cn/lili/modules/member/service/StoreCollectionService.java new file mode 100644 index 00000000..df2d12a4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/service/StoreCollectionService.java @@ -0,0 +1,47 @@ +package cn.lili.modules.member.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.StoreCollection; +import cn.lili.modules.member.entity.vo.StoreCollectionVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 店铺收藏业务层 + * + * @author Chopper + * @date 2020/11/18 2:52 下午 + */ +public interface StoreCollectionService extends IService { + + /** + * 店铺收藏分页 + * @param pageVo 分页VO + * @return 店铺收藏分页列表 + */ + IPage storeCollection(PageVO pageVo); + + /** + * 是否收藏此店铺 + * + * @param storeId 店铺ID + * @return 是否收藏 + */ + boolean isCollection(String storeId); + + /** + * 店铺商品收藏 + * + * @param storeId 店铺ID + * @return 操作状态 + */ + StoreCollection addStoreCollection(String storeId); + + /** + * 店铺收藏 + * + * @param storeId 店铺ID + * @return 操作状态 + */ + boolean deleteStoreCollection(String storeId); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java new file mode 100644 index 00000000..ff7d65b1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java @@ -0,0 +1,101 @@ +package cn.lili.modules.member.serviceimpl; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.FootPrint; +import cn.lili.modules.member.mapper.FootprintMapper; +import cn.lili.modules.member.service.FootprintService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.service.EsGoodsSearchService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * 会员浏览历史业务层实现 + * + * @author Chopper + * @date 2020/11/18 10:46 上午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FootprintServiceImpl extends ServiceImpl implements FootprintService { + + //足迹数据层 + private final FootprintMapper footprintMapper; + //es商品业务层 + private final EsGoodsSearchService esGoodsSearchService; + + @Override + public FootPrint saveFootprint(FootPrint footPrint) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(FootPrint::getMemberId, footPrint.getMemberId()); + queryWrapper.eq(FootPrint::getGoodsId, footPrint.getGoodsId()); + //如果已存在某商品记录,则更新其修改时间 + //如果不存在则添加记录 + List oldPrints = list(queryWrapper); + if (oldPrints != null && !oldPrints.isEmpty()) { + FootPrint oldPrint = oldPrints.get(0); + oldPrint.setSkuId(footPrint.getSkuId()); + this.updateById(oldPrint); + return oldPrint; + } else { + footPrint.setCreateTime(new Date()); + this.save(footPrint); + //删除超过100条后的记录 + footprintMapper.deleteLastFootPrint(footPrint.getMemberId()); + return footPrint; + } + } + + @Override + public boolean clean() { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(FootPrint::getMemberId, UserContext.getCurrentUser().getId()); + return this.remove(lambdaQueryWrapper); + } + + @Override + public boolean deleteByIds(List ids) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(FootPrint::getMemberId, UserContext.getCurrentUser().getId()); + lambdaQueryWrapper.in(FootPrint::getGoodsId, ids); + this.remove(lambdaQueryWrapper); + return true; + } + + @Override + public List footPrintPage(PageVO pageVO) { + + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(FootPrint::getMemberId, UserContext.getCurrentUser().getId()); + lambdaQueryWrapper.eq(FootPrint::getDeleteFlag,false); + lambdaQueryWrapper.orderByDesc(FootPrint::getUpdateTime); + List skuIdList = footprintMapper.footprintSkuIdList(PageUtil.initPage(pageVO), lambdaQueryWrapper); + if (skuIdList.size() > 0) { + List list = esGoodsSearchService.getEsGoodsBySkuIds(skuIdList); + //去除为空的商品数据 + list.removeIf(Objects::isNull); + return list; + } + return null; + } + + @Override + public Integer getFootprintNum() { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(FootPrint::getMemberId, UserContext.getCurrentUser().getId()); + lambdaQueryWrapper.eq(FootPrint::getDeleteFlag,false); + return this.count(lambdaQueryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/GoodsCollectionServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/GoodsCollectionServiceImpl.java new file mode 100644 index 00000000..06341860 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/GoodsCollectionServiceImpl.java @@ -0,0 +1,103 @@ +package cn.lili.modules.member.serviceimpl; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.GoodsTagsEnum; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.member.entity.dos.GoodsCollection; +import cn.lili.modules.member.entity.vo.GoodsCollectionVO; +import cn.lili.modules.member.mapper.GoodsCollectionMapper; +import cn.lili.modules.member.service.GoodsCollectionService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +/** + * 会员收藏业务层实现 + * + * @author Chopper + * @date 2020/11/18 2:25 下午 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsCollectionServiceImpl extends ServiceImpl implements GoodsCollectionService { + + //商品收藏 + private final GoodsCollectionMapper goodsCollectionMapper; + //rocketMq + private final RocketMQTemplate rocketMQTemplate; + //rocketMq配置 + private final RocketmqCustomProperties rocketmqCustomProperties; + + @Override + public IPage goodsCollection(PageVO pageVo) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("gc.member_id", UserContext.getCurrentUser().getId()); + queryWrapper.groupBy("gc.id"); + queryWrapper.orderByDesc("gc.create_time"); + return goodsCollectionMapper.goodsCollectionVOList(PageUtil.initPage(pageVo), queryWrapper); + } + + @Override + public boolean isCollection(String skuId) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("member_id", UserContext.getCurrentUser().getId()); + queryWrapper.eq(skuId != null, "sku_id", skuId); + return Optional.ofNullable(this.getOne(queryWrapper)).isPresent(); + } + + @Override + public GoodsCollection addGoodsCollection(String skuId) { + GoodsCollection goodsCollection = this.getOne(new LambdaUpdateWrapper() + .eq(GoodsCollection::getMemberId, UserContext.getCurrentUser().getId()) + .eq(GoodsCollection::getSkuId, skuId)); + if (goodsCollection == null) { + goodsCollection = new GoodsCollection(UserContext.getCurrentUser().getId(), skuId); + + this.save(goodsCollection); + //商品收藏消息 + String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.GOODS_COLLECTION.name(); + //发送mq消息 + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(skuId), RocketmqSendCallbackBuilder.commonCallback()); + return goodsCollection; + } + throw new ServiceException(ResultCode.USER_COLLECTION_EXIST); + } + + @Override + public boolean deleteGoodsCollection(String skuId) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("member_id", UserContext.getCurrentUser().getId()); + queryWrapper.eq(skuId != null, "sku_id", skuId); + return this.remove(queryWrapper); + } + + @Override + public boolean deleteGoodsCollection(List goodsIds) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.in("sku_id", goodsIds); + return this.remove(queryWrapper); + } + + @Override + public boolean deleteSkuCollection(List skuIds) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.in("sku_id", skuIds); + return this.remove(queryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberEvaluationServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberEvaluationServiceImpl.java new file mode 100644 index 00000000..8ef5c7cd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberEvaluationServiceImpl.java @@ -0,0 +1,217 @@ +package cn.lili.modules.member.serviceimpl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.GoodsTagsEnum; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberEvaluation; +import cn.lili.modules.member.entity.dto.EvaluationQueryParams; +import cn.lili.modules.member.entity.dto.MemberEvaluationDTO; +import cn.lili.modules.member.entity.dto.StoreEvaluationQueryParams; +import cn.lili.modules.member.entity.enums.EvaluationGradeEnum; +import cn.lili.modules.member.entity.vo.EvaluationNumberVO; +import cn.lili.modules.member.entity.vo.MemberEvaluationListVO; +import cn.lili.modules.member.entity.vo.MemberEvaluationVO; +import cn.lili.modules.member.mapper.MemberEvaluationMapper; +import cn.lili.modules.member.service.MemberEvaluationService; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.enums.CommentStatusEnum; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.system.utils.CharacterConstant; +import cn.lili.modules.system.utils.SensitiveWordsFilter; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 会员商品评价业务层实现 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberEvaluationServiceImpl extends ServiceImpl implements MemberEvaluationService { + + //会员评价数据层 + private final MemberEvaluationMapper memberEvaluationMapper; + //订单 + private final OrderService orderService; + //子订单 + private final OrderItemService orderItemService; + //会员 + private final MemberService memberService; + //商品 + private final GoodsSkuService goodsSkuService; + //rocketMq + private final RocketMQTemplate rocketMQTemplate; + //rocketMq配置 + private final RocketmqCustomProperties rocketmqCustomProperties; + + @Override + public IPage queryByParams(EvaluationQueryParams queryParams) { + //获取评价分页 + return this.page(PageUtil.initPage(queryParams), queryParams.queryWrapper()); + } + + @Override + public IPage queryByParams(StoreEvaluationQueryParams storeEvaluationQueryParams) { + return memberEvaluationMapper.getMemberEvaluationList(PageUtil.initPage(storeEvaluationQueryParams), storeEvaluationQueryParams.queryWrapper()); + } + + @Override + public IPage queryPage(EvaluationQueryParams evaluationQueryParams, PageVO page) { + return memberEvaluationMapper.getMemberEvaluationList(PageUtil.initPage(page), evaluationQueryParams.queryWrapper()); + } + + @Override + public MemberEvaluationDTO addMemberEvaluation(MemberEvaluationDTO memberEvaluationDTO) { + //获取子订单信息 + OrderItem orderItem = orderItemService.getBySn(memberEvaluationDTO.getOrderItemSn()); + //获取订单信息 + Order order = orderService.getBySn(orderItem.getOrderSn()); + //检测是否可以添加会员评价 + checkMemberEvaluation(orderItem,order); + //获取用户信息 + Member member = memberService.getUserInfo(); + //获取商品信息 + GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(memberEvaluationDTO.getSkuId()); + //新增用户评价 + MemberEvaluation memberEvaluation = new MemberEvaluation(memberEvaluationDTO, goodsSku, member, order); + //过滤商品咨询敏感词 + memberEvaluation.setContent(SensitiveWordsFilter.filter(memberEvaluation.getContent(), CharacterConstant.WILDCARD_STAR)); + //添加评价 + this.save(memberEvaluation); + + //修改订单货物评价状态为已评价 + orderItemService.updateCommentStatus(orderItem.getSn(), CommentStatusEnum.FINISHED); + //发送商品评价消息 + String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.GOODS_COMMENT_COMPLETE.name(); + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(memberEvaluation), RocketmqSendCallbackBuilder.commonCallback()); + return memberEvaluationDTO; + } + + @Override + public MemberEvaluationVO queryById(String id) { + return new MemberEvaluationVO(this.getById(id)); + } + + @Override + public boolean updateStatus(String id, String status) { + UpdateWrapper updateWrapper = Wrappers.update(); + updateWrapper.eq("id", id); + updateWrapper.set("status", status.equals(SwitchEnum.OPEN.name()) ? SwitchEnum.OPEN.name() : SwitchEnum.CLOSE.name()); + return this.update(updateWrapper); + } + + @Override + public boolean delete(String id) { + LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); + updateWrapper.set(MemberEvaluation::getDeleteFlag, true); + updateWrapper.eq(MemberEvaluation::getId, id); + return this.update(updateWrapper); + } + + @Override + public boolean reply(String id, String reply, String replyImage) { + UpdateWrapper updateWrapper = Wrappers.update(); + updateWrapper.set("reply_status", true); + updateWrapper.set("reply", reply); + if (StringUtils.isNotEmpty(replyImage)) { + updateWrapper.set("have_reply_image", true); + updateWrapper.set("reply_image", replyImage); + } + updateWrapper.eq("id", id); + return this.update(updateWrapper); + } + + @Override + public EvaluationNumberVO getEvaluationNumber(String goodsId) { + EvaluationNumberVO evaluationNumberVO = new EvaluationNumberVO(); + List> list = memberEvaluationMapper.getEvaluationNumber(goodsId); + + Integer good = 0; + Integer moderate = 0; + Integer worse = 0; + for (Map map : list) { + if (map.get("grade").equals(EvaluationGradeEnum.GOOD.name())) { + good = Integer.valueOf(map.get("num").toString()); + } else if (map.get("grade").equals(EvaluationGradeEnum.MODERATE.name())) { + moderate = Integer.valueOf(map.get("num").toString()); + } else if (map.get("grade").equals(EvaluationGradeEnum.WORSE.name())) { + worse = Integer.valueOf(map.get("num").toString()); + } + } + evaluationNumberVO.setAll(good + moderate + worse); + evaluationNumberVO.setGood(good); + evaluationNumberVO.setModerate(moderate); + evaluationNumberVO.setWorse(worse); + evaluationNumberVO.setHaveImage(this.count(new QueryWrapper() + .eq("have_image", 1) + .eq("goods_id", goodsId))); + + return evaluationNumberVO; + } + + @Override + public Integer todayMemberEvaluation() { + return this.count(new LambdaQueryWrapper().gt(MemberEvaluation::getCreateTime, DateUtil.beginOfDay(new DateTime()))); + } + + @Override + public Integer getWaitReplyNum() { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq(StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), + "store_id", UserContext.getCurrentUser().getStoreId()); + queryWrapper.eq("reply_status", false); + return this.count(queryWrapper); + } + + /** + * 检测会员评价 + * @param orderItem 子订单 + * @param order 订单 + */ + public void checkMemberEvaluation(OrderItem orderItem,Order order){ + + //根据子订单编号判断是否评价过 + if (orderItem.getCommentStatus().equals(CommentStatusEnum.FINISHED.name())) { + throw new ServiceException(ResultCode.EVALUATION_DOUBLE_ERROR); + } + + //判断是否是当前会员的订单 + if (!order.getMemberId().equals(UserContext.getCurrentUser().getId())) { + throw new ServiceException(ResultCode.ORDER_NOT_USER); + } + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberMessageServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberMessageServiceImpl.java new file mode 100644 index 00000000..db131c08 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberMessageServiceImpl.java @@ -0,0 +1,64 @@ +package cn.lili.modules.member.serviceimpl; + + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.MemberMessage; +import cn.lili.modules.member.entity.vo.MemberMessageQueryVO; +import cn.lili.modules.member.mapper.MemberMessageMapper; +import cn.lili.modules.member.service.MemberMessageService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 会员消息业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:48 下午 + */ +@Service +@Transactional +public class MemberMessageServiceImpl extends ServiceImpl implements MemberMessageService { + + + @Override + public IPage getPage(MemberMessageQueryVO memberMessageQueryVO, PageVO pageVO) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + //消息标题 + queryWrapper.like(StringUtils.isNotEmpty(memberMessageQueryVO.getTitle()), "title", memberMessageQueryVO.getTitle()); + //会员id + queryWrapper.like(StringUtils.isNotEmpty(memberMessageQueryVO.getMemberId()), "member_id", memberMessageQueryVO.getMemberId()); + //消息状态 + queryWrapper.like(StringUtils.isNotEmpty(memberMessageQueryVO.getStatus()), "status", memberMessageQueryVO.getStatus()); + //构建查询 + return this.page(PageUtil.initPage(pageVO), queryWrapper); + } + + @Override + public Boolean editStatus(String status, String messageId) { + //查询消息是否存在 + MemberMessage memberMessage = this.getById(messageId); + if (memberMessage != null) { + memberMessage.setStatus(status); + //执行修改 + return this.updateById(memberMessage); + } + return false; + } + + + @Override + public Boolean deleteMessage(String messageId) { + //查询消息是否存在 + MemberMessage memberMessage = this.getById(messageId); + if (memberMessage != null) { + //执行删除 + return this.removeById(memberMessage); + } + return false; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberNoticeLogServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberNoticeLogServiceImpl.java new file mode 100644 index 00000000..ad0d24a0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberNoticeLogServiceImpl.java @@ -0,0 +1,17 @@ +package cn.lili.modules.member.serviceimpl; + +import cn.lili.modules.member.entity.dos.MemberNoticeLog; +import cn.lili.modules.member.mapper.MemberNoticeLogMapper; +import cn.lili.modules.member.service.MemberNoticeLogService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + * 会员消息业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +@Service +public class MemberNoticeLogServiceImpl extends ServiceImpl implements MemberNoticeLogService { +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberNoticeSenterServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberNoticeSenterServiceImpl.java new file mode 100644 index 00000000..aa5f3e8b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberNoticeSenterServiceImpl.java @@ -0,0 +1,86 @@ +package cn.lili.modules.member.serviceimpl; + +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.member.entity.dos.MemberNoticeSenter; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberNotice; +import cn.lili.modules.member.entity.enums.SendTypeEnum; +import cn.lili.modules.member.mapper.MemberNoticeSenterMapper; +import cn.lili.modules.member.service.MemberNoticeSenterService; +import cn.lili.modules.member.service.MemberNoticeService; +import cn.lili.modules.member.service.MemberService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 会员消息业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +@Service +@Transactional(propagation = Propagation.REQUIRED, rollbackFor = java.lang.Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberNoticeSenterServiceImpl extends ServiceImpl implements MemberNoticeSenterService { + + //会员 + private final MemberService memberService; + //会员站内信 + private final MemberNoticeService memberNoticeService; + + @Override + public boolean customSave(MemberNoticeSenter memberNoticeSenter) { + + if (this.saveOrUpdate(memberNoticeSenter)) { + List memberNotices = new ArrayList<>(); + //如果是选中会员发送 + if (memberNoticeSenter.getSendType().equals(SendTypeEnum.SELECT.name())) { + //判定消息是否有效 + if (!StringUtils.isEmpty(memberNoticeSenter.getMemberIds())) { + String[] ids = memberNoticeSenter.getMemberIds().split(","); + MemberNotice memberNotice; + for (String id : ids) { + memberNotice = new MemberNotice(); + memberNotice.setIsRead(false); + memberNotice.setContent(memberNoticeSenter.getContent()); + memberNotice.setMemberId(id); + memberNotice.setTitle(memberNoticeSenter.getTitle()); + memberNotices.add(memberNotice); + } + } else { + return true; + } + } // 否则是全部会员发送 + else { + List members = memberService.list(); + MemberNotice memberNotice; + for (Member member : members) { + memberNotice = new MemberNotice(); + memberNotice.setIsRead(false); + memberNotice.setContent(memberNoticeSenter.getContent()); + memberNotice.setMemberId(member.getId()); + memberNotice.setTitle(memberNoticeSenter.getTitle()); + memberNotices.add(memberNotice); + } + } + //防止没有会员导致报错 + if (memberNotices.size() > 0) { + //批量保存 + if (memberNoticeService.saveBatch(memberNotices)) { + return true; + } else { + throw new ServiceException("发送站内信异常,请检查系统日志"); + } + } + } + return true; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberNoticeServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberNoticeServiceImpl.java new file mode 100644 index 00000000..1444b14e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberNoticeServiceImpl.java @@ -0,0 +1,18 @@ +package cn.lili.modules.member.serviceimpl; + +import cn.lili.modules.member.entity.dos.MemberNotice; +import cn.lili.modules.member.mapper.MemberNoticeMapper; +import cn.lili.modules.member.service.MemberNoticeService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + * 会员站内信业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +@Service +public class MemberNoticeServiceImpl extends ServiceImpl implements MemberNoticeService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberPointsHistoryServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberPointsHistoryServiceImpl.java new file mode 100644 index 00000000..de21a05c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberPointsHistoryServiceImpl.java @@ -0,0 +1,47 @@ +package cn.lili.modules.member.serviceimpl; + + +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.member.entity.dos.MemberPointsHistory; +import cn.lili.modules.member.entity.vo.MemberPointsHistoryVO; +import cn.lili.modules.member.mapper.MemberPointsHistoryMapper; +import cn.lili.modules.member.service.MemberPointsHistoryService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 会员积分历史业务层实现 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberPointsHistoryServiceImpl extends ServiceImpl implements MemberPointsHistoryService { + + private final MemberPointsHistoryMapper memberPointsHistoryMapper; + + @Override + public MemberPointsHistoryVO getMemberPointsHistoryVO(String memberId) { + MemberPointsHistoryVO memberPointsHistoryVO = new MemberPointsHistoryVO(); + Long point = 0L; + Long variablePoint = 0L; + + if (StringUtils.isNotEmpty(memberId)) { + point = memberPointsHistoryMapper.getMemberPointsHistoryVO(1, memberId); + variablePoint = memberPointsHistoryMapper.getMemberPointsHistoryVO(0, memberId); + + } else { + point = memberPointsHistoryMapper.getALLMemberPointsHistoryVO(0); + variablePoint = memberPointsHistoryMapper.getALLMemberPointsHistoryVO(1); + } + memberPointsHistoryVO.setPoint(point == null ? 0 : point); + memberPointsHistoryVO.setVariablePoint(variablePoint == null ? 0 : variablePoint); + memberPointsHistoryVO.setVariablePoint(memberPointsHistoryVO.getPoint() - memberPointsHistoryVO.getVariablePoint()); + return memberPointsHistoryVO; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberReceiptServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberReceiptServiceImpl.java new file mode 100644 index 00000000..0a172585 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberReceiptServiceImpl.java @@ -0,0 +1,123 @@ +package cn.lili.modules.member.serviceimpl; + + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberReceipt; +import cn.lili.modules.member.entity.vo.MemberReceiptAddVO; +import cn.lili.modules.member.entity.vo.MemberReceiptVO; +import cn.lili.modules.member.mapper.MemberReceiptMapper; +import cn.lili.modules.member.service.MemberReceiptService; +import cn.lili.modules.member.service.MemberService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +/** + * 会员发票业务层实现 + * + * @author Chopper + * @date 2021-03-29 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberReceiptServiceImpl extends ServiceImpl implements MemberReceiptService { + + private final MemberService memberService; + + @Override + public IPage getPage(MemberReceiptVO memberReceiptVO, PageVO pageVO) { + return this.page(PageUtil.initPage(pageVO), memberReceiptVO.lambdaQueryWrapper()); + } + + @Override + public Boolean addMemberReceipt(MemberReceiptAddVO memberReceiptAddVO, String memberId) { + //校验发票抬头是否重复 + List receipts = this.baseMapper.selectList(new QueryWrapper() + .eq("member_id", memberId) + .eq("receipt_title", memberReceiptAddVO.getReceiptTitle()) + ); + if (receipts.size() > 0) { + throw new ServiceException(ResultCode.USER_RECEIPT_REPEAT_ERROR); + } + //参数封装 + MemberReceipt memberReceipt = new MemberReceipt(); + BeanUtil.copyProperties(memberReceiptAddVO, memberReceipt); + //根据会员信息查询会员 + Member member = memberService.getById(memberId); + if (member != null) { + memberReceipt.setMemberId(memberId); + memberReceipt.setMemberName(member.getUsername()); + //设置发票默认 + List list = this.baseMapper.selectList(new QueryWrapper().eq("member_id", memberId)); + //如果当前会员只有一个发票则默认为默认发票,反之需要校验参数默认值,做一些处理 + if (list.size() <= 0) { + memberReceipt.setIsDefault(1); + } else { + if (memberReceiptAddVO.getIsDefault().equals(1)) { + //如果参数传递新添加的发票信息为默认,则需要把其他发票置为非默认 + this.update(new UpdateWrapper().eq("member_id", memberId)); + //设置当前发票信息为默认 + memberReceipt.setIsDefault(memberReceiptAddVO.getIsDefault()); + } else { + memberReceiptAddVO.setIsDefault(0); + } + } + return this.baseMapper.insert(memberReceipt) > 0 ? true : false; + } + throw new ServiceException(ResultCode.USER_RECEIPT_NOT_EXIST); + } + + @Override + public Boolean editMemberReceipt(MemberReceiptAddVO memberReceiptAddVO, String memberId) { + //根据会员id查询发票信息 + MemberReceipt memberReceiptDb = this.baseMapper.selectById(memberReceiptAddVO.getId()); + if (memberReceiptDb != null) { + //检验是否有权限修改 + if (!memberReceiptDb.getMemberId().equals(memberId)) { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + //校验发票抬头是否重复 + List receipts = this.baseMapper.selectList(new QueryWrapper() + .eq("member_id", memberId) + .eq("receipt_title", memberReceiptAddVO.getReceiptTitle()) + .ne("id", memberReceiptAddVO.getId()) + ); + if (receipts.size() > 0) { + throw new ServiceException(ResultCode.USER_RECEIPT_REPEAT_ERROR); + } + BeanUtil.copyProperties(memberReceiptAddVO, memberReceiptDb); + //对发票默认进行处理 如果参数传递新添加的发票信息为默认,则需要把其他发票置为非默认 + if (memberReceiptAddVO.getIsDefault().equals(1)) { + this.update(new UpdateWrapper().eq("member_id", memberId)); + } + return this.baseMapper.updateById(memberReceiptDb) > 0 ? true : false; + } + throw new ServiceException(ResultCode.USER_RECEIPT_NOT_EXIST); + } + + @Override + public Boolean deleteMemberReceipt(String id) { + //根据会员id查询发票信息 + MemberReceipt memberReceiptDb = this.baseMapper.selectById(id); + if (memberReceiptDb != null) { + //如果会员发票信息不为空 则逻辑删除此发票信息 + memberReceiptDb.setDeleteFlag(false); + this.baseMapper.updateById(memberReceiptDb); + } + return true; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java new file mode 100644 index 00000000..11f42aa3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberServiceImpl.java @@ -0,0 +1,568 @@ +package cn.lili.modules.member.serviceimpl; + + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.MemberTagsEnum; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.token.Token; +import cn.lili.common.token.base.generate.MemberTokenGenerate; +import cn.lili.common.token.base.generate.StoreTokenGenerate; +import cn.lili.common.utils.*; +import cn.lili.common.vo.PageVO; +import cn.lili.config.context.ThreadContextHolder; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.connect.config.ConnectAuthEnum; +import cn.lili.modules.connect.entity.Connect; +import cn.lili.modules.connect.entity.dto.ConnectAuthUser; +import cn.lili.modules.connect.service.ConnectService; +import cn.lili.modules.connect.util.UuidUtils; +import cn.lili.modules.member.entity.aop.annotation.PointLogPoint; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dto.ManagerMemberEditDTO; +import cn.lili.modules.member.entity.dto.MemberAddDTO; +import cn.lili.modules.member.entity.dto.MemberEditDTO; +import cn.lili.modules.member.entity.dto.MemberPointMessage; +import cn.lili.modules.member.entity.vo.MemberDistributionVO; +import cn.lili.modules.member.entity.vo.MemberSearchVO; +import cn.lili.modules.member.mapper.MemberMapper; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.entity.enums.StoreStatusEnum; +import cn.lili.modules.store.service.StoreService; +import cn.lili.modules.system.utils.CharacterConstant; +import cn.lili.modules.system.utils.SensitiveWordsFilter; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 会员接口业务层实现 + * + * @author Chopper + * @date 2021-03-29 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberServiceImpl extends ServiceImpl implements MemberService { + + //会员数据处理层 + private final MemberMapper memberMapper; + //会员token + private MemberTokenGenerate memberTokenGenerate; + //商家token + private StoreTokenGenerate storeTokenGenerate; + //联合登录 + private ConnectService connectService; + @Autowired + private StoreService storeService; + //RocketMQ 配置 + private final RocketmqCustomProperties rocketmqCustomProperties; + //RocketMQ + private final RocketMQTemplate rocketMQTemplate; + //缓存 + private final Cache cache; + + @Override + public Member findByUsername(String userName) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("username", userName); + return memberMapper.selectOne(queryWrapper); + } + + + @Override + public Member getUserInfo() { + AuthUser tokenUser = UserContext.getCurrentUser(); + if (tokenUser != null) { + return this.findByUsername(tokenUser.getUsername()); + } + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + + @Override + public boolean findByMobile(String uuid, String mobile) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("mobile", mobile); + Member member = memberMapper.selectOne(queryWrapper); + if (member == null) { + throw new ServiceException(ResultCode.USER_NOT_PHONE); + } + cache.put(CachePrefix.FIND_MOBILE + uuid, mobile, 300L); + + return true; + } + + @Override + public Token usernameLogin(String username, String password) { + Member member = this.findByUsername(username); + //判断用户是否存在 + if (member == null || !member.getDisabled()) { + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + //判断密码是否输入正确 + if (!new BCryptPasswordEncoder().matches(password, member.getPassword())) { + throw new ServiceException(ResultCode.USER_PASSWORD_ERROR); + } + loginBindUser(member); + return memberTokenGenerate.createToken(member.getUsername(), false); + } + + @Override + public Token usernameStoreLogin(String username, String password) { + + Member member = this.findByUsername(username); + //判断用户是否存在 + if (member == null || !member.getDisabled()) { + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + //判断密码是否输入正确 + if (!new BCryptPasswordEncoder().matches(password, member.getPassword())) { + throw new ServiceException(ResultCode.USER_PASSWORD_ERROR); + } + //对店铺状态的判定处理 + if (member.getHaveStore()) { + Store store = storeService.getById(member.getStoreId()); + if (!store.getStoreDisable().equals(StoreStatusEnum.OPEN.name())) { + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + } else { + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + + return storeTokenGenerate.createToken(member.getUsername(), false); + } + + @Override + public Token autoRegister(ConnectAuthUser authUser) { + + if (StringUtils.isEmpty(authUser.getNickname())) { + authUser.setNickname("临时昵称"); + } + if (StringUtils.isEmpty(authUser.getAvatar())) { + authUser.setAvatar("https://i.loli.net/2020/11/19/LyN6JF7zZRskdIe.png"); + } + try { + String username = UuidUtils.getUUID(); + Member member = new Member(username, UuidUtils.getUUID(), authUser.getAvatar(), authUser.getNickname(), + authUser.getGender() != null ? Integer.parseInt(authUser.getGender().getCode()) : 0); + //保存会员 + this.save(member); + Member loadMember = this.findByUsername(username); + //绑定登录方式 + loginBindUser(loadMember, authUser.getUuid(), authUser.getSource()); + return memberTokenGenerate.createToken(username, false); + } catch (ServiceException e) { + log.error("自动注册服务泡出异常:", e); + throw e; + } catch (Exception e) { + log.error("自动注册异常:", e); + throw new ServiceException("自动注册失败,请稍后重试"); + } + } + + @Override + public Token autoRegister() { + ConnectAuthUser connectAuthUser = this.checkConnectUser(); + return this.autoRegister(connectAuthUser); + } + + @Override + public Token refreshToken(String refreshToken) { + return memberTokenGenerate.refreshToken(refreshToken); + } + + @Override + public Token refreshStoreToken(String refreshToken) { + return storeTokenGenerate.refreshToken(refreshToken); + } + + @Override + public Token mobilePhoneLogin(String mobilePhone) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("mobile", mobilePhone); + Member member = memberMapper.selectOne(queryWrapper); + //如果手机号不存在则自动注册用户 + if (member == null) { + member = new Member(mobilePhone, UuidUtils.getUUID(), mobilePhone); + //保存会员 + this.save(member); + String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_REGISTER.name(); + rocketMQTemplate.asyncSend(destination, member, RocketmqSendCallbackBuilder.commonCallback()); + } + loginBindUser(member); + return memberTokenGenerate.createToken(member.getUsername(), false); + } + + @Override + public Member editOwn(MemberEditDTO memberEditDTO) { + //查询会员信息 + Member member = this.findByUsername(UserContext.getCurrentUser().getUsername()); + //传递修改会员信息 + BeanUtil.copyProperties(memberEditDTO, member); + //修改会员 + this.updateById(member); + return member; + } + + @Override + public Member modifyPass(String oldPassword, String newPassword) { + AuthUser tokenUser = UserContext.getCurrentUser(); + if (tokenUser == null) { + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + Member member = this.getById(tokenUser.getId()); + //判断旧密码输入是否正确 + if (!new BCryptPasswordEncoder().matches(oldPassword, member.getPassword())) { + throw new ServiceException(ResultCode.USER_OLD_PASSWORD_ERROR); + } + //修改会员密码 + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.eq(Member::getId, member.getId()); + lambdaUpdateWrapper.set(Member::getPassword, new BCryptPasswordEncoder().encode(newPassword)); + this.update(lambdaUpdateWrapper); + return member; + } + + @Override + public Token register(String userName, String password, String mobilePhone) { + //检测会员信息 + checkMember(userName, mobilePhone); + //设置会员信息 + Member member = new Member(userName, new BCryptPasswordEncoder().encode(password), mobilePhone); + //注册成功后用户自动登录 + if (this.save(member)) { + Token token = memberTokenGenerate.createToken(member.getUsername(), false); + String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_REGISTER.name(); + rocketMQTemplate.asyncSend(destination, member, RocketmqSendCallbackBuilder.commonCallback()); + return token; + } + return null; + } + + @Override + public boolean changeMobile(String mobile) { + AuthUser tokenUser = UserContext.getCurrentUser(); + Member member = this.findByUsername(tokenUser.getUsername()); + + //判断是否用户登录并且会员ID为当前登录会员ID + if (tokenUser == null || tokenUser.getId() != member.getId()) { + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + //修改会员手机号 + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.eq(Member::getId, member.getId()); + lambdaUpdateWrapper.set(Member::getMobile, mobile); + return this.update(lambdaUpdateWrapper); + } + + @Override + public boolean resetByMobile(String uuid, String password) { + String phone = cache.get(CachePrefix.FIND_MOBILE + uuid).toString(); + //根据手机号获取会员判定是否存在此会员 + if (phone != null) { + //修改密码 + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.eq(Member::getMobile, phone); + lambdaUpdateWrapper.set(Member::getPassword, new BCryptPasswordEncoder().encode(password)); + return this.update(lambdaUpdateWrapper); + } else { + throw new ServiceException(ResultCode.USER_PHONE_NOT_EXIST); + } + + } + + @Override + public Member addMember(MemberAddDTO memberAddDTO) { + + //检测会员信息 + checkMember(memberAddDTO.getUsername(), memberAddDTO.getMobile()); + + //添加会员 + Member member = new Member(memberAddDTO.getUsername(), memberAddDTO.getPassword(), memberAddDTO.getMobile()); + if (this.save(member)) { + String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_REGISTER.name(); + rocketMQTemplate.asyncSend(destination, member, RocketmqSendCallbackBuilder.commonCallback()); + return member; + } + throw new ServiceException(ResultCode.ERROR); + } + + @Override + public Member updateMember(ManagerMemberEditDTO managerMemberEditDTO) { + //判断是否用户登录并且会员ID为当前登录会员ID + AuthUser tokenUser = UserContext.getCurrentUser(); + if (tokenUser == null) { + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + //过滤会员昵称敏感词 + if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotBlank(managerMemberEditDTO.getNickName())) { + managerMemberEditDTO.setNickName(SensitiveWordsFilter.filter(managerMemberEditDTO.getNickName(), CharacterConstant.WILDCARD_STAR)); + } + //如果密码不为空则加密密码 + if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotBlank(managerMemberEditDTO.getPassword())) { + managerMemberEditDTO.setPassword(new BCryptPasswordEncoder().encode(managerMemberEditDTO.getPassword())); + } + //查询会员信息 + Member member = this.findByUsername(managerMemberEditDTO.getUsername()); + //传递修改会员信息 + BeanUtil.copyProperties(managerMemberEditDTO, member); + if (this.updateById(member)) { + return member; + } + throw new ServiceException(ResultCode.ERROR); + } + + @Override + public IPage getMemberPage(MemberSearchVO memberSearchVO, PageVO page) { + QueryWrapper queryWrapper = Wrappers.query(); + //用户名查询 + queryWrapper.like(StringUtils.isNotBlank(memberSearchVO.getUsername()), "username", memberSearchVO.getUsername()); + //用户名查询 + queryWrapper.like(StringUtils.isNotBlank(memberSearchVO.getNickName()), "nick_name", memberSearchVO.getNickName()); + //按照电话号码查询 + queryWrapper.like(StringUtils.isNotBlank(memberSearchVO.getMobile()), "mobile", memberSearchVO.getMobile()); + //按照会员状态查询 + queryWrapper.eq(StringUtils.isNotBlank(memberSearchVO.getDisabled()), "disabled", + memberSearchVO.getDisabled().equals(SwitchEnum.OPEN.name())?1:0); + queryWrapper.orderByDesc("create_time"); + return this.page(PageUtil.initPage(page), queryWrapper); + } + + @Override + @PointLogPoint + public Boolean updateMemberPoint(Long point, Integer type, String memberId, String content) { + //获取当前会员信息 + Member member = this.getById(memberId); + if (member != null) { + //积分变动后的会员积分 + long currentPoint; + if (type == 1) { + currentPoint = CurrencyUtil.add(member.getPoint(), point).longValue(); + } else { + currentPoint = CurrencyUtil.sub(member.getPoint(), point) < 0 ? 0 : new Double(CurrencyUtil.sub(member.getPoint(), point)).longValue(); + } + member.setPoint(currentPoint); + Boolean result = this.updateById(member); + if (result) { + //发送会员消息 + MemberPointMessage memberPointMessage = new MemberPointMessage(); + memberPointMessage.setPoint(point); + memberPointMessage.setType(type); + memberPointMessage.setMemberId(memberId); + String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_POINT_CHANGE.name(); + rocketMQTemplate.asyncSend(destination, memberPointMessage, RocketmqSendCallbackBuilder.commonCallback()); + return true; + } + return false; + + } + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + + + @Override + public Boolean updateMemberStatus(List memberIds, Boolean status) { + UpdateWrapper updateWrapper = Wrappers.update(); + updateWrapper.set("disabled", status); + updateWrapper.in("id", memberIds); + + return this.update(updateWrapper); + } + + @Override + public List distribution() { + List memberDistributionVOS = memberMapper.distribution(); + return memberDistributionVOS; + } + + /** + * 根据手机号获取会员 + * + * @param mobilePhone 手机号 + * @return 会员 + */ + private Member findByPhone(String mobilePhone) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("mobile", mobilePhone); + return memberMapper.selectOne(queryWrapper); + } + + /** + * 获取cookie中的联合登录对象 + * + * @param uuid uuid + * @param type 状态 + * @return + */ + private ConnectAuthUser getConnectAuthUser(String uuid, String type) { + Object context = cache.get(ConnectService.cacheKey(type, uuid)); + if (context != null) { + return (ConnectAuthUser) context; + } + return null; + } + + /** + * 成功登录,则检测cookie中的信息,进行会员绑定 + * + * @param member 会员 + * @param unionId unionId + * @param type 状态 + */ + private void loginBindUser(Member member, String unionId, String type) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Connect::getUnionId, unionId); + queryWrapper.eq(Connect::getUnionType, type); + Connect connect = connectService.getOne(queryWrapper); + if (connect == null) { + connect = new Connect(member.getId(), unionId, type); + connectService.save(connect); + } + } + + /** + * 成功登录,则检测cookie中的信息,进行会员绑定 + * + * @param member 会员 + */ + private void loginBindUser(Member member) { + //获取cookie存储的信息 + String uuid = CookieUtil.getCookie(ConnectService.CONNECT_COOKIE, ThreadContextHolder.getHttpRequest()); + String connectType = CookieUtil.getCookie(ConnectService.CONNECT_TYPE, ThreadContextHolder.getHttpRequest()); + //如果联合登陆存储了信息 + if (StringUtils.isNotEmpty(uuid) && StringUtils.isNotEmpty(connectType)) { + try { + //获取信息 + ConnectAuthUser connectAuthUser = getConnectAuthUser(uuid, connectType); + if (connectAuthUser == null) { + return; + } + //检测是否已经绑定过用户 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Connect::getUnionId, connectAuthUser.getUuid()); + queryWrapper.eq(Connect::getUnionType, connectType); + Connect connect = connectService.getOne(queryWrapper); + if (connect == null) { + connect = new Connect(member.getId(), connectAuthUser.getUuid(), connectType); + connectService.save(connect); + } + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + log.error("绑定第三方联合登陆失败:", e); + } finally { + //联合登陆成功与否,都清除掉cookie中的信息 + CookieUtil.delCookie(ConnectService.CONNECT_COOKIE, ThreadContextHolder.getHttpResponse()); + CookieUtil.delCookie(ConnectService.CONNECT_TYPE, ThreadContextHolder.getHttpResponse()); + } + } + + } + + + /** + * 检测是否可以绑定第三方联合登陆 + * 返回null原因 + * 包含原因1:redis中已经没有联合登陆信息 2:已绑定其他账号 + * + * @return 返回对象则代表可以进行绑定第三方会员,返回null则表示联合登陆无法继续 + */ + private ConnectAuthUser checkConnectUser() { + //获取cookie存储的信息 + String uuid = CookieUtil.getCookie(ConnectService.CONNECT_COOKIE, ThreadContextHolder.getHttpRequest()); + String connectType = CookieUtil.getCookie(ConnectService.CONNECT_TYPE, ThreadContextHolder.getHttpRequest()); + + //如果联合登陆存储了信息 + if (StringUtils.isNotEmpty(uuid) && StringUtils.isNotEmpty(connectType)) { + try { + //枚举 联合登陆类型获取 + ConnectAuthEnum authInterface = ConnectAuthEnum.valueOf(connectType); + + ConnectAuthUser connectAuthUser = getConnectAuthUser(uuid, connectType); + if (connectAuthUser == null) { + throw new ServiceException("授权信息已过期,请从新授权/登录"); + } + //检测是否已经绑定过用户 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Connect::getUnionId, connectAuthUser.getUuid()); + queryWrapper.eq(Connect::getUnionType, connectType); + Connect connect = connectService.getOne(queryWrapper); + //没有关联则返回true,表示可以继续绑定 + if (connect == null) { + connectAuthUser.setConnectEnum(authInterface); + return connectAuthUser; + } else { + throw new ServiceException("当前联合登陆方式,已绑定其他账号,需进行解绑操作"); + } + } catch (Exception e) { + throw e; + } + } else { + throw new ServiceException("暂无联合登陆信息,无法实现一键注册功能,请点击第三方登录进行授权"); + } + } + + @Override + public Integer getMemberNum(MemberSearchVO memberSearchVO) { + QueryWrapper queryWrapper = Wrappers.query(); + //用户名查询 + queryWrapper.like(StringUtils.isNotBlank(memberSearchVO.getUsername()), "username", memberSearchVO.getUsername()); + //按照电话号码查询 + queryWrapper.like(StringUtils.isNotBlank(memberSearchVO.getMobile()), "mobile", memberSearchVO.getMobile()); + //按照电话号码查询 + queryWrapper.like(StringUtils.isNotBlank(memberSearchVO.getDisabled()), "disabled", memberSearchVO.getDisabled()); + queryWrapper.orderByDesc("create_time"); + return this.count(queryWrapper); + } + + /** + * 检测会员 + * + * @param userName 会员名称 + * @param mobilePhone 手机号 + */ + private void checkMember(String userName, String mobilePhone) { + //判断用户名是否存在 + if (findByUsername(userName) != null) { + throw new ServiceException(ResultCode.USER_NAME_EXIST); + } + //判断手机号是否存在 + if (findByPhone(mobilePhone) != null) { + throw new ServiceException(ResultCode.USER_PHONE_EXIST); + } + } + + @Autowired + public void setConnectService(ConnectService connectService) { + this.connectService = connectService; + } + + @Autowired + public void setMemberTokenGenerate(MemberTokenGenerate memberTokenGenerate) { + this.memberTokenGenerate = memberTokenGenerate; + } + + @Autowired + public void setStoreTokenGenerate(StoreTokenGenerate storeTokenGenerate) { + this.storeTokenGenerate = storeTokenGenerate; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberSignServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberSignServiceImpl.java new file mode 100644 index 00000000..bbca303c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberSignServiceImpl.java @@ -0,0 +1,143 @@ +package cn.lili.modules.member.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.MemberTagsEnum; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.member.entity.dos.MemberSign; +import cn.lili.modules.member.mapper.MemberSignMapper; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.member.service.MemberSignService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.PointSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.gson.Gson; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 会员签到业务层实现 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberSignServiceImpl extends ServiceImpl implements MemberSignService { + + //会员签到 + private final MemberSignMapper memberSignMapper; + //RocketMQ + private final RocketMQTemplate rocketMQTemplate; + //RocketMQ 配置 + private final RocketmqCustomProperties rocketmqCustomProperties; + //配置 + private final SettingService settingService; + //会员 + private final MemberService memberService; + + + @Override + public Boolean memberSign() { + //获取当前会员信息 + AuthUser authUser = UserContext.getCurrentUser(); + if (authUser != null) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("member_id",authUser.getId()); + queryWrapper.between("create_time",new Date(DateUtil.startOfTodDay()*1000),DateUtil.getCurrentDayEndTime()); + //校验今天是否已经签到 + List todaySigns = memberSignMapper.getTodayMemberSign(queryWrapper); + if (todaySigns.size() > 0) { + throw new ServiceException(ResultCode.MEMBER_SIGN_REPEAT); + } + //当前签到天数的前一天日期 + List signs = memberSignMapper.getBeforeMemberSign(authUser.getId()); + //构建参数 + MemberSign memberSign = new MemberSign(); + memberSign.setMemberId(authUser.getId()); + memberSign.setMemberName(authUser.getUsername()); + //如果size大于0 说明昨天已经签到过,获取昨天的签到数,反之新签到 + if (signs.size() > 0) { + //截止目前为止 签到总天数 不带今天 + Integer signDay = signs.get(0).getSignDay(); + memberSign.setSignDay(CurrencyUtil.add(signDay, 1).intValue()); + } else { + memberSign.setSignDay(1); + } + Integer result = memberSignMapper.insert(memberSign); + //签到成功后发送消息赠送积分 + if (result > 0) { + String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_SING.name(); + rocketMQTemplate.asyncSend(destination, memberSign, RocketmqSendCallbackBuilder.commonCallback()); + return true; + } + return false; + } + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + + @Override + public List getMonthSignDay(String time) { + //获取当前会员 + AuthUser authUser = UserContext.getCurrentUser(); + if (authUser != null) { + return memberSignMapper.getMonthMemberSign(authUser.getId(), time); + } + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + + @Override + public void memberSignSendPoint(String memberId, Integer day) { + try { + //获取签到积分赠送设置 + Setting setting = settingService.get(SettingEnum.POINT_SETTING.name()); + if (setting != null) { + PointSetting pointSetting = new Gson().fromJson(setting.getSettingValue(), PointSetting.class); + String content = ""; + Long point = -1L; + //将对象转换成map 方便签到天数和签到积分数获取,方便扩展 + Map map = StringUtils.objectToMap(pointSetting); + for (int i = 1; i <= 4; i++) { + //连续签到数 + Object dayObj = map.get("signIn" + i); + //连续签到赠送积分 + Object pointObj = map.get("signIn" + i + "Point"); + if (dayObj != null && pointObj != null) { + //如果当前连续赠送天数等于设置连续赠送天数 则记录赠送积分数 + if (dayObj == day) { + point = Long.valueOf(map.get("signIn" + i + "Point").toString()); + content = "会员连续签到" + day + "天,赠送积分" + point + "分"; + } + } + } + //如果他不处于连续赠送阶段,则只赠送签到积分数 + if (point == -1 && pointSetting.getSignIn() != null) { + point = Long.valueOf(pointSetting.getSignIn().toString()); + content = "会员签到第" + day + "天,赠送积分" + point + "分"; + } + //赠送会员积分 + memberService.updateMemberPoint(point, 1, memberId, content); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberWalletServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberWalletServiceImpl.java new file mode 100644 index 00000000..1e461cf1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberWalletServiceImpl.java @@ -0,0 +1,277 @@ +package cn.lili.modules.member.serviceimpl; + + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.SnowFlake; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.MemberWallet; +import cn.lili.modules.member.entity.dos.MemberWithdrawApply; +import cn.lili.modules.member.entity.enums.WithdrawStatusEnum; +import cn.lili.modules.member.entity.vo.MemberWalletVO; +import cn.lili.modules.member.mapper.MemberWalletMapper; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.member.service.MemberWalletService; +import cn.lili.modules.member.service.MemberWithdrawApplyService; +import cn.lili.modules.order.trade.entity.dos.WalletLog; +import cn.lili.modules.order.trade.entity.enums.DepositServiceTypeEnum; +import cn.lili.modules.order.trade.service.WalletLogService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.WithdrawalSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.gson.Gson; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + + +/** + * 会员预存款业务层实现 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberWalletServiceImpl extends ServiceImpl implements MemberWalletService { + + //预存款数据层 + private final MemberWalletMapper walletMapper; + //预存款日志 + private final WalletLogService walletLogService; + //设置 + private final SettingService settingService; + //会员 + private final MemberService memberService; + //会员提现申请 + private MemberWithdrawApplyService memberWithdrawApplyService; + + @Override + public MemberWalletVO getMemberWallet(String memberId) { + //构建查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("member_id", memberId); + //执行查询 + MemberWallet memberWallet = this.walletMapper.selectOne(queryWrapper); + //如果没有钱包,则创建钱包 + if (memberWallet == null) { + memberWallet = this.save(memberId, memberService.getById(memberId).getUsername()); + } + //返回查询数据 + return new MemberWalletVO(memberWallet.getMemberWallet(), memberWallet.getMemberFrozenWallet()); + } + + @Override + public Boolean increaseWithdrawal(Double money, String memberId, String detail, String serviceType) { + //检测会员预存款讯息是否存在,如果不存在则新建 + MemberWallet memberWallet = this.checkMemberWallet(memberId); + //余额变动 + memberWallet.setMemberWallet(CurrencyUtil.add(memberWallet.getMemberWallet(), money)); + memberWallet.setMemberFrozenWallet(CurrencyUtil.sub(memberWallet.getMemberFrozenWallet(), money)); + this.walletMapper.updateById(memberWallet); + //新增预存款日志 + WalletLog walletLog = new WalletLog(memberWallet.getMemberId(), memberWallet.getMemberName(), money, detail, serviceType); + walletLogService.save(walletLog); + return true; + } + + @Override + public Boolean increase(Double money, String memberId, String detail, String serviceType) { + //检测会员预存款讯息是否存在,如果不存在则新建 + MemberWallet memberWallet = this.checkMemberWallet(memberId); + //新增预存款 + memberWallet.setMemberWallet(CurrencyUtil.add(memberWallet.getMemberWallet(), money)); + this.walletMapper.updateById(memberWallet); + //新增预存款日志 + WalletLog walletLog = new WalletLog(memberWallet.getMemberId(), memberWallet.getMemberName(), money, detail, serviceType); + walletLogService.save(walletLog); + return true; + } + + + @Override + public Boolean reduce(Double money, String memberId, String detail, String serviceType) { + //检测会员预存款讯息是否存在,如果不存在则新建 + MemberWallet memberWallet = this.checkMemberWallet(memberId); + //减少预存款,需要校验 如果不够扣减预存款 + if (0 > CurrencyUtil.sub(memberWallet.getMemberWallet(), money)) { + return false; + } + memberWallet.setMemberWallet(CurrencyUtil.sub(memberWallet.getMemberWallet(), money)); + //保存记录 + this.walletMapper.updateById(memberWallet); + //新增预存款日志 + WalletLog walletLog = new WalletLog(memberWallet.getMemberId(), memberWallet.getMemberName(), -money, detail, serviceType); + walletLogService.save(walletLog); + return true; + } + + + @Override + public Boolean reduceWithdrawal(Double money, String memberId, String detail, String serviceType) { + //检测会员预存款讯息是否存在,如果不存在则新建 + MemberWallet memberWallet = this.checkMemberWallet(memberId); + //减少预存款,需要校验 如果不够扣减预存款 + if (0 > CurrencyUtil.sub(memberWallet.getMemberWallet(), money)) { + throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_INSUFFICIENT); + } + memberWallet.setMemberWallet(CurrencyUtil.sub(memberWallet.getMemberWallet(), money)); + memberWallet.setMemberFrozenWallet(CurrencyUtil.add(memberWallet.getMemberFrozenWallet(), money)); + //修改余额 + this.walletMapper.updateById(memberWallet); + //新增预存款日志 + WalletLog walletLog = new WalletLog(memberWallet.getMemberId(), memberWallet.getMemberName(), -money, detail, serviceType); + walletLogService.save(walletLog); + return true; + + } + + @Override + public Boolean reduceFrozen(Double money, String memberId, String detail, String serviceType) { + //检测会员预存款讯息是否存在,如果不存在则新建 + MemberWallet memberWallet = this.checkMemberWallet(memberId); + //校验此金额是否超过冻结金额 + if (0 > CurrencyUtil.sub(memberWallet.getMemberWallet(), money)) { + throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_INSUFFICIENT); + } + memberWallet.setMemberWallet(CurrencyUtil.sub(memberWallet.getMemberWallet(), money)); + this.walletMapper.updateById(memberWallet); + //新增预存款日志 + WalletLog walletLog = new WalletLog(memberWallet.getMemberId(), memberWallet.getMemberName(), -money, "提现金额已冻结,审核通过提现成功", serviceType); + walletLogService.save(walletLog); + return true; + } + + /** + * 检测会员预存款是否存在,如果不存在则新建 + * + * @param memberId 会员id + */ + private MemberWallet checkMemberWallet(String memberId) { + //获取会员预存款信息 + MemberWallet memberWallet = this.walletMapper.selectOne(new QueryWrapper().eq("member_id", memberId)); + //如果会员预存款信息不存在则同步重新建立预存款信息 + if (memberWallet == null) { + Member member = memberService.getById(memberId); + if (member != null) { + memberWallet = this.save(memberId, member.getUsername()); + } else { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + } + return memberWallet; + } + + @Override + public void setMemberWalletPassword(Member member, String password) { + //对密码进行加密 + String pwd = new BCryptPasswordEncoder().encode(password); + //校验会员预存款是否存在 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("member_id", member.getId()); + MemberWallet memberWallet = this.walletMapper.selectOne(queryWrapper); + //如果 预存款信息不为空 执行设置密码 + if (memberWallet != null) { + memberWallet.setWalletPassword(pwd); + this.walletMapper.updateById(memberWallet); + } + } + + + @Override + public Boolean checkPassword() { + //获取当前登录会员 + AuthUser authUser = UserContext.getCurrentUser(); + //构建查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("member_id", authUser.getId()); + MemberWallet wallet = this.walletMapper.selectOne(queryWrapper); + return wallet != null && !StringUtils.isEmpty(wallet.getWalletPassword()); + } + + @Override + public MemberWallet save(String memberId, String memberName) { + MemberWallet memberWallet = new MemberWallet(); + memberWallet.setMemberId(memberId); + memberWallet.setMemberName(memberName); + memberWallet.setMemberWallet(0D); + memberWallet.setMemberFrozenWallet(0D); + this.walletMapper.insert(memberWallet); + return memberWallet; + } + + /** + * 提现方法 + * 1、先执行平台逻辑,平台逻辑成功后扣减第三方余额,顺序问题为了防止第三方提现成功,平台逻辑失败导致第三方零钱已提现,而我们商城余额未扣减 + * 2、如果余额扣减失败 则抛出异常,事务回滚 + * + * @param price 提现金额 + * @return + */ + @Override + public Boolean applyWithdrawal(Double price) { + AuthUser authUser = UserContext.getCurrentUser(); + //构建审核参数 + MemberWithdrawApply memberWithdrawApply = new MemberWithdrawApply(); + memberWithdrawApply.setMemberId(authUser.getId()); + memberWithdrawApply.setMemberName(authUser.getNickName()); + memberWithdrawApply.setApplyMoney(price); + memberWithdrawApply.setApplyStatus(WithdrawStatusEnum.APPLY.name()); + memberWithdrawApply.setSn("W" + SnowFlake.getId()); + //校验该次提现是否需要审核,如果未进行配置 默认是需要审核 + Setting setting = settingService.get(SettingEnum.WITHDRAWAL_SETTING.name()); + if (setting != null) { + //如果不需要审核则审核自动通过 + WithdrawalSetting withdrawalSetting = new Gson().fromJson(setting.getSettingValue(), WithdrawalSetting.class); + if (!withdrawalSetting.getApply()) { + memberWithdrawApply.setApplyStatus(WithdrawStatusEnum.VIA_AUDITING.name()); + memberWithdrawApply.setInspectRemark("系统自动审核通过"); + //校验金额是否满足提现,因为是从余额扣减,所以校验的是余额 + MemberWalletVO memberWalletVO = this.getMemberWallet(memberWithdrawApply.getMemberId()); + if (memberWalletVO.getMemberWallet() < price) { + throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_INSUFFICIENT); + } + //微信零钱提现 + Boolean result = withdrawal(memberWithdrawApply); + if (result) { + this.reduce(price, authUser.getId(), "余额提现成功", DepositServiceTypeEnum.WALLET_WITHDRAWAL.name()); + } + } else { + //扣减余额到冻结金额 + this.reduceWithdrawal(price, authUser.getId(), "提现金额已冻结,审核成功后到账", DepositServiceTypeEnum.WALLET_WITHDRAWAL.name()); + } + } + return memberWithdrawApplyService.save(memberWithdrawApply); + } + + @Override + public Boolean withdrawal(MemberWithdrawApply memberWithdrawApply) { + memberWithdrawApply.setInspectTime(new Date()); + //保存或者修改零钱提现 + this.memberWithdrawApplyService.saveOrUpdate(memberWithdrawApply); + //TODO 调用自动提现接口 + boolean result = true; + //如果微信提现失败 则抛出异常 回滚数据 + if (!result) { + throw new ServiceException(ResultCode.WALLET_ERROR_INSUFFICIENT); + } + return result; + } + + @Autowired + public void setMemberWithdrawApplyService(MemberWithdrawApplyService memberWithdrawApplyService) { + this.memberWithdrawApplyService = memberWithdrawApplyService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberWithdrawApplyServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberWithdrawApplyServiceImpl.java new file mode 100644 index 00000000..7fc4ced0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/MemberWithdrawApplyServiceImpl.java @@ -0,0 +1,103 @@ +package cn.lili.modules.member.serviceimpl; + + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.MemberWithdrawApply; +import cn.lili.modules.member.entity.enums.WithdrawStatusEnum; +import cn.lili.modules.member.entity.vo.MemberWalletVO; +import cn.lili.modules.member.entity.vo.MemberWithdrawApplyQueryVO; +import cn.lili.modules.member.mapper.MemberWithdrawApplyMapper; +import cn.lili.modules.member.service.MemberWalletService; +import cn.lili.modules.member.service.MemberWithdrawApplyService; +import cn.lili.modules.order.trade.entity.enums.DepositServiceTypeEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + + +/** + * 会员提现申请业务层实现 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberWithdrawApplyServiceImpl extends ServiceImpl implements MemberWithdrawApplyService { + //提现申请数据层 + private final MemberWithdrawApplyMapper memberWithdrawApplyMapper; + //会员余额 + private final MemberWalletService memberWalletService; + + @Override + public Boolean audit(String applyId, Boolean result, String remark) { + //查询申请记录 + MemberWithdrawApply memberWithdrawApply = memberWithdrawApplyMapper.selectById(applyId); + memberWithdrawApply.setInspectRemark(remark); + if (memberWithdrawApply != null) { + //如果审核通过 则微信直接提现,反之则记录审核状态 + if (result) { + //校验金额是否满足提现,因为是从冻结金额扣减,所以校验的是冻结金额 + MemberWalletVO memberWalletVO = memberWalletService.getMemberWallet(memberWithdrawApply.getMemberId()); + if (memberWalletVO.getMemberFrozenWallet() < memberWithdrawApply.getApplyMoney()) { + throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_INSUFFICIENT); + } + memberWithdrawApply.setApplyStatus(WithdrawStatusEnum.VIA_AUDITING.name()); + //保存审核记录 + memberWithdrawApplyMapper.updateById(memberWithdrawApply); + //提现,微信提现成功后扣减冻结金额 + Boolean bool = memberWalletService.withdrawal(memberWithdrawApply); + if (bool) { + memberWalletService.reduceFrozen(memberWithdrawApply.getApplyMoney(), memberWithdrawApply.getMemberId(), "审核通过,余额提现", DepositServiceTypeEnum.WALLET_WITHDRAWAL.name()); + return true; + } + } else { + //如果审核拒绝 审核备注必填 + if (StringUtils.isEmpty(remark)) { + throw new ServiceException(ResultCode.WALLET_REMARK_ERROR); + } + memberWithdrawApply.setApplyStatus(WithdrawStatusEnum.FAIL_AUDITING.name()); + memberWithdrawApplyMapper.updateById(memberWithdrawApply); + //需要从冻结金额扣减到余额 + memberWalletService.increaseWithdrawal(memberWithdrawApply.getApplyMoney(), memberWithdrawApply.getMemberId(), "审核拒绝,提现金额解冻到余额", DepositServiceTypeEnum.WALLET_WITHDRAWAL.name()); + return true; + } + } + throw new ServiceException(ResultCode.ERROR); + } + + + @Override + public IPage getMemberWithdrawPage(PageVO pageVO, MemberWithdrawApplyQueryVO memberWithdrawApplyQueryVO) { + //构建查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + //会员名称 + queryWrapper.like(!StringUtils.isEmpty(memberWithdrawApplyQueryVO.getMemberName()), "member_name", memberWithdrawApplyQueryVO.getMemberName()); + //充值订单号 + queryWrapper.eq(!StringUtils.isEmpty(memberWithdrawApplyQueryVO.getSn()), "sn", memberWithdrawApplyQueryVO.getSn()); + //会员id + queryWrapper.eq(!StringUtils.isEmpty(memberWithdrawApplyQueryVO.getMemberId()), "member_id", memberWithdrawApplyQueryVO.getMemberId()); + //已付款的充值订单 + queryWrapper.eq(!StringUtils.isEmpty(memberWithdrawApplyQueryVO.getApplyStatus()), "apply_status", memberWithdrawApplyQueryVO.getApplyStatus()); + //开始时间和结束时间 + if (!StringUtils.isEmpty(memberWithdrawApplyQueryVO.getStartDate()) && !StringUtils.isEmpty(memberWithdrawApplyQueryVO.getEndDate())) { + Date start = cn.hutool.core.date.DateUtil.parse(memberWithdrawApplyQueryVO.getStartDate()); + Date end = cn.hutool.core.date.DateUtil.parse(memberWithdrawApplyQueryVO.getEndDate()); + queryWrapper.between("create_time", start, end); + } + queryWrapper.orderByDesc("create_time"); + //查询返回数据 + return this.memberWithdrawApplyMapper.selectPage(PageUtil.initPage(pageVO), queryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/StoreCollectionServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/StoreCollectionServiceImpl.java new file mode 100644 index 00000000..52859312 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/StoreCollectionServiceImpl.java @@ -0,0 +1,71 @@ +package cn.lili.modules.member.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.StoreCollection; +import cn.lili.modules.member.entity.vo.StoreCollectionVO; +import cn.lili.modules.member.mapper.StoreCollectionMapper; +import cn.lili.modules.member.service.StoreCollectionService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +/** + * 会员店铺收藏业务层实现 + * + * @author Chopper + * @date 2020/11/18 2:52 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreCollectionServiceImpl extends ServiceImpl implements StoreCollectionService { + + private final StoreCollectionMapper storeCollectionMapper; + + @Override + public IPage storeCollection(PageVO pageVo) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("sc.member_id", UserContext.getCurrentUser().getId()); + queryWrapper.orderByDesc("sc.create_time"); + return storeCollectionMapper.storeCollectionVOList(PageUtil.initPage(pageVo), queryWrapper); + } + + @Override + public boolean isCollection(String storeId) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("member_id", UserContext.getCurrentUser().getId()); + queryWrapper.eq("store_id", storeId); + return Optional.ofNullable(this.getOne(queryWrapper)).isPresent(); + } + + @Override + public StoreCollection addStoreCollection(String storeId) { + if (this.getOne(new LambdaUpdateWrapper() + .eq(StoreCollection::getMemberId, UserContext.getCurrentUser().getId()) + .eq(StoreCollection::getStoreId, storeId)) == null) { + StoreCollection storeCollection = new StoreCollection(UserContext.getCurrentUser().getId(), storeId); + this.save(storeCollection); + return storeCollection; + } + throw new ServiceException(ResultCode.USER_COLLECTION_EXIST); + } + + @Override + public boolean deleteStoreCollection(String storeId) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("member_id", UserContext.getCurrentUser().getId()); + queryWrapper.eq("store_id", storeId); + return this.remove(queryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dos/Message.java b/framework/src/main/java/cn/lili/modules/message/entity/dos/Message.java new file mode 100644 index 00000000..5be9f77f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dos/Message.java @@ -0,0 +1,52 @@ +package cn.lili.modules.message.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.message.entity.enums.RangeEnum; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Transient; + +/** + * @author lili + */ + +@Data +@Entity +@Table(name = "li_message") +@TableName("li_message") +@ApiModel(value = "消息") +public class Message extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "标题") + private String title; + + @ApiModelProperty(value = "内容") + private String content; + + /** + * @see RangeEnum + */ + @ApiModelProperty(value = "发送范围") + private String messageRange; + + @ApiModelProperty(value = "发送客户端 商家和会员") + private String messageClient; + + @Transient + @TableField(exist = false) + @ApiModelProperty(value = "发送指定用户id") + private String[] userIds; + + @Transient + @TableField(exist = false) + @ApiModelProperty(value = "发送指定用户名称") + private String[] userNames; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dos/NoticeMessage.java b/framework/src/main/java/cn/lili/modules/message/entity/dos/NoticeMessage.java new file mode 100644 index 00000000..817aff46 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dos/NoticeMessage.java @@ -0,0 +1,48 @@ +package cn.lili.modules.message.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 站内消息模板设置 + * + * @author Bulbasaur + * @version v4.1 + * @date 2020/12/8 9:46 + */ +@Data +@Entity +@Table(name = "li_notice_message") +@TableName("li_notice_message") +@ApiModel(value = "站内消息模板") +public class NoticeMessage extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "站内信节点") + private String noticeNode; + + @ApiModelProperty(value = "站内信标题") + private String noticeTitle; + + @ApiModelProperty(value = "站内信内容") + private String noticeContent; + /** + * @see cn.lili.common.enums.SwitchEnum + */ + @ApiModelProperty(value = "站内信是否开启") + private String noticeStatus; + /** + * @see cn.lili.modules.message.entity.enums.NoticeMessageParameterEnum + */ + @ApiModelProperty(value = "消息变量") + private String variable; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dos/ShortLink.java b/framework/src/main/java/cn/lili/modules/message/entity/dos/ShortLink.java new file mode 100644 index 00000000..3dfbfa3f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dos/ShortLink.java @@ -0,0 +1,28 @@ +package cn.lili.modules.message.entity.dos; + +import cn.lili.base.IdEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * @author Chopper + */ +@Data +@Entity +@Table(name = "li_short_link") +@TableName("li_short_link") +@ApiModel(value = "短链接/暂时只用于小程序二维码业务") +public class ShortLink extends IdEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "原始参数") + private String originalParams; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dos/SmsReach.java b/framework/src/main/java/cn/lili/modules/message/entity/dos/SmsReach.java new file mode 100644 index 00000000..51e02367 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dos/SmsReach.java @@ -0,0 +1,63 @@ +package cn.lili.modules.message.entity.dos; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + + +/** + * 短信任务 + * + * @author Chopper + * @date 2021/1/30 4:13 下午 + */ +@Data +@Entity +@Table(name = "li_sms_reach") +@TableName("li_sms_reach") +@ApiModel(value = "短信任务") +public class SmsReach { + + @Id + @TableId + @TableField + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @ApiModelProperty(value = "签名名称", required = true) + private String signName; + + @ApiModelProperty(value = "模板名称") + private String smsName; + + @ApiModelProperty(value = "消息CODE") + private String messageCode; + + @ApiModelProperty(value = "消息内容") + private String context; + + @ApiModelProperty(value = "接收人", allowableValues = "1:全部会员,2:选择会员") + private String smsRange; + + @ApiModelProperty(value = "预计发送条数") + private String num; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dos/SmsSign.java b/framework/src/main/java/cn/lili/modules/message/entity/dos/SmsSign.java new file mode 100644 index 00000000..e1f0cb10 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dos/SmsSign.java @@ -0,0 +1,62 @@ +package cn.lili.modules.message.entity.dos; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + + +/** + * 短信签名 + * + * @author Chopper + * @date 2021/1/30 4:13 下午 + */ +@Data +@Entity +@Table(name = "li_sms_sign") +@TableName("li_sms_sign") +@ApiModel(value = "短信签名") +public class SmsSign { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @ApiModelProperty(value = "签名名称", required = true) + private String signName; + + @ApiModelProperty(value = "签名来源", required = true) + private Integer signSource; + + @ApiModelProperty(value = "短信签名申请说明", required = true) + private String remark; + + @ApiModelProperty(value = "营业执照", required = true) + private String businessLicense; + + @ApiModelProperty(value = "授权委托书", required = true) + private String license; + + /** + * 0:审核中。 + * 1:审核通过。 + * 2:审核失败,请在返回参数Reason中查看审核失败原因。 + */ + @ApiModelProperty(value = "签名审核状态") + private Integer signStatus; + + @ApiModelProperty(value = "审核备注") + private String reason; + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dos/SmsTemplate.java b/framework/src/main/java/cn/lili/modules/message/entity/dos/SmsTemplate.java new file mode 100644 index 00000000..e17da568 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dos/SmsTemplate.java @@ -0,0 +1,62 @@ +package cn.lili.modules.message.entity.dos; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + + +/** + * 短信模板 + * + * @author Chopper + * @date 2021/1/30 4:13 下午 + */ +@Data +@Entity +@Table(name = "li_sms_template") +@TableName("li_sms_template") +@ApiModel(value = "短信模板") +public class SmsTemplate { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @ApiModelProperty(value = "模板名称", required = true) + private String templateName; + + @ApiModelProperty(value = "短信类型", required = true) + private Integer templateType; + + @ApiModelProperty(value = "短信模板申请说明", required = true) + private String remark; + + @ApiModelProperty(value = "模板内容", required = true) + private String templateContent; + + /** + * 0:审核中。 + * 1:审核通过。 + * 2:审核失败,请在返回参数Reason中查看审核失败原因。 + */ + @ApiModelProperty(value = "模板审核状态") + private Integer templateStatus; + + @ApiModelProperty(value = "短信模板CODE") + private String templateCode; + + @ApiModelProperty(value = "审核备注") + private String reason; + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dos/StoreMessage.java b/framework/src/main/java/cn/lili/modules/message/entity/dos/StoreMessage.java new file mode 100644 index 00000000..12479e53 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dos/StoreMessage.java @@ -0,0 +1,77 @@ +package cn.lili.modules.message.entity.dos; + +import cn.lili.modules.message.entity.enums.MessageStatusEnum; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.*; +import java.util.Date; + +/** + * 店铺消息 + * @author Chopper + * @date 2021/1/30 4:13 下午 + */ +@Data +@Entity +@Table(name = "li_store_message") +@TableName("li_store_message") +@ApiModel(value = "店铺消息") +public class StoreMessage { + + private static final long serialVersionUID = 1L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "关联消息id") + private String messageId; + + @ApiModelProperty(value = "关联店铺id") + private String storeId; + + @ApiModelProperty(value = "关联店铺名称") + private String storeName; + + /** + * @see MessageStatusEnum + */ + @ApiModelProperty(value = "状态 0默认未读 1已读 2回收站") + private String status = MessageStatusEnum.UN_READY.name(); + + + @Transient + @TableField(exist = false) + @ApiModelProperty(value = "消息标题") + private String title; + + @Transient + @TableField(exist = false) + @ApiModelProperty(value = "消息内容") + private String content; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dos/WechatMPMessage.java b/framework/src/main/java/cn/lili/modules/message/entity/dos/WechatMPMessage.java new file mode 100644 index 00000000..c1c059f1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dos/WechatMPMessage.java @@ -0,0 +1,45 @@ +package cn.lili.modules.message.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * @author Chopper + */ +@Data +@Entity +@Table(name = "li_wechat_mp_message") +@TableName("li_wechat_mp_message") +@ApiModel(value = "微信小程序消息订阅") +public class WechatMPMessage extends BaseEntity { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "模版id") + private String templateId; + + @ApiModelProperty(value = "模版名称") + private String name; + + @ApiModelProperty(value = "微信模版码") + private String code; + + @ApiModelProperty(value = "关键字") + private String keywords; + + @ApiModelProperty(value = "关键字描述(小程序发送消息时使用)") + private String keywordsText; + + @ApiModelProperty(value = "是否开启") + private Boolean enable = true; + + @ApiModelProperty("订单状态") + private String orderStatus; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dos/WechatMessage.java b/framework/src/main/java/cn/lili/modules/message/entity/dos/WechatMessage.java new file mode 100644 index 00000000..2422fed7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dos/WechatMessage.java @@ -0,0 +1,52 @@ +package cn.lili.modules.message.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 微信消息 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/12/10 17:02 + */ +@Data +@Entity +@Table(name = "li_wechat_message") +@TableName("li_wechat_message") +@ApiModel(value = "微信消息") +public class WechatMessage extends BaseEntity { + + + private static final long serialVersionUID = -9157586585885836755L; + + @ApiModelProperty(value = "模版名称") + private String name; + + @ApiModelProperty(value = "微信模版码") + private String code; + + @ApiModelProperty(value = "关键字") + private String keywords; + + @ApiModelProperty(value = "是否开启") + private Boolean enable = true; + + @ApiModelProperty("订单状态") + private String orderStatus; + + @ApiModelProperty(value = "模版头部信息") + private String first; + + @ApiModelProperty(value = "模版备注(位于最下方)") + private String remark; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dto/NoticeMessageDTO.java b/framework/src/main/java/cn/lili/modules/message/entity/dto/NoticeMessageDTO.java new file mode 100644 index 00000000..473ab68a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dto/NoticeMessageDTO.java @@ -0,0 +1,25 @@ +package cn.lili.modules.message.entity.dto; + +import cn.lili.modules.message.entity.enums.NoticeMessageNodeEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Map; + +/** + * 站内信消息 + * @author Chopper + * @date 2020/12/8 9:46 + */ +@Data +public class NoticeMessageDTO { + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "消息节点") + private NoticeMessageNodeEnum noticeMessageNodeEnum; + + @ApiModelProperty(value = "消息参数") + private Map parameter; +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dto/NoticeMessageDetailDTO.java b/framework/src/main/java/cn/lili/modules/message/entity/dto/NoticeMessageDetailDTO.java new file mode 100644 index 00000000..9ef8195a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dto/NoticeMessageDetailDTO.java @@ -0,0 +1,19 @@ +package cn.lili.modules.message.entity.dto; + +import cn.lili.modules.message.entity.dos.NoticeMessage; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 站内信消息DTO + * @author Chopper + * @date 2020/12/8 9:46 + */ +@Data +public class NoticeMessageDetailDTO extends NoticeMessage { + + @ApiModelProperty(value = "消息变量") + private List variables; +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/dto/SmsReachDTO.java b/framework/src/main/java/cn/lili/modules/message/entity/dto/SmsReachDTO.java new file mode 100644 index 00000000..7840ed28 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/dto/SmsReachDTO.java @@ -0,0 +1,17 @@ +package cn.lili.modules.message.entity.dto; + +import cn.lili.modules.message.entity.dos.SmsReach; +import lombok.Data; + +import java.util.List; + +/** + * 短信任务DTO + * @author Chopper + * @date 2020/12/8 9:46 + */ +@Data +public class SmsReachDTO extends SmsReach { + + private List mobile; +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageRangeEnum.java b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageRangeEnum.java new file mode 100644 index 00000000..2b2cc7d1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageRangeEnum.java @@ -0,0 +1,27 @@ +package cn.lili.modules.message.entity.enums; + +/** + * 发送消息范围 + * + * @author pikachu + * @date 2020/12/8 9:46 + */ +public enum MessageRangeEnum { + + + ALL("所有用户"), + + USER("指定用户"); + + private final String description; + + MessageRangeEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageShowType.java b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageShowType.java new file mode 100644 index 00000000..319ca812 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageShowType.java @@ -0,0 +1,29 @@ +package cn.lili.modules.message.entity.enums; + +/** + * 消息展示类型 + * + * @author pikachu + * @date 2020/12/8 9:46 + */ +public enum MessageShowType { + + //订单 + ORDER("订单"), + //售后单 + AFTER_SALE("售后订单"), + //站内信 + NOTICE("站内信"); + + private final String description; + + MessageShowType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageStatusEnum.java b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageStatusEnum.java new file mode 100644 index 00000000..11e107cf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageStatusEnum.java @@ -0,0 +1,29 @@ +package cn.lili.modules.message.entity.enums; + +/** + * 消息状态枚举 + * + * @author pikachu + * @date 2020/12/8 9:46 + */ +public enum MessageStatusEnum { + + //未读消息 + UN_READY("未读消息"), + //已读消息 + ALREADY_READY("已读消息"), + //回收站 + ALREADY_REMOVE("回收站"); + + private final String description; + + MessageStatusEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageTemplateType.java b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageTemplateType.java new file mode 100644 index 00000000..3783a2e5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageTemplateType.java @@ -0,0 +1,29 @@ +package cn.lili.modules.message.entity.enums; + +/** + * 消息模板类型 + * + * @author pikachu + * @date 2020/12/8 9:46 + */ +public enum MessageTemplateType { + + //会员消息 + MEMBER("会员消息"), + //店铺消息 + STORE("店铺消息"), + //其他消息 + OTHER("其他消息"); + + private final String description; + + MessageTemplateType(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageTitle.java b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageTitle.java new file mode 100644 index 00000000..7bd5612d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/enums/MessageTitle.java @@ -0,0 +1,24 @@ +package cn.lili.modules.message.entity.enums; +/** + * 消息标题 + * + * @author pikachu + * @date 2020/12/8 9:46 + */ +public enum MessageTitle { + + NEW_ORDER("您有新的订单,请您关注"), + + PAY_ORDER("您有订单被支付,请您及时进行发货处理"); + + private final String description; + + MessageTitle(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/enums/NoticeMessageNodeEnum.java b/framework/src/main/java/cn/lili/modules/message/entity/enums/NoticeMessageNodeEnum.java new file mode 100644 index 00000000..55552f6b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/enums/NoticeMessageNodeEnum.java @@ -0,0 +1,67 @@ +package cn.lili.modules.message.entity.enums; + +/** + * 消息编码枚举 + * + * @author pikachu + * @date 2020/12/8 9:46 + */ +public enum NoticeMessageNodeEnum { + + /** + * 订单 + */ + ORDER_CREATE_SUCCESS("订单提交成功通知"), + ORDER_CANCEL_SUCCESS("订单取消成功通知"), + ORDER_PAY_SUCCESS("订单支付成功通知"), + ORDER_PAY_ERROR("支付失败自动退款通知"), + ORDER_DELIVER("订单发货通知"), + ORDER_COMPLETE("订单完成通知"), + ORDER_EVALUATION("订单评价提醒"), + + /** + * 售后 + */ + AFTER_SALE_CREATE_SUCCESS("售后提交成功通知"), + RETURN_GOODS_PASS("退货审核通过通知"), + RETURN_MONEY_PASS("退款审核通过通知"), + RETURN_GOODS_REFUSE("退货审核未通过通知"), + RETURN_MONEY_REFUSE("退款审核未通过通知"), + AFTER_SALE_ROG_PASS("退货物品签收通知"), + AFTER_SALE_ROG_REFUSE("退货物品拒收通知"), + AFTER_SALE_COMPLETE("售后完成通知"), + + + /** + * 拼团 + */ + PINTUAN_CREATE("开团成功通知"), + PINTUAN_ERROR("拼团失败通知"), + PINTUAN_SUCCESS("拼团成功通知"), + + /** + * 积分 + */ + POINT_CHANGE("积分变更通知"), + + /** + * 用户余额 + */ + WALLET_CHANGE("余额账户变更通知"), + WALLET_WITHDRAWAL_CREATE("提现申请提交成功通知"), + WALLET_WITHDRAWAL_SUCCESS("提现成功通知"), + WALLET_WITHDRAWAL_ERROR("提现申请驳回通知"); + + + private final String description; + + NoticeMessageNodeEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/enums/NoticeMessageParameterEnum.java b/framework/src/main/java/cn/lili/modules/message/entity/enums/NoticeMessageParameterEnum.java new file mode 100644 index 00000000..15be4c22 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/enums/NoticeMessageParameterEnum.java @@ -0,0 +1,54 @@ +package cn.lili.modules.message.entity.enums; + +/** + * 消息编码枚举 + * + * @author pikachu + * @date 2020/12/8 9:46 + */ +public enum NoticeMessageParameterEnum { + + + GOODS("goods", "商品名称"), + EXPENDITURE_POINTS("expenditure_points", "消费积分"), + INCOME_POINTS("income_points", "获得积分"), + EXPENDITURE("expenditure", "支出金额"), + INCOME("income", "收入金额"), + REFUSE("refuse", "拒绝原因"), + CANCEL_REASON("cancel_reason","取消原因"); + + private final String type; + private final String description; + + NoticeMessageParameterEnum(String type, String description) { + this.type = type; + this.description = description; + } + + /** + * 根据type获取去value + * + * @param type + * @return + */ + public static String getValueByType(String type) { + for (NoticeMessageParameterEnum noticeMessageParameterEnum : NoticeMessageParameterEnum.values()) { + if (type.toLowerCase().equals(noticeMessageParameterEnum.getType().toLowerCase())) { + return noticeMessageParameterEnum.getDescription(); + } + } + return null; + } + + + public String getType() { + return type; + } + + + public String getDescription() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/enums/RangeEnum.java b/framework/src/main/java/cn/lili/modules/message/entity/enums/RangeEnum.java new file mode 100644 index 00000000..0f622253 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/enums/RangeEnum.java @@ -0,0 +1,27 @@ +package cn.lili.modules.message.entity.enums; + +/** + * 消息发送类型 + * + * @author pikachu + * @date 2020/12/8 9:46 + */ +public enum RangeEnum { + + //全部用户 + ALL("全部"), + //指定用户 + APPOINT("指定用户"); + + private final String description; + + RangeEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/enums/WechatMessageItemEnums.java b/framework/src/main/java/cn/lili/modules/message/entity/enums/WechatMessageItemEnums.java new file mode 100644 index 00000000..fd9905cf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/enums/WechatMessageItemEnums.java @@ -0,0 +1,39 @@ +package cn.lili.modules.message.entity.enums; + +/** + * 微信模版设置变量 + * + * @author Chopper + * @version v1.0 + * 2020-12-10 17:47 + */ +public enum WechatMessageItemEnums { + SHOP_NAME(new String[]{"商户名称"}), + MEMBER_NAME(new String[]{"买家昵称"}), + PRICE(new String[]{"支付金额","订单金额"}), + + GOODS_INFO(new String[]{"订单详情","商品清单","商品名称"}), + + ORDER_SN(new String[]{"订单编号"}), + + + LOGISTICS_NAME(new String[]{"快递公司"}), + LOGISTICS_NO(new String[]{"快递单号"}), + + LOGISTICS_TIME(new String[]{"发货时间"}), + PAYMENT_TIME(new String[]{"支付时间"}), + ; + + /** + * 名称 + */ + private String[] text; + + WechatMessageItemEnums(String[] text) { + this.text = text; + } + + public String[] getText() { + return text; + } +} diff --git a/framework/src/main/java/cn/lili/modules/message/entity/vos/MessageShowVO.java b/framework/src/main/java/cn/lili/modules/message/entity/vos/MessageShowVO.java new file mode 100644 index 00000000..577f9779 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/vos/MessageShowVO.java @@ -0,0 +1,42 @@ +package cn.lili.modules.message.entity.vos; + +import cn.lili.modules.message.entity.enums.MessageShowType; +import cn.lili.modules.message.entity.enums.RangeEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 消息 + * + * @author Chopper + * @date 2020/12/2 17:50 + */ +@Data +@ApiModel(value = "消息") +@AllArgsConstructor +@NoArgsConstructor +public class MessageShowVO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "标题") + private String title; + + /** + * @see MessageShowType + */ + @ApiModelProperty(value = "消息类型") + private String type; + + @ApiModelProperty(value = "消息内容") + private String content; + /** + * @see RangeEnum + */ + @ApiModelProperty(value = "发送范围") + private String messageRange; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/entity/vos/MessageVO.java b/framework/src/main/java/cn/lili/modules/message/entity/vos/MessageVO.java new file mode 100644 index 00000000..8221dcdf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/vos/MessageVO.java @@ -0,0 +1,39 @@ +package cn.lili.modules.message.entity.vos; + +import cn.hutool.core.util.StrUtil; +import cn.lili.modules.message.entity.dos.Message; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 消息 + * @author Chopper + * @date 2020/12/2 17:50 + * + */ +@Data +@ApiModel(value = "消息") +public class MessageVO { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "标题") + private String title; + + @ApiModelProperty(value = "内容") + private String content; + + public LambdaQueryWrapper lambdaQueryWrapper() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotEmpty(title)) { + queryWrapper.like(Message::getTitle, title); + } + if (StrUtil.isNotEmpty(content)) { + queryWrapper.like(Message::getContent, content); + } + queryWrapper.orderByDesc(Message::getCreateTime); + return queryWrapper; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/entity/vos/StoreMessageQueryVO.java b/framework/src/main/java/cn/lili/modules/message/entity/vos/StoreMessageQueryVO.java new file mode 100644 index 00000000..fc746773 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/entity/vos/StoreMessageQueryVO.java @@ -0,0 +1,33 @@ +package cn.lili.modules.message.entity.vos; + +import cn.lili.modules.message.entity.enums.MessageStatusEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + + +/** + * 店铺消息查询 + * + * @author Chopper + * @date 2020/12/2 17:50 + */ +@Data +@ApiModel(value = "消息") +public class StoreMessageQueryVO { + + private static final long serialVersionUID = 1L; + + /** + * @see MessageStatusEnum + */ + @ApiModelProperty(value = "状态") + private String status; + + @ApiModelProperty(value = "消息id") + private String messageId; + + @ApiModelProperty(value = "商家id") + private String storeId; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/mapper/MessageMapper.java b/framework/src/main/java/cn/lili/modules/message/mapper/MessageMapper.java new file mode 100644 index 00000000..64df8a05 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/mapper/MessageMapper.java @@ -0,0 +1,12 @@ +package cn.lili.modules.message.mapper; + +import cn.lili.modules.message.entity.dos.Message; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 消息内容数据处理层 + * @author Chopper + * @date 2020/12/2 17:50 + */ +public interface MessageMapper extends BaseMapper { +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/mapper/NoticeMessageTemplateMapper.java b/framework/src/main/java/cn/lili/modules/message/mapper/NoticeMessageTemplateMapper.java new file mode 100644 index 00000000..38f1c3d6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/mapper/NoticeMessageTemplateMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.message.mapper; + +import cn.lili.modules.message.entity.dos.NoticeMessage; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 消息模板数据处理层 + * + * @author Bulbasaur + * @date 2020/12/8 9:46 + */ +public interface NoticeMessageTemplateMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/mapper/ShortLinkMapper.java b/framework/src/main/java/cn/lili/modules/message/mapper/ShortLinkMapper.java new file mode 100644 index 00000000..503003ce --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/mapper/ShortLinkMapper.java @@ -0,0 +1,12 @@ +package cn.lili.modules.message.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import cn.lili.modules.message.entity.dos.ShortLink; + +/** + * 短链接 Dao层 + * @author Chopper + */ +public interface ShortLinkMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/mapper/SmsReachMapper.java b/framework/src/main/java/cn/lili/modules/message/mapper/SmsReachMapper.java new file mode 100644 index 00000000..b9e5d8c2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/mapper/SmsReachMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.message.mapper; + +import cn.lili.modules.message.entity.dos.SmsReach; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 短信任务数据处理层 + * @author Chopper + * @date 2021/1/30 4:17 下午 + */ +public interface SmsReachMapper extends BaseMapper { + +} + diff --git a/framework/src/main/java/cn/lili/modules/message/mapper/SmsSignMapper.java b/framework/src/main/java/cn/lili/modules/message/mapper/SmsSignMapper.java new file mode 100644 index 00000000..ad24c205 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/mapper/SmsSignMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.message.mapper; + +import cn.lili.modules.message.entity.dos.SmsSign; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 短信签名数据处理层 + * @author Chopper + * @date 2021/1/30 4:17 下午 + */ +public interface SmsSignMapper extends BaseMapper { + +} + diff --git a/framework/src/main/java/cn/lili/modules/message/mapper/SmsTemplateMapper.java b/framework/src/main/java/cn/lili/modules/message/mapper/SmsTemplateMapper.java new file mode 100644 index 00000000..ab6b93b9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/mapper/SmsTemplateMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.message.mapper; + +import cn.lili.modules.message.entity.dos.SmsTemplate; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 短信模板数据处理层 + * @author Chopper + * @date 2021/1/30 4:17 下午 + */ +public interface SmsTemplateMapper extends BaseMapper { + +} + diff --git a/framework/src/main/java/cn/lili/modules/message/mapper/StoreMessageMapper.java b/framework/src/main/java/cn/lili/modules/message/mapper/StoreMessageMapper.java new file mode 100644 index 00000000..4c580c00 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/mapper/StoreMessageMapper.java @@ -0,0 +1,21 @@ +package cn.lili.modules.message.mapper; + +import cn.lili.modules.message.entity.dos.StoreMessage; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 消息发送数据处理层 + * + * @author Chopper + * @date 2021/1/30 4:17 下午 + */ +public interface StoreMessageMapper extends BaseMapper { + + @Select("select me.title,me.content,me.create_time,sp.store_name,sp.store_id,sp.id,sp.status from li_message me inner join li_store_message sp on me.id = sp.message_id ${ew.customSqlSegment} ") + IPage queryByParams(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/mapper/WechatMPMessageMapper.java b/framework/src/main/java/cn/lili/modules/message/mapper/WechatMPMessageMapper.java new file mode 100644 index 00000000..55e7e92d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/mapper/WechatMPMessageMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.message.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import cn.lili.modules.message.entity.dos.WechatMPMessage; +import org.apache.ibatis.annotations.Delete; + +/** + * 微信小程序消息订阅 Dao层 + * @author Chopper + */ +public interface WechatMPMessageMapper extends BaseMapper { + + @Delete("delete from li_wechat_mp_message") + void deleteAll(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/mapper/WechatMessageMapper.java b/framework/src/main/java/cn/lili/modules/message/mapper/WechatMessageMapper.java new file mode 100644 index 00000000..e71e5e1c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/mapper/WechatMessageMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.message.mapper; + +import cn.lili.modules.message.entity.dos.WechatMessage; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Delete; + +/** + * 微信消息 Dao层 + * @author Chopper + */ +public interface WechatMessageMapper extends BaseMapper { + + @Delete("delete from li_wechat_message") + void deleteAll(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/service/MessageService.java b/framework/src/main/java/cn/lili/modules/message/service/MessageService.java new file mode 100644 index 00000000..f5189caf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/service/MessageService.java @@ -0,0 +1,42 @@ +package cn.lili.modules.message.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.message.entity.dos.Message; +import cn.lili.modules.message.entity.vos.MessageVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 消息内容业务层 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +public interface MessageService extends IService { + + /** + * 多条件分页获取 + * + * @param messageVO + * @param pageVO + * @return + */ + IPage getPage(MessageVO messageVO, PageVO pageVO); + + /** + * 发送站内信 + * + * @param message 站内信 + * @return + */ + Boolean sendMessage(Message message); + + /** + * 删除站内信 + * + * @param id 站内信id + * @return + */ + Boolean deleteMessage(String id); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/service/NoticeMessageService.java b/framework/src/main/java/cn/lili/modules/message/service/NoticeMessageService.java new file mode 100644 index 00000000..fabf2576 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/service/NoticeMessageService.java @@ -0,0 +1,33 @@ +package cn.lili.modules.message.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.message.entity.dos.NoticeMessage; +import cn.lili.modules.message.entity.dto.NoticeMessageDTO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 消息模板业务层 + * + * @author Bulbasaur + * @date 2020/12/8 9:47 + */ +public interface NoticeMessageService extends IService { + + /** + * 多条件分页获取 + * + * @param pageVO 分页数据 + * @param type 类型 + * @return + */ + IPage getMessageTemplate(PageVO pageVO, String type); + + /** + * 根据模板编码获取消息模板 + * + * @param noticeMessageDTO 站内信消息 + */ + void noticeMessage(NoticeMessageDTO noticeMessageDTO); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/service/ShortLinkService.java b/framework/src/main/java/cn/lili/modules/message/service/ShortLinkService.java new file mode 100644 index 00000000..3789ff56 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/service/ShortLinkService.java @@ -0,0 +1,12 @@ +package cn.lili.modules.message.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import cn.lili.modules.message.entity.dos.ShortLink; + +/** + * 短链接 业务层 + * @author Chopper + */ +public interface ShortLinkService extends IService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/service/SmsReachService.java b/framework/src/main/java/cn/lili/modules/message/service/SmsReachService.java new file mode 100644 index 00000000..6866dd22 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/service/SmsReachService.java @@ -0,0 +1,24 @@ +package cn.lili.modules.message.service; + +import cn.lili.modules.message.entity.dos.SmsReach; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 短信任务业务层 + * + * @author Bulbasaur + * @date 2021/1/30 3:19 下午 + */ +public interface SmsReachService extends IService { + + /** + * 添加短信任务 + * + * @param smsReach 短信签名 + */ + void addSmsReach(SmsReach smsReach, List mobile); + + +} diff --git a/framework/src/main/java/cn/lili/modules/message/service/SmsSignService.java b/framework/src/main/java/cn/lili/modules/message/service/SmsSignService.java new file mode 100644 index 00000000..f527d24e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/service/SmsSignService.java @@ -0,0 +1,52 @@ +package cn.lili.modules.message.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.message.entity.dos.SmsSign; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 签名申请业务层 + * + * @author Bulbasaur + * @date 2021/1/30 3:19 下午 + */ +public interface SmsSignService extends IService { + + /** + * 添加短信签名 + * + * @param smsSign 短信签名 + */ + void addSmsSign(SmsSign smsSign); + + /** + * 删除短信签名 + * + * @param id 短信签名id + */ + void deleteSmsSign(String id); + + /** + * 查询短信签名申请状态 + */ + void querySmsSign(); + + /** + * 修改未审核通过的短信签名,并重新提交审核。 + * + * @param smsSign 短信签名 + */ + void modifySmsSign(SmsSign smsSign); + + + /** + * 分页查询短信签名 + * + * @param pageVO 分页参数 + * @param signStatus 短信签名状态 + * @return 分页数据 + */ + IPage page(PageVO pageVO, Integer signStatus); + +} diff --git a/framework/src/main/java/cn/lili/modules/message/service/SmsTemplateService.java b/framework/src/main/java/cn/lili/modules/message/service/SmsTemplateService.java new file mode 100644 index 00000000..fc25b636 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/service/SmsTemplateService.java @@ -0,0 +1,51 @@ +package cn.lili.modules.message.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.message.entity.dos.SmsTemplate; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 签名申请业务层 + * + * @author Bulbasaur + * @date 2021/1/30 3:19 下午 + */ +public interface SmsTemplateService extends IService { + + /** + * 添加短信模板 + * + * @param smsTemplate 短信模板 + */ + void addSmsTemplate(SmsTemplate smsTemplate); + + /** + * 删除短信模板 + * + * @param templateCode 短信模板CODE + */ + void deleteSmsTemplate(String templateCode); + + /** + * 查询短信模板的审核状态 + */ + void querySmsTemplate(); + + /** + * 修改未通过审核的短信模板,并重新提交审核。 + * + * @param smsTemplate 短信模板 + */ + void modifySmsTemplate(SmsTemplate smsTemplate); + + /** + * 分页查询短信模板 + * + * @param pageVO 分页参数 + * @param templateStatus 状态 + * @return + */ + IPage page(PageVO pageVO, Integer templateStatus); + +} diff --git a/framework/src/main/java/cn/lili/modules/message/service/StoreMessageService.java b/framework/src/main/java/cn/lili/modules/message/service/StoreMessageService.java new file mode 100644 index 00000000..ad700035 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/service/StoreMessageService.java @@ -0,0 +1,54 @@ +package cn.lili.modules.message.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.message.entity.dos.StoreMessage; +import cn.lili.modules.message.entity.vos.StoreMessageQueryVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 消息发送业务层 + * + * @author Chopper + * @date 2020/11/17 3:44 下午 + */ +public interface StoreMessageService extends IService { + + + /** + * 通过消息id删除 + * + * @param messageId + */ + boolean deleteByMessageId(String messageId); + + /** + * 多条件分页获取 + * + * @param storeMessageQueryVO + * @param pageVO + * @return + */ + IPage getPage(StoreMessageQueryVO storeMessageQueryVO, PageVO pageVO); + + /** + * 保存消息信息 + * + * @param messages 消息 + * @return + */ + boolean save(List messages); + + + /** + * 修改店铺消息状态 + * + * @param status 状态 + * @param id id + * @return + */ + boolean editStatus(String status, String id); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/service/WechatMPMessageService.java b/framework/src/main/java/cn/lili/modules/message/service/WechatMPMessageService.java new file mode 100644 index 00000000..05b2aef8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/service/WechatMPMessageService.java @@ -0,0 +1,16 @@ +package cn.lili.modules.message.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import cn.lili.modules.message.entity.dos.WechatMPMessage; + +/** + * 微信小程序消息订阅 业务层 + * @author Chopper + */ +public interface WechatMPMessageService extends IService { + + /** + * 初始化微信消息订阅模版 + */ + void init(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/service/WechatMessageService.java b/framework/src/main/java/cn/lili/modules/message/service/WechatMessageService.java new file mode 100644 index 00000000..f471ed89 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/service/WechatMessageService.java @@ -0,0 +1,16 @@ +package cn.lili.modules.message.service; + +import cn.lili.modules.message.entity.dos.WechatMessage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 微信消息 业务层 + * @author Chopper + */ +public interface WechatMessageService extends IService { + + /** + * 初始化微信消息模版 + */ + void init(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/serviceimpl/MessageServiceImpl.java b/framework/src/main/java/cn/lili/modules/message/serviceimpl/MessageServiceImpl.java new file mode 100644 index 00000000..1af5539b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/serviceimpl/MessageServiceImpl.java @@ -0,0 +1,68 @@ +package cn.lili.modules.message.serviceimpl; + +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.OtherTagsEnum; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.message.entity.dos.Message; +import cn.lili.modules.message.entity.vos.MessageVO; +import cn.lili.modules.message.mapper.MessageMapper; +import cn.lili.modules.message.mapper.StoreMessageMapper; +import cn.lili.modules.message.service.MessageService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 消息内容业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:48 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MessageServiceImpl extends ServiceImpl implements MessageService { + + private final MessageMapper messageMapper; + + private final StoreMessageMapper storeMessageMapper; + private final SimpMessagingTemplate messagingTemplate; + private final RocketMQTemplate rocketMQTemplate; + private final RocketmqCustomProperties rocketmqCustomProperties; + + + @Override + public IPage getPage(MessageVO messageVO, PageVO pageVO) { + return this.page(PageUtil.initPage(pageVO), messageVO.lambdaQueryWrapper()); + } + + + @Override + public Boolean sendMessage(Message message) { + //保存站内信信息 + this.save(message); + //发送站内信消息提醒 + String destination = rocketmqCustomProperties.getOtherTopic() + ":" + OtherTagsEnum.MESSAGE.name(); + rocketMQTemplate.asyncSend(destination, message, RocketmqSendCallbackBuilder.commonCallback()); + String noticeSendDestination = rocketmqCustomProperties.getNoticeSendTopic() + ":" + OtherTagsEnum.MESSAGE.name(); + rocketMQTemplate.asyncSend(noticeSendDestination, message, RocketmqSendCallbackBuilder.commonCallback()); + return true; + } + + @Override + public Boolean deleteMessage(String id) { + //只有查询到此记录才真实删除,未找到记录则直接返回true即可 + Message message = this.getById(id); + if (message != null) { + return this.removeById(id); + } + return true; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/serviceimpl/NoticeMessageServiceImpl.java b/framework/src/main/java/cn/lili/modules/message/serviceimpl/NoticeMessageServiceImpl.java new file mode 100644 index 00000000..2aded459 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/serviceimpl/NoticeMessageServiceImpl.java @@ -0,0 +1,93 @@ +package cn.lili.modules.message.serviceimpl; + +import cn.lili.common.enums.SwitchEnum; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.MemberNotice; +import cn.lili.modules.member.service.MemberNoticeService; +import cn.lili.modules.message.entity.dos.NoticeMessage; +import cn.lili.modules.message.entity.dto.NoticeMessageDTO; +import cn.lili.modules.message.entity.enums.NoticeMessageParameterEnum; +import cn.lili.modules.message.mapper.NoticeMessageTemplateMapper; +import cn.lili.modules.message.service.NoticeMessageService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Map; + +/** + * 消息模板业务层实现 + * + * @author Bulbasaur + * @date 2020/12/8 9:48 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class NoticeMessageServiceImpl extends ServiceImpl implements NoticeMessageService { + + + private final MemberNoticeService memberNoticeService; + + @Override + public IPage getMessageTemplate(PageVO pageVO, String type) { + //构建查询参数 + QueryWrapper messageTemplateQueryWrapper = new QueryWrapper<>(); + //消息模板类型 + messageTemplateQueryWrapper.eq(!StringUtils.isEmpty(type), "type", type); + messageTemplateQueryWrapper.orderByDesc("create_time"); + //查询数据返回 + return this.page(PageUtil.initPage(pageVO), messageTemplateQueryWrapper); + + } + + @Override + public void noticeMessage(NoticeMessageDTO noticeMessageDTO) { + if (noticeMessageDTO == null) { + return; + } + NoticeMessage noticeMessage = this.getOne(new LambdaQueryWrapper().eq(NoticeMessage::getNoticeNode, noticeMessageDTO.getNoticeMessageNodeEnum().getDescription().trim())); + //如果通知类站内信开启的情况下 + if (noticeMessage != null && noticeMessage.getNoticeStatus().equals(SwitchEnum.OPEN.name())) { + MemberNotice memberNotice = new MemberNotice(); + memberNotice.setMemberId(noticeMessageDTO.getMemberId()); + memberNotice.setTitle(noticeMessage.getNoticeTitle()); + memberNotice.setContent(noticeMessage.getNoticeContent()); + //参数不为空,替换内容 + if (noticeMessageDTO.getParameter() != null) { + memberNotice.setContent(replaceNoticeContent(noticeMessage.getNoticeContent(), noticeMessageDTO.getParameter())); + } else { + memberNotice.setContent(noticeMessage.getNoticeContent()); + } + //添加站内信 + memberNoticeService.save(memberNotice); + } + + } + + /** + * 替换站内信内容 + * + * @param noticeContent 站内信内容 + * @param parameter 参数 + * @return 替换后站内信内容 + */ + String replaceNoticeContent(String noticeContent, Map parameter) { + for (String key : parameter.keySet()) { + String description = NoticeMessageParameterEnum.getValueByType(key); + if (description != null && parameter.get(key) != null) { + noticeContent = noticeContent.replace("#{" + description + "}".trim(), parameter.get(key)); + } + } + return noticeContent; + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/serviceimpl/ShortLinkServiceImpl.java b/framework/src/main/java/cn/lili/modules/message/serviceimpl/ShortLinkServiceImpl.java new file mode 100644 index 00000000..b69519df --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/serviceimpl/ShortLinkServiceImpl.java @@ -0,0 +1,22 @@ +package cn.lili.modules.message.serviceimpl; + +import cn.lili.modules.message.mapper.ShortLinkMapper; +import cn.lili.modules.message.entity.dos.ShortLink; +import cn.lili.modules.message.service.ShortLinkService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 短链接 业务实现 + * @author Chopper + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ShortLinkServiceImpl extends ServiceImpl implements ShortLinkService { + + private final ShortLinkMapper shortLinkMapper; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/serviceimpl/SmsReachServiceImpl.java b/framework/src/main/java/cn/lili/modules/message/serviceimpl/SmsReachServiceImpl.java new file mode 100644 index 00000000..620c7077 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/serviceimpl/SmsReachServiceImpl.java @@ -0,0 +1,47 @@ +package cn.lili.modules.message.serviceimpl; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.OtherTagsEnum; +import cn.lili.common.utils.BeanUtil; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.message.entity.dos.SmsReach; +import cn.lili.modules.message.entity.dto.SmsReachDTO; +import cn.lili.modules.message.mapper.SmsReachMapper; +import cn.lili.modules.message.service.SmsReachService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 短信任务业务层实现 + * + * @author Bulbasaur + * @date 2021/1/30 3:19 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SmsReachServiceImpl extends ServiceImpl implements SmsReachService { + + private final RocketMQTemplate rocketMQTemplate; + private final RocketmqCustomProperties rocketmqCustomProperties; + + + @Override + public void addSmsReach(SmsReach smsReach,List mobile) { + String destination = rocketmqCustomProperties.getNoticeSendTopic() + ":" + OtherTagsEnum.SMS.name(); + SmsReachDTO smsReachDTO = new SmsReachDTO(); + BeanUtil.copyProperties(smsReach,smsReachDTO); + smsReachDTO.setMobile(mobile); + this.save(smsReach); + //发送订单变更mq消息 + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(smsReachDTO), RocketmqSendCallbackBuilder.commonCallback()); + + } +} diff --git a/framework/src/main/java/cn/lili/modules/message/serviceimpl/SmsSignServiceImpl.java b/framework/src/main/java/cn/lili/modules/message/serviceimpl/SmsSignServiceImpl.java new file mode 100644 index 00000000..d77efb5e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/serviceimpl/SmsSignServiceImpl.java @@ -0,0 +1,100 @@ +package cn.lili.modules.message.serviceimpl; + +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.sms.AliSmsUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.message.entity.dos.SmsSign; +import cn.lili.modules.message.mapper.SmsSignMapper; +import cn.lili.modules.message.service.SmsSignService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 短信签名业务层实现 + * @author Chopper + * @date 2021/1/30 4:27 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SmsSignServiceImpl extends ServiceImpl implements SmsSignService { + + private final AliSmsUtil aliSmsUtil; + + @Override + public void addSmsSign(SmsSign smsSign) { + try { + //如果短信签名已存在,不能重复申请 + if (this.getOne(new QueryWrapper().eq("sign_name", smsSign.getSignName())) != null) { + throw new ServiceException(ResultCode.SMS_SIGN_EXIST_ERROR); + } + aliSmsUtil.addSmsSign(smsSign); + smsSign.setSignStatus(0); + this.save(smsSign); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void deleteSmsSign(String id) { + try { + SmsSign smsSign = this.getById(id); + if (smsSign != null) { + aliSmsUtil.deleteSmsSign(smsSign.getSignName()); + this.removeById(id); + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + public void querySmsSign() { + try { + Map map = new HashMap<>(); + //获取未审核通过的签名列表 + List list = list(new LambdaQueryWrapper().eq(SmsSign::getSignStatus, 0)); + //查询签名状态 + for (SmsSign smsSign : list) { + map = aliSmsUtil.querySmsSign(smsSign.getSignName()); + + smsSign.setSignStatus((Integer) map.get("SignStatus")); + smsSign.setReason(map.get("Reason").toString()); + this.updateById(smsSign); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void modifySmsSign(SmsSign smsSign) { + try { + aliSmsUtil.modifySmsSign(smsSign); + this.updateById(smsSign); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public IPage page(PageVO pageVO, Integer signStatus) { + return this.page(PageUtil.initPage(pageVO), new QueryWrapper() + .eq(signStatus != null, "sign_status", signStatus)); + } +} diff --git a/framework/src/main/java/cn/lili/modules/message/serviceimpl/SmsTemplateServiceImpl.java b/framework/src/main/java/cn/lili/modules/message/serviceimpl/SmsTemplateServiceImpl.java new file mode 100644 index 00000000..028d417a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/serviceimpl/SmsTemplateServiceImpl.java @@ -0,0 +1,96 @@ +package cn.lili.modules.message.serviceimpl; + +import cn.lili.common.sms.AliSmsUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.message.entity.dos.SmsTemplate; +import cn.lili.modules.message.mapper.SmsTemplateMapper; +import cn.lili.modules.message.service.SmsTemplateService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 短信模板业务层实现 + * @author Chopper + * @date 2021/1/30 4:27 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SmsTemplateServiceImpl extends ServiceImpl implements SmsTemplateService { + + private final AliSmsUtil aliSmsUtil; + + + @Override + public void addSmsTemplate(SmsTemplate smsTemplate) { + try { + smsTemplate.setTemplateCode(aliSmsUtil.addSmsTemplate(smsTemplate)); + smsTemplate.setTemplateStatus(0); + smsTemplate.setTemplateType(1); + this.save(smsTemplate); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void deleteSmsTemplate(String id) { + try { + SmsTemplate smsTemplate = this.getById(id); + if (smsTemplate.getTemplateCode() != null) { + aliSmsUtil.deleteSmsTemplate(smsTemplate.getTemplateCode()); + } + this.removeById(id); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + public void querySmsTemplate() { + try { + Map map = new HashMap<>(); + //获取未审核通过的签名列表 + List list = list(new LambdaQueryWrapper().eq(SmsTemplate::getTemplateStatus, 0)); + //查询签名状态 + for (SmsTemplate smsTemplate : list) { + map = aliSmsUtil.querySmsTemplate(smsTemplate.getTemplateName()); + smsTemplate.setTemplateStatus((Integer) map.get("TemplateStatus")); + smsTemplate.setReason(map.get("Reason").toString()); + smsTemplate.setTemplateCode(map.get("TemplateCode").toString()); + this.updateById(smsTemplate); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void modifySmsTemplate(SmsTemplate smsTemplate) { + try { + aliSmsUtil.modifySmsTemplate(smsTemplate); + smsTemplate.setTemplateStatus(0); + this.updateById(smsTemplate); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public IPage page(PageVO pageVO, Integer templateStatus) { + return this.page(PageUtil.initPage(pageVO), new QueryWrapper() + .eq(templateStatus != null, "template_status", templateStatus)); + } +} diff --git a/framework/src/main/java/cn/lili/modules/message/serviceimpl/StoreMessageServiceImpl.java b/framework/src/main/java/cn/lili/modules/message/serviceimpl/StoreMessageServiceImpl.java new file mode 100644 index 00000000..6a72d6b1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/serviceimpl/StoreMessageServiceImpl.java @@ -0,0 +1,88 @@ +package cn.lili.modules.message.serviceimpl; + + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.message.entity.dos.StoreMessage; +import cn.lili.modules.message.entity.vos.StoreMessageQueryVO; +import cn.lili.modules.message.mapper.StoreMessageMapper; +import cn.lili.modules.message.service.StoreMessageService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.elasticsearch.ResourceNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 消息发送业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:48 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreMessageServiceImpl extends ServiceImpl implements StoreMessageService { + + private final StoreMessageMapper storeMessageMapper; + + @Override + public boolean deleteByMessageId(String messageId) { + StoreMessage storeMessage = this.getById(messageId); + if (storeMessage != null) { + int result = storeMessageMapper.deleteById(messageId); + return result > 0; + } + return false; + + } + + @Override + public IPage getPage(StoreMessageQueryVO storeMessageQueryVO, PageVO pageVO) { + + QueryWrapper queryWrapper = new QueryWrapper<>(); + //消息id查询 + if (StringUtils.isNotEmpty(storeMessageQueryVO.getMessageId())) { + queryWrapper.eq("message_id", storeMessageQueryVO.getMessageId()); + } + //商家id + if (StringUtils.isNotEmpty(storeMessageQueryVO.getStoreId())) { + queryWrapper.eq("store_id", storeMessageQueryVO.getStoreId()); + } + //状态查询 + if (storeMessageQueryVO.getStatus() != null) { + queryWrapper.eq("status", storeMessageQueryVO.getStatus()); + } + queryWrapper.orderByDesc("status"); + return storeMessageMapper.queryByParams(PageUtil.initPage(pageVO), queryWrapper); + + } + + @Override + public boolean save(List messages) { + return saveBatch(messages); + } + + @Override + public boolean editStatus(String status, String id) { + StoreMessage storeMessage = this.getById(id); + if (storeMessage != null) { + //校验权限 + if (!storeMessage.getStoreId().equals(UserContext.getCurrentUser().getStoreId())) { + throw new ResourceNotFoundException(ResultCode.USER_AUTHORITY_ERROR.message()); + } + storeMessage.setStatus(status); + int result = this.storeMessageMapper.updateById(storeMessage); + return result > 0; + } + return false; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/serviceimpl/WechatMPMessageServiceImpl.java b/framework/src/main/java/cn/lili/modules/message/serviceimpl/WechatMPMessageServiceImpl.java new file mode 100644 index 00000000..daaaa215 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/serviceimpl/WechatMPMessageServiceImpl.java @@ -0,0 +1,215 @@ +package cn.lili.modules.message.serviceimpl; + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.message.entity.dos.WechatMPMessage; +import cn.lili.modules.message.entity.enums.WechatMessageItemEnums; +import cn.lili.modules.message.mapper.WechatMPMessageMapper; +import cn.lili.modules.message.service.WechatMPMessageService; +import cn.lili.modules.message.util.WechatAccessTokenUtil; +import cn.lili.modules.message.util.WechatMessageUtil; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.system.utils.HttpUtils; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 微信小程序消息订阅 业务实现 + * + * @author Chopper + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WechatMPMessageServiceImpl extends ServiceImpl implements WechatMPMessageService { + + private final WechatMPMessageMapper wechatMPMessageMapper; + private final WechatAccessTokenUtil wechatAccessTokenUtil; + + //get 获取所有的模版 + private final String allMsgTpl = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token="; + + //获取keyid + private final String keywords = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token="; + + //post 删除模版 添加模版 获取模版id + private final String delMsgTpl = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token="; + private final String addTpl = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token="; + + @Override + public void init() { + wechatMPMessageMapper.deleteAll(); + try { + String accessToken = wechatAccessTokenUtil.cgiAccessToken(ClientTypeEnum.WECHAT_MP); + //获取已有模版,删除 + String context = HttpUtil.get(allMsgTpl + accessToken); + JSONObject jsonObject = new JSONObject(context); + WechatMessageUtil.wechatHandler(jsonObject); + List oldList = new ArrayList<>(); + if (jsonObject.containsKey("data")) { + jsonObject.getJSONArray("data").forEach(item -> { + oldList.add(JSONUtil.parseObj(item).getStr("priTmplId")); + }); + } + if (oldList.size() != 0) { + oldList.forEach(templateId -> { + Map params = new HashMap<>(1); + params.put("priTmplId", templateId); + WechatMessageUtil.wechatHandler(HttpUtil.post(delMsgTpl + accessToken, params)); + }); + } + + //加入数据 + List tmpList = initData(); + tmpList.forEach(tplData -> { + WechatMPMessage wechatMPMessage = new WechatMPMessage(); + + Map params = new HashMap<>(); + params.put("tid", tplData.getTid()); + //获取微信消息订阅keys + String keywordsItems = WechatMessageUtil.wechatHandler(HttpUtil.get(keywords + accessToken, params)); + JSONArray jsonArray = new JSONObject(keywordsItems).getJSONArray("data"); + List keywordArray = jsonArray.toList(WechatMessageKeyword.class); + + log.error("keywords:" + keywordArray); + //存放约定好的kids + List kids = new ArrayList<>(tplData.keyWord.size()); + List kidTexts = new ArrayList<>(tplData.keyWord.size()); + keywordArray: + for (WechatMessageItemEnums enums : tplData.getKeyWord()) { + + for (String tplKey : enums.getText()) { + for (WechatMessageKeyword wechatMessageKeyword : keywordArray) { + if (wechatMessageKeyword.getName().equals(tplKey)) { + kids.add(wechatMessageKeyword.getKid()); + kidTexts.add(wechatMessageKeyword.getRule() + wechatMessageKeyword.getKid()); + continue keywordArray; + } + } + } + } + params = new HashMap<>(); + params.put("tid", tplData.getTid()); + params.put("kidList", kids); + params.put("sceneDesc", tplData.getSceneDesc()); + String content = HttpUtils.doPostWithJson(addTpl + accessToken, params); + log.error(JSONUtil.toJsonStr(params)); + JSONObject tplContent = new JSONObject(content); + WechatMessageUtil.wechatHandler(tplContent); + + //如果包含模版id则进行操作,否则抛出异常 + if (tplContent.containsKey("priTmplId")) { + wechatMPMessage.setCode(tplContent.getStr("priTmplId")); + } else { + throw new ServiceException("未能获取到微信模版消息id"); + } + + wechatMPMessage.setName(tplData.getSceneDesc()); + wechatMPMessage.setTemplateId(tplData.getTid()); + wechatMPMessage.setKeywords(JSONUtil.toJsonStr(tplData.getKeyWord())); + wechatMPMessage.setKeywordsText(JSONUtil.toJsonStr(kidTexts)); + wechatMPMessage.setEnable(true); + wechatMPMessage.setOrderStatus(tplData.getOrderStatus().name()); + this.save(wechatMPMessage); + }); + } catch (Exception e) { + log.error("初始化微信消息异常", e); + } + + } + + + public List initData() { + + List msg = new ArrayList<>(); + //支付提醒 + List PAIDkeyWord = new ArrayList<>(); + PAIDkeyWord.add(WechatMessageItemEnums.ORDER_SN); + PAIDkeyWord.add(WechatMessageItemEnums.MEMBER_NAME); + PAIDkeyWord.add(WechatMessageItemEnums.PRICE); + PAIDkeyWord.add(WechatMessageItemEnums.GOODS_INFO); + msg.add(new WechatMPMessageData( + "订单支付成功,准备发货", + "487", PAIDkeyWord, + OrderStatusEnum.UNDELIVERED)); + //发货提醒 + + List deliverdKeyWord = new ArrayList<>(); + deliverdKeyWord.add(WechatMessageItemEnums.ORDER_SN); + deliverdKeyWord.add(WechatMessageItemEnums.GOODS_INFO); + deliverdKeyWord.add(WechatMessageItemEnums.LOGISTICS_NAME); + deliverdKeyWord.add(WechatMessageItemEnums.LOGISTICS_NO); + msg.add(new WechatMPMessageData( + "订单发货成功", + "374", deliverdKeyWord, + OrderStatusEnum.DELIVERED)); + + + //已完成 + + List completeKeyWord = new ArrayList<>(); + completeKeyWord.add(WechatMessageItemEnums.SHOP_NAME); + completeKeyWord.add(WechatMessageItemEnums.GOODS_INFO); + msg.add(new WechatMPMessageData( + "订单完成", + "3606", completeKeyWord, + OrderStatusEnum.COMPLETED)); + + return msg; + + + } +} + +@Data +@AllArgsConstructor +@NoArgsConstructor +class WechatMPMessageData { + + /** + * 场景描述 // 等于本服务器模版名称 + */ + String sceneDesc; + + /** + * 模版id + */ + String tid; + + /** + * 自身服务器 消息字段 + */ + List keyWord; + + /** + * 处于什么状态发送 + */ + OrderStatusEnum orderStatus; +} + +@Data +class WechatMessageKeyword { + //id字段 + private String kid; + //名称 + private String name; + //示例值 + private String example; + //示例值 + private String rule; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/serviceimpl/WechatMessageServiceImpl.java b/framework/src/main/java/cn/lili/modules/message/serviceimpl/WechatMessageServiceImpl.java new file mode 100644 index 00000000..1d323640 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/serviceimpl/WechatMessageServiceImpl.java @@ -0,0 +1,179 @@ +package cn.lili.modules.message.serviceimpl; + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.message.entity.dos.WechatMessage; +import cn.lili.modules.message.entity.enums.WechatMessageItemEnums; +import cn.lili.modules.message.mapper.WechatMessageMapper; +import cn.lili.modules.message.service.WechatMessageService; +import cn.lili.modules.message.util.WechatAccessTokenUtil; +import cn.lili.modules.message.util.WechatMessageUtil; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.system.utils.HttpUtils; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 微信模版消息(公众号) 业务实现 + * + * @author Chopper + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WechatMessageServiceImpl extends ServiceImpl implements WechatMessageService { + + private final WechatMessageMapper wechatMessageMapper; + private final WechatAccessTokenUtil wechatAccessTokenUtil; + + //get 获取所有的模版 + private final String allMsgTpl = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token="; + + //post 删除模版 添加模版 获取模版id + private final String delMsgTpl = "https://api.weixin.qq.com/cgi-bin/template/del_private_template?access_token="; + private final String addTpl = "https://api.weixin.qq.com/cgi-bin/template/api_add_template?access_token="; + + @Override + public void init() { + try { + wechatMessageMapper.deleteAll(); + + String accessToken = wechatAccessTokenUtil.cgiAccessToken(ClientTypeEnum.H5); + //获取已有模版,删除 + String context = HttpUtil.get(allMsgTpl + accessToken); + JSONObject jsonObject = new JSONObject(context); + WechatMessageUtil.wechatHandler(jsonObject); + List oldList = new ArrayList<>(); + if (jsonObject.containsKey("template_list")) { + jsonObject.getJSONArray("template_list").forEach(item -> { + oldList.add(JSONUtil.parseObj(item).getStr("template_id")); + }); + } + if (oldList.size() != 0) { + oldList.forEach(templateId -> { + Map params = new HashMap<>(1); + params.put("template_id", templateId); + WechatMessageUtil.wechatHandler(HttpUtil.post(delMsgTpl + accessToken, params)); + }); + } + + //加入数据 + List tmpList = initData(); + tmpList.forEach(tplData -> { + WechatMessage wechatMessage = new WechatMessage(); + Map params = new HashMap<>(1); + params.put("template_id_short", tplData.getMsgId()); + String content = HttpUtils.doPostWithJson(addTpl + accessToken, params); + JSONObject tplContent = new JSONObject(content); + WechatMessageUtil.wechatHandler(tplContent); + + //如果包含模版id则进行操作,否则抛出异常 + if (tplContent.containsKey("template_id")) { + wechatMessage.setCode(tplContent.getStr("template_id")); + } else { + throw new ServiceException("未能获取到微信模版消息id"); + } + + wechatMessage.setName(tplData.getName()); + wechatMessage.setFirst(tplData.getFirst()); + wechatMessage.setRemark(tplData.getRemark()); + wechatMessage.setKeywords(tplData.getKeyWord()); + wechatMessage.setEnable(true); + wechatMessage.setOrderStatus(tplData.getOrderStatus().name()); + this.save(wechatMessage); + }); + } catch (Exception e) { + log.error("初始化微信消息异常", e); + } + } + + + /** + * 初始化数据 + * + * @return + */ + private List initData() { + List msg = new ArrayList<>(); + //新订单消息提示 + msg.add(new WechatMessageData( + "待支付", + "您有新订单需要支付", + "如有问题,请联系在线客服", + "OPENTM207498902", + WechatMessageItemEnums.MEMBER_NAME.name() + "," + WechatMessageItemEnums.ORDER_SN.name() + "," + + WechatMessageItemEnums.PRICE.name() + "," + WechatMessageItemEnums.GOODS_INFO.name(), + OrderStatusEnum.UNPAID)); + //已发货 + msg.add(new WechatMessageData( + "订单发货", + "您的订单已发货", + "如有问题,请联系在线客服", + "OPENTM200565259", + WechatMessageItemEnums.ORDER_SN.name() + "," + + WechatMessageItemEnums.LOGISTICS_NAME.name() + "," + WechatMessageItemEnums.LOGISTICS_NO.name(), + OrderStatusEnum.DELIVERED)); + + //已完成 + msg.add(new WechatMessageData( + "订单完成", + "您的订单已完成,是否有什么想对掌柜说的话呢", + "诚邀您来评价,评价还赠送积分哦", + "OPENTM416131050", + WechatMessageItemEnums.MEMBER_NAME.name() + "," + WechatMessageItemEnums.ORDER_SN.name() + "," + + WechatMessageItemEnums.PRICE.name() + "," + WechatMessageItemEnums.GOODS_INFO.name(), + OrderStatusEnum.COMPLETED)); + + return msg; + } + +} + +@Data +@AllArgsConstructor +@NoArgsConstructor +class WechatMessageData { + + /** + * 名称 + */ + String name; + /** + * 首部信息 + */ + String first; + /** + * 备注信息 + */ + String remark; + /** + * 微信消息id + */ + String msgId; + + /** + * 消息内容 + */ + String keyWord; + + /** + * 处于什么状态发送 + */ + OrderStatusEnum orderStatus; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/util/WechatAccessTokenUtil.java b/framework/src/main/java/cn/lili/modules/message/util/WechatAccessTokenUtil.java new file mode 100644 index 00000000..07f145a3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/util/WechatAccessTokenUtil.java @@ -0,0 +1,115 @@ +package cn.lili.modules.message.util; + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.connect.util.HttpUtils; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.connect.WechatConnectSetting; +import cn.lili.modules.system.entity.dto.connect.dto.WechatConnectSettingItem; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.google.gson.Gson; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 微信API交互token + * + * @author Chopper + * @version v1.0 + * 2020-12-10 19:25 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WechatAccessTokenUtil { + + private final Cache cache; + + private final SettingService settingService; + + /** + * 获取某一平台等cgi token 用于业务调用,例如发送公众号消息 + * + * @param clientTypeEnum h5 公众号 / wechatMP 微信小程序 + * @return + */ + public String cgiAccessToken(ClientTypeEnum clientTypeEnum) { + //h5 和MP 才有获取token的能力 + if (clientTypeEnum.equals(ClientTypeEnum.H5) || clientTypeEnum.equals(ClientTypeEnum.WECHAT_MP)) { + + //缓存一下token + String token = cache.getString(CachePrefix.WECHAT_CGI_ACCESS_TOKEN.getPrefix() + clientTypeEnum.name()); + if (token != null) { + return token; + } + //获取微信配置 + Setting setting = settingService.get(SettingEnum.WECHAT_CONNECT.name()); + if (setting == null) { + log.error("获取token客户端异常" + clientTypeEnum.name() + ",客户端未配置微信参数,请前往后台=》联合登陆,进行对应微信配置"); + return null; + } + //获取配置,获取对应的配置 + WechatConnectSetting wechatConnectSetting = new Gson().fromJson(setting.getSettingValue(), WechatConnectSetting.class); + WechatConnectSettingItem item = null; + for (WechatConnectSettingItem wechatConnectSettingItem : wechatConnectSetting.getWechatConnectSettingItems()) { + if (wechatConnectSettingItem.getClientType().equals(clientTypeEnum.name())) { + item = wechatConnectSettingItem; + } + } + //微信h5配置与否 + if (item == null) { + return null; + } + //获取token + String content = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + + "&appid=" + item.getAppId() + "&secret=" + item.getAppSecret()); + + JSONObject object = new JSONObject(content); + log.info("token获取【" + clientTypeEnum.name() + "】返回" + object.toString()); + String accessToken = object.get("access_token").toString(); + cache.put(CachePrefix.WECHAT_CGI_ACCESS_TOKEN.getPrefix() + clientTypeEnum.name(), + object.getStr("access_token"), object.getLong("expires_in")); + return accessToken; + } else { + log.error("获取token客户端异常" + clientTypeEnum.name()); + return null; + } + } + + /** + * 获取某一平台等cgi token 用于业务调用,例如发送公众号消息 + * + * @param clientTypeEnum + * @return + */ + public String cgiJsApiTicket(ClientTypeEnum clientTypeEnum) { + //缓存一下token + String token = cache.getString(CachePrefix.WECHAT_JS_API_TOKEN.getPrefix() + clientTypeEnum.name()); + if (token != null) { + return token; + } + String accessToken = this.cgiAccessToken(clientTypeEnum); + try { + String content = new HttpUtils().get("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi"); + + JSONObject object = new JSONObject(content); + String ticket = object.getStr("ticket"); + Long expires = object.getLong("expires_in"); + cache.put(CachePrefix.WECHAT_JS_API_TOKEN.getPrefix() + clientTypeEnum.name(), ticket, expires); + return ticket; + } catch (Exception e) { + log.error("微信JsApi签名异常", e); + throw new ServiceException("微信JsApi签名异常"); + } + + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/message/util/WechatMessageData.java b/framework/src/main/java/cn/lili/modules/message/util/WechatMessageData.java new file mode 100644 index 00000000..6d5d9a90 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/util/WechatMessageData.java @@ -0,0 +1,98 @@ +package cn.lili.modules.message.util; + +import cn.hutool.json.JSONUtil; +import lombok.Data; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 微信公众号消息 数据模型 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/12/11 09:52 + */ +@Data +public class WechatMessageData { + + /** + * 抬头文字 + */ + private String first; + + /** + * 备注文字 + */ + private String remark; + + /** + * 消息内容 + */ + private List messageData; + /** + * 小程序消息内容 + */ + private Map mpMessageData; + + /** + * kids + */ + private List kids; + + + /** + * 创建data数据 + * + * @return + */ + public String createData() { + + Map> dataMap = new LinkedHashMap<>(); + + //拼接开头 + dataMap.put("first", this.createValue(first)); + + //拼接关键字 + for (int i = 0; i < messageData.size(); i++) { + dataMap.put("keyword" + (i + 1), createValue(this.messageData.get(i))); + } + //拼接备注 + dataMap.put("remark", createValue(this.remark)); + + return JSONUtil.toJsonStr(dataMap); + } + + + /** + * 创建data数据 + * + * @return + */ + public Map> createMPData() { + + LinkedHashMap> dataMap = new LinkedHashMap<>(); + + for (String key : mpMessageData.keySet()) { + dataMap.put(key, createValue(mpMessageData.get(key))); + } + return dataMap; + } + + /** + * 创建统一格式的map + * + * @param msg + * @return + */ + private Map createValue(String msg) { + Map map = new HashMap<>(); + map.put("value", msg); + return map; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/message/util/WechatMessageUtil.java b/framework/src/main/java/cn/lili/modules/message/util/WechatMessageUtil.java new file mode 100644 index 00000000..ad3e8d87 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/util/WechatMessageUtil.java @@ -0,0 +1,294 @@ +package cn.lili.modules.message.util; + +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.connect.entity.Connect; +import cn.lili.modules.connect.entity.enums.ConnectEnum; +import cn.lili.modules.connect.service.ConnectService; +import cn.lili.modules.message.entity.dos.WechatMPMessage; +import cn.lili.modules.message.entity.dos.WechatMessage; +import cn.lili.modules.message.entity.enums.WechatMessageItemEnums; +import cn.lili.modules.message.service.WechatMPMessageService; +import cn.lili.modules.message.service.WechatMessageService; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.system.utils.HttpUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * 微信消息 + * + * @author Chopper + * @version v1.0 + * 2020-12-10 19:12 + */ +@Slf4j +@Component +public class WechatMessageUtil { + + @Autowired + private ConnectService connectService; + @Autowired + private OrderService orderService; + @Autowired + private OrderItemService orderItemService; + @Autowired + private WechatAccessTokenUtil wechatAccessTokenUtil; + @Autowired + private WechatMessageService wechatMessageService; + + @Autowired + private WechatMPMessageService wechatMPMessageService; + + public void sendWechatMessage(String sn) { + try { + this.wechatMessage(sn); + } catch (Exception e) { + log.error("微信公众号消息异常:", e); + } + try { + this.wechatMpMessage(sn); + } catch (Exception e) { + log.error("小程序消息订阅异常:", e); + } + + } + + /** + * 发送微信消息 + * + * @param sn + */ + public void wechatMessage(String sn) { + + Order order = orderService.getBySn(sn); + //获取微信消息 + LambdaQueryWrapper wechatMessageQueryWrapper = new LambdaQueryWrapper(); + wechatMessageQueryWrapper.eq(WechatMessage::getOrderStatus, order.getOrderStatus()); + WechatMessage wechatMessage = wechatMessageService.getOne(wechatMessageQueryWrapper); + if (wechatMessage == null) { + return; + } + + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("user_id", order.getMemberId()); + queryWrapper.eq("union_type", ConnectEnum.WECHAT_OPEN_ID.name()); + + Connect connect = connectService.getOne(queryWrapper); + if (connect == null) { + return; + } + + log.info("微信消息发送消息:", order.getMemberId() + "-" + sn); + //获取token + String token = wechatAccessTokenUtil.cgiAccessToken(ClientTypeEnum.H5); + + //发送url + String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + token; + + Map map = new HashMap<>(); + // 用户id + map.put("touser", connect.getUnionId()); + //模版id + map.put("template_id", wechatMessage.getCode()); + //模版中所需数据 + String postParams = createData(order, wechatMessage); + map.put("data", postParams); + + log.info("参数内容:" + JSONUtil.toJsonStr(map)); + String content = HttpUtils.doPostWithJson(url, map); + JSONObject json = new JSONObject(content); + log.info("微信消息发送结果:" + content); + String errorMessage = json.getStr("errmsg"); + String errcode = json.getStr("errcode"); + //发送失败 + if (!"0".equals(errcode)) { + log.error("消息发送失败:" + errorMessage); + log.error("消息发送请求token:" + token); + log.error("消息发送请求:" + postParams); + } + } + + /** + * 发送微信消息 + * + * @param sn + */ + public void wechatMpMessage(String sn) { + + log.error("发送消息订阅"); + Order order = orderService.getBySn(sn); + //获取微信消息 + LambdaQueryWrapper wechatMPMessageQueryWrapper = new LambdaQueryWrapper(); + wechatMPMessageQueryWrapper.eq(WechatMPMessage::getOrderStatus, order.getOrderStatus()); + WechatMPMessage wechatMPMessage = wechatMPMessageService.getOne(wechatMPMessageQueryWrapper); + if (wechatMPMessage == null) { + log.error("未配置微信消息订阅"); + return; + } + + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("user_id", order.getMemberId()); + queryWrapper.eq("union_type", ConnectEnum.WECHAT_MP_OPEN_ID.name()); + + Connect connect = connectService.getOne(queryWrapper); + if (connect == null) { + return; + } + + log.info("微信消息订阅消息发送:", order.getMemberId() + "-" + sn); + //获取token + String token = wechatAccessTokenUtil.cgiAccessToken(ClientTypeEnum.WECHAT_MP); + + //发送url + String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + token; + + Map map = new HashMap<>(); + // 用户id + map.put("touser", connect.getUnionId()); + //模版id + map.put("template_id", wechatMPMessage.getCode()); + //模版中所需数据 + Map> postParams = createData(order, wechatMPMessage); + map.put("data", postParams); + map.put("page", "pages/order/orderDetail?sn="+order.getSn()); + log.info("参数内容:" + JSONUtil.toJsonStr(map)); + String content = null; + try { + content = HttpUtil.post(url, JSONUtil.toJsonStr(map)); + } catch (Exception e) { + e.printStackTrace(); + } + JSONObject json = new JSONObject(content); + log.info("微信消息发送结果:" + content); + String errorMessage = json.getStr("errmsg"); + String errcode = json.getStr("errcode"); + //发送失败 + if (!"0".equals(errcode)) { + log.error("消息发送失败:" + errorMessage); + log.error("消息发送请求token:" + token); + log.error("消息发送请求:" + postParams); + } + } + + /** + * 构造数据中所需的内容 + * + * @param order + * @param wechatMessage + * @return + */ + private String createData(Order order, WechatMessage wechatMessage) { + WechatMessageData wechatMessageData = new WechatMessageData(); + wechatMessageData.setFirst(wechatMessage.getFirst()); + wechatMessageData.setRemark(wechatMessage.getRemark()); + String[] paramArray = wechatMessage.getKeywords().split(","); + LinkedList params = new LinkedList(); + + for (String param : paramArray) { + WechatMessageItemEnums wechatMessageItemEnums = WechatMessageItemEnums.valueOf(param); + //初始化参数内容 + String val = getParams(wechatMessageItemEnums, order); + params.add(val); + } + wechatMessageData.setMessageData(params); + return wechatMessageData.createData(); + } + + /** + * 构造数据中所需的内容 + * + * @param order + * @param wechatMPMessage + * @return + */ + private Map> createData(Order order, WechatMPMessage wechatMPMessage) { + WechatMessageData wechatMessageData = new WechatMessageData(); + List paramArray = JSONUtil.toList(wechatMPMessage.getKeywords(), String.class); + List texts = JSONUtil.toList(wechatMPMessage.getKeywordsText(), String.class); + Map params = new LinkedHashMap<>(); + for (int i = 0; i < paramArray.size(); i++) { + WechatMessageItemEnums wechatMessageItemEnums = WechatMessageItemEnums.valueOf(paramArray.get(i)); + //初始化参数内容 + String val = getParams(wechatMessageItemEnums, order); + val = StringUtils.subStringLength(val, 20); + params.put(texts.get(i), val); + } + wechatMessageData.setMpMessageData(params); + return wechatMessageData.createMPData(); + } + + /** + * 获取具体参数 + * + * @param itemEnums + * @param order + * @return + */ + private String getParams(WechatMessageItemEnums itemEnums, Order order) { + switch (itemEnums) { + case PRICE: + return order.getPriceDetailDTO().getFlowPrice().toString(); + case ORDER_SN: + return order.getSn(); + case SHOP_NAME: + return order.getStoreName(); + case GOODS_INFO: + List orderItems = orderItemService.getByOrderSn(order.getSn()); + StringBuffer stringBuffer = new StringBuffer(); + orderItems.forEach(orderItem -> { + stringBuffer.append(orderItem.getGoodsName() + "*" + orderItem.getNum() + " "); + }); + return stringBuffer.toString(); + case MEMBER_NAME: + return order.getMemberName(); + case LOGISTICS_NO: + return order.getLogisticsNo(); + case LOGISTICS_NAME: + return order.getLogisticsName(); + case LOGISTICS_TIME: + return DateUtil.toString(order.getLogisticsTime(), DateUtil.STANDARD_FORMAT); + + } + return ""; + } + + /** + * 如果返回信息有错误 + * + * @param jsonObject + */ + public static void wechatHandler(JSONObject jsonObject) { + if (jsonObject.containsKey("errmsg")) { + if (jsonObject.getStr("errmsg").equals("ok")) { + return; + } + throw new ServiceException("微信接口异常,请联系管理员:错误码" + jsonObject.get("errcode") + "," + jsonObject.getStr("errmsg")); + } + } + + /** + * 如果返回信息有错误 + * + * @param string + */ + public static String wechatHandler(String string) { + JSONObject jsonObject = new JSONObject(); + wechatHandler(jsonObject); + return string; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/message/util/WechatMpCodeUtil.java b/framework/src/main/java/cn/lili/modules/message/util/WechatMpCodeUtil.java new file mode 100644 index 00000000..09600fda --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/message/util/WechatMpCodeUtil.java @@ -0,0 +1,196 @@ +package cn.lili.modules.message.util; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.message.entity.dos.ShortLink; +import cn.lili.modules.message.service.ShortLinkService; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.protocol.HTTP; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 微信API交互token + * + * @author Chopper + * @version v1.0 + * 2020-12-10 19:25 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WechatMpCodeUtil { + + private static String UN_LIMIT_API = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token="; + private static String CREATE_QR_CODE = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token="; + + + @Autowired + private WechatAccessTokenUtil wechatAccessTokenUtil; + + @Autowired + private ShortLinkService shortLinkService; + + /** + * 生成分享二维码 + * + * @param path 路径 + * @return + */ + public String createQrCode(String path) { + try { + String accessToken = wechatAccessTokenUtil.cgiAccessToken(ClientTypeEnum.WECHAT_MP); + Map params = new HashMap<>(); + params.put("path", path); + params.put("width", "280"); + + // ======================================================================// + // 执行URL Post调用 + // ======================================================================// + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(CREATE_QR_CODE + accessToken); + httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json"); + // 必须是json模式的 post + String body = JSON.toJSONString(params); + StringEntity entity = new StringEntity(body); + entity.setContentType("image/png"); + httpPost.setEntity(entity); + HttpResponse httpResponse = httpClient.execute(httpPost); + HttpEntity httpEntity = httpResponse.getEntity(); + // ======================================================================// + // 处理HTTP返回结果 + // ======================================================================// + InputStream contentStream = httpEntity.getContent(); + byte[] bytes = toByteArray(contentStream); + contentStream.read(bytes); + // 返回内容 + return Base64.getEncoder().encodeToString(bytes); + } catch (Exception e) { + log.error("生成二维码错误:", e); + throw new ServiceException(ResultCode.ERROR); + } + + } + + /** + * 生成分享二维码 + * + * @param page + * @param scene + * @return + */ + public String createCode(String page, String scene) { + try { + + //短链接存储 + ShortLink shortLink = new ShortLink(); + //已经保存过则不再保存 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + queryWrapper.eq(ShortLink::getOriginalParams, scene); + List shortLinks = shortLinkService.list(queryWrapper); + if (shortLinks.size() > 0) { + shortLink = shortLinks.get(0); + } else { + shortLink.setOriginalParams(scene); + shortLinkService.save(shortLink); + shortLink = shortLinkService.getOne(queryWrapper); + } + String accessToken = wechatAccessTokenUtil.cgiAccessToken(ClientTypeEnum.WECHAT_MP); + Map params = new HashMap<>(); + params.put("page", page); + params.put("scene", shortLink.getId()); + params.put("width", "280"); + + // ======================================================================// + // 执行URL Post调用 + // ======================================================================// + CloseableHttpClient httpClient = HttpClients.createDefault(); + HttpPost httpPost = new HttpPost(UN_LIMIT_API + accessToken); + httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json"); + // 必须是json模式的 post + String body = JSON.toJSONString(params); + StringEntity entity = new StringEntity(body); + entity.setContentType("image/png"); + httpPost.setEntity(entity); + HttpResponse httpResponse = httpClient.execute(httpPost); + HttpEntity httpEntity = httpResponse.getEntity(); + // ======================================================================// + // 处理HTTP返回结果 + // ======================================================================// + InputStream contentStream = httpEntity.getContent(); + byte[] bytes = toByteArray(contentStream); + contentStream.read(bytes); + // 返回内容 + return Base64.getEncoder().encodeToString(bytes); + } catch (Exception e) { + log.error("生成二维码错误:", e); + throw new ServiceException(ResultCode.ERROR); + } + + } + + /** + * @param input + * @return + * @throws IOException + */ + @SneakyThrows + public static byte[] toByteArray(InputStream input) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + copy(input, output); + return output.toByteArray(); + } + + /** + * @param input + * @param output + * @return + * @throws IOException + */ + public static int copy(InputStream input, OutputStream output) throws IOException { + long count = copyLarge(input, output); + if (count > 2147483647L) { + return -1; + } + return (int) count; + } + + /** + * @param input + * @param output + * @return + * @throws IOException + */ + public static long copyLarge(InputStream input, OutputStream output) throws IOException { + byte[] buffer = new byte[4096]; + long count = 0L; + int n = 0; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/MemberCouponDTO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/MemberCouponDTO.java new file mode 100644 index 00000000..413956d5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/MemberCouponDTO.java @@ -0,0 +1,42 @@ +package cn.lili.modules.order.cart.entity.dto; + +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +/** + * 用于计算优惠券结算详情 + * + * @author Chopper + * @date 2020-03-25 2:30 下午 + */ +@Data +public class MemberCouponDTO implements Serializable { + + + private static final long serialVersionUID = 8276369124551043085L; + /** + * 在整比交易中时: key 为店铺id,value 为每个店铺跨店优惠 结算金额 + * 在购物车中时: key为sku id ,value为每个商品结算时的金额 + */ + private Map skuDetail; + + /** + * 优惠券详情 + */ + private MemberCoupon memberCoupon; + + public MemberCouponDTO() { + } + + public MemberCouponDTO(MemberCoupon memberCoupon) { + this.memberCoupon = memberCoupon; + } + + public MemberCouponDTO(Map skuDetail, MemberCoupon memberCoupon) { + this.skuDetail = skuDetail; + this.memberCoupon = memberCoupon; + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/StoreRemarkDTO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/StoreRemarkDTO.java new file mode 100644 index 00000000..f9d2d981 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/StoreRemarkDTO.java @@ -0,0 +1,24 @@ +package cn.lili.modules.order.cart.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 店铺备注 + * + * @author Chopper + * @date 2020-03-25 2:30 下午 + */ +@Data +public class StoreRemarkDTO implements Serializable { + + private static final long serialVersionUID = -6793274046513576434L; + @ApiModelProperty(value = "商家id") + private String storeId; + + @ApiModelProperty(value = "备注") + private String remark; + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/TradeDTO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/TradeDTO.java new file mode 100644 index 00000000..fb9ae4e9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/dto/TradeDTO.java @@ -0,0 +1,134 @@ +package cn.lili.modules.order.cart.entity.dto; + +import cn.lili.modules.member.entity.dos.MemberAddress; +import cn.lili.modules.order.cart.entity.enums.SuperpositionPromotionEnum; +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import cn.lili.modules.order.order.entity.vo.OrderVO; +import cn.lili.modules.order.order.entity.vo.ReceiptVO; +import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import cn.lili.modules.order.cart.entity.vo.PriceDetailVO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 购物车视图 + * + * @author Chopper + * @date 2020-03-25 2:30 下午 + */ +@Data +public class TradeDTO implements Serializable { + + private static final long serialVersionUID = -3137165707807057810L; + + @ApiModelProperty(value = "sn") + private String sn; + + @ApiModelProperty(value = "是否为其他订单下的订单,如果是则为依赖订单的sn,否则为空") + private String parentOrderSn; + + @ApiModelProperty(value = "购物车列表") + private List cartList; + + @ApiModelProperty(value = "整笔交易中所有的规格商品") + private List skuList; + + @ApiModelProperty(value = "购物车车计算后的总价") + private PriceDetailVO priceDetailVO; + + @ApiModelProperty(value = "购物车车计算后的总价") + private PriceDetailDTO priceDetailDTO; + + @ApiModelProperty(value = "发票信息") + private ReceiptVO receiptVO; + + @ApiModelProperty(value = "是否需要发票") + private Boolean needReceipt; + + /** + * 购物车类型 + */ + private CartTypeEnum cartTypeEnum; + + /** + * key 为商家id + * value 为商家优惠券 + * 商家优惠券 + */ + private Map storeCoupons; + + /** + * key 为商家id + * value 为商家优惠券 + * 商家优惠券 + */ + private List storeRemark; + + /** + * sku促销连线 包含满优惠 + *

+ * KEY值为 sku_id+"_"+SuperpositionPromotionEnum + * VALUE值为 对应的活动ID + * + * @see SuperpositionPromotionEnum + */ + private Map skuPromotionDetail; + + /** + * 使用平台优惠券,一笔订单只能使用一个平台优惠券 + */ + private MemberCouponDTO platformCoupon; + + /** + * 收货地址 + */ + private MemberAddress memberAddress; + + /** + * 客户端类型 + */ + private String clientType; + + /** + * 买家名称 + */ + private String memberName; + + /** + * 买家id + */ + private String memberId; + + /** + * 分销商id + */ + private String distributionId; + + /** + * 订单vo + */ + private List orderVO; + + public TradeDTO(CartTypeEnum cartTypeEnum) { + this.skuList = new ArrayList<>(); + this.cartList = new ArrayList<>(); + this.skuPromotionDetail = new HashMap<>(); + this.storeCoupons = new HashMap<>(); + this.storeCoupons = new HashMap<>(); + this.priceDetailDTO = new PriceDetailDTO(); + this.cartTypeEnum = cartTypeEnum; + this.needReceipt = false; + } + + public TradeDTO() { + this(CartTypeEnum.CART); + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/CartTypeEnum.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/CartTypeEnum.java new file mode 100644 index 00000000..054f5e1c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/CartTypeEnum.java @@ -0,0 +1,28 @@ +package cn.lili.modules.order.cart.entity.enums; + +/** + * 购物车类型 + * + * @author Chopper + * @date 2020-03-25 2:30 下午 + */ +public enum CartTypeEnum { + + /** + * 购物车 + */ + CART, + /** + * 立即购买 + */ + BUY_NOW, + /** + * 拼团 + */ + PINTUAN, + /** + * 积分 + */ + POINTS, + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/DeliveryMethodEnum.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/DeliveryMethodEnum.java new file mode 100644 index 00000000..01c58035 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/DeliveryMethodEnum.java @@ -0,0 +1,25 @@ +package cn.lili.modules.order.cart.entity.enums; + +/** + * 配送方式 + * + * @author paulG + * @date 2020-03-25 2:30 下午 + **/ +public enum DeliveryMethodEnum { + + SELF_PICK_UP("自提"), + LOCAL_TOWN_DELIVERY("同城配送"), + LOGISTICS("物流"); + + private final String description; + + DeliveryMethodEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/SuperpositionPromotionEnum.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/SuperpositionPromotionEnum.java new file mode 100644 index 00000000..9c7f793e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/SuperpositionPromotionEnum.java @@ -0,0 +1,28 @@ +package cn.lili.modules.order.cart.entity.enums; + +/** + * 活动叠加 + * + * @author Chopper + * @date 2020-03-25 2:30 下午 + */ +public enum SuperpositionPromotionEnum { + + /** + * 商品促销放在商品属性,这里只负责可叠加的其他促销 + * 叠加促销枚举,每一个商品,以下每个参数都只能参加一个 + */ + SELLER_COUPON("店铺优惠券"), + PLATFORM_COUPON("平台优惠券"), + FULL_DISCOUNT("满优惠"); + + private final String description; + + SuperpositionPromotionEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/TradeCacheEnum.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/TradeCacheEnum.java new file mode 100644 index 00000000..6a98ed64 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/enums/TradeCacheEnum.java @@ -0,0 +1,30 @@ +package cn.lili.modules.order.cart.entity.enums; + +/** + * 交易缓存枚举 + * + * @author Chopper + * @date 2020-03-25 2:30 下午 + */ +public enum TradeCacheEnum { + + //================交易================= + + /** + * 拼团 + */ + PINTUAN, + /** + * 购物车原始数据 + */ + CART_DATA, + /** + * 立即购买购物车原始数据 + */ + BUY_NOW_CART_DATA; + + + public String getPrefix() { + return "{" + this.name() + "}_"; + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/CartBase.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/CartBase.java new file mode 100644 index 00000000..50eea20a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/CartBase.java @@ -0,0 +1,42 @@ +package cn.lili.modules.order.cart.entity.vo; + +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 购物车基础 + * + * @author Chopper + * @date 2020-03-24 10:33 上午 + */ +@Data +public class CartBase implements Serializable { + + private static final long serialVersionUID = -5172752506920017597L; + + @ApiModelProperty(value = "卖家id") + private String storeId; + + @ApiModelProperty(value = "卖家姓名") + private String storeName; + + @ApiModelProperty(value = "此商品价格流水计算") + private PriceDetailDTO priceDetailDTO; + + @ApiModelProperty(value = "此商品价格展示") + private PriceDetailVO priceDetailVO; + + public CartBase() { + priceDetailDTO = new PriceDetailDTO(); + } + + public PriceDetailVO getPriceDetailVO() { + if (this.priceDetailDTO != null) { + return new PriceDetailVO(priceDetailDTO); + } + return new PriceDetailVO(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/CartSkuVO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/CartSkuVO.java new file mode 100644 index 00000000..29d2be87 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/CartSkuVO.java @@ -0,0 +1,103 @@ +package cn.lili.modules.order.cart.entity.vo; + +import cn.lili.modules.distribution.entity.dos.DistributionGoods; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 购物车中的产品 + * + * @author Chopper + * @date 2020-03-24 10:33 上午 + */ +@Data +@NoArgsConstructor +public class CartSkuVO extends CartBase implements Serializable { + + + private static final long serialVersionUID = -894598033321906974L; + + + private String sn; + /** + * 对应的sku DO + */ + private GoodsSku goodsSku; + + /** + * 分销描述 + */ + private DistributionGoods distributionGoods; + + @ApiModelProperty(value = "购买数量") + private Integer num; + + @ApiModelProperty(value = "购买时的成交价") + private Double purchasePrice; + + @ApiModelProperty(value = "小记") + private Double subTotal; + /** + * 是否选中,要去结算 0:未选中 1:已选中,默认 + */ + @ApiModelProperty(value = "是否选中,要去结算") + private Boolean checked; + + + @ApiModelProperty(value = "是否免运费") + private Boolean isFreeFreight; + + @ApiModelProperty(value = "积分购买 积分数量") + private Integer point; + + @ApiModelProperty(value = "是否失效 ") + private Boolean invalid; + + @ApiModelProperty(value = "购物车商品错误消息") + private String errorMessage; + + @ApiModelProperty(value = "是否可配送") + private Boolean isShip; + + @ApiModelProperty(value = + "拼团id 如果是拼团购买 此值为拼团活动id," + + "当pintuanId为空,则表示普通购买(或者拼团商品,单独购买)") + private String pintuanId; + + @ApiModelProperty(value = "可参与的单品活动") + private List promotions; + + @ApiModelProperty(value = "参与促销活动更新时间(一天更新一次) 例如时间为:2020-01-01 00:00:01") + private Date updatePromotionTime; + + /** + * @see CartTypeEnum + */ + @ApiModelProperty(value = "购物车类型") + private CartTypeEnum cartType; + + /** + * 在构造器里初始化促销列表,规格列表 + */ + public CartSkuVO(GoodsSku goodsSku) { + this.goodsSku = goodsSku; + this.checked = true; + this.invalid = false; + //默认时间为0,让系统为此商品更新缓存 + this.updatePromotionTime = new Date(0); + this.errorMessage = ""; + this.isShip = true; + this.purchasePrice = goodsSku.getIsPromotion() != null && goodsSku.getIsPromotion() ? goodsSku.getPromotionPrice() : goodsSku.getPrice(); + this.isFreeFreight = false; + this.setStoreId(goodsSku.getStoreId()); + this.setStoreName(goodsSku.getStoreName()); + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/CartVO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/CartVO.java new file mode 100644 index 00000000..f93fd268 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/CartVO.java @@ -0,0 +1,99 @@ +package cn.lili.modules.order.cart.entity.vo; + +import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum; +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + + +/** + * 购物车展示VO + * + * @author Chopper + * @date 2020-03-24 10:33 上午 + */ +@Data +@ApiModel(description = "购物车") +public class CartVO extends CartBase implements Serializable { + + private static final long serialVersionUID = -5651775413457562422L; + + @ApiModelProperty(value = "购物车中的产品列表") + private List skuList; + + @ApiModelProperty(value = "sn") + private String sn; + + @ApiModelProperty(value = "购物车页展示时,店铺内的商品是否全选状态.1为店铺商品全选状态,0位非全选") + private Boolean checked; + + @ApiModelProperty(value = "满优惠活动") + private FullDiscountVO fullDiscount; + + @ApiModelProperty(value = "满优惠促销的商品") + private List fullDiscountSkuIds; + + @ApiModelProperty(value = "是否满优惠") + private Boolean isFull; + + @ApiModelProperty(value = "使用的优惠券列表") + private List couponList; + + + @ApiModelProperty(value = "赠品列表") + private List giftList; + + @ApiModelProperty(value = "赠送优惠券列表") + private List giftCouponList; + + @ApiModelProperty(value = "赠送积分") + private Integer giftPoint; + + @ApiModelProperty(value = "重量") + private Double weight; + + @ApiModelProperty(value = "购物车商品数量") + private Integer goodsNum; + + @ApiModelProperty(value = "购物车商品数量") + private String remark; + + /** + * @see DeliveryMethodEnum + */ + @ApiModelProperty(value = "配送方式") + private String deliveryMethod; + + @ApiModelProperty(value = "已参与的的促销活动提示,直接展示给客户") + private String promotionNotice; + + public CartVO() { + } + + public CartVO(CartSkuVO cartSkuVO) { + this.setStoreId(cartSkuVO.getStoreId()); + this.setStoreName(cartSkuVO.getStoreName()); + this.setSkuList(new ArrayList<>()); + this.setCouponList(new ArrayList<>()); + this.setGiftList(new ArrayList<>()); + this.setGiftCouponList(new ArrayList<>()); + this.setChecked(false); + this.isFull = false; + this.weight = 0d; + this.giftPoint = 0; + this.remark = ""; + } + + public void add(Integer goodsNum) { + if (this.goodsNum == null) { + this.goodsNum = goodsNum; + } else { + this.goodsNum += goodsNum; + } + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/FullDiscountVO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/FullDiscountVO.java new file mode 100644 index 00000000..8422fdf3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/FullDiscountVO.java @@ -0,0 +1,40 @@ +package cn.lili.modules.order.cart.entity.vo; + +import cn.lili.modules.promotion.entity.dos.FullDiscount; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import lombok.Data; + +import java.util.List; + +/** + * 满额活动VO + * + * @author Chopper + * @date 2020-04-01 10:42 上午 + */ +@Data +public class FullDiscountVO extends FullDiscount { + + private static final long serialVersionUID = -2330552735874105354L; + /** + * 促销关联的商品 + */ + private List promotionGoodsList; + + private Integer number; + + public String notice() { + StringBuilder stringBuffer = new StringBuilder(); + if (Boolean.TRUE.equals(this.getIsFreeFreight())) { + stringBuffer.append("免运费 "); + } + if (Boolean.TRUE.equals(this.getIsFullMinus())) { + stringBuffer.append("减").append(this.getFullMinus()).append("元 "); + } + if (Boolean.TRUE.equals(this.getIsFullRate())) { + stringBuffer.append("打").append(this.getFullRate()).append("折 "); + } + return stringBuffer.toString(); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/GoodsPromotionVO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/GoodsPromotionVO.java new file mode 100644 index 00000000..197faede --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/GoodsPromotionVO.java @@ -0,0 +1,56 @@ +package cn.lili.modules.order.cart.entity.vo; + +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 商品促销VO + * + * @author Chopper + * @date 2020-04-01 10:42 上午 + */ +@Data +@ApiModel(description = "购物车中") +public class GoodsPromotionVO implements Serializable { + + + private static final long serialVersionUID = 1622051257060817414L; + @ApiModelProperty(value = "活动开始时间") + private Date startTime; + + @ApiModelProperty(value = "活动结束时间") + private Date endTime; + + @ApiModelProperty(value = "活动id") + private String promotionId; + + /** + * @see cn.lili.modules.promotion.entity.enums.PromotionTypeEnum + */ + @ApiModelProperty(value = "活动工具类型") + private String promotionType; + + @ApiModelProperty(value = "活动名称") + private String title; + + + @ApiModelProperty(value = "限购数量") + private Integer limitNum; + + public GoodsPromotionVO(PromotionGoods promotionGoods) { + this.startTime = promotionGoods.getStartTime(); + this.endTime = promotionGoods.getEndTime(); + this.promotionId = promotionGoods.getPromotionId(); + this.setPromotionType(promotionGoods.getPromotionType()); + this.setLimitNum(promotionGoods.getLimitNum()); + } + + public GoodsPromotionVO() { + + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/PriceDetailVO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/PriceDetailVO.java new file mode 100644 index 00000000..2dbb4082 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/PriceDetailVO.java @@ -0,0 +1,50 @@ +package cn.lili.modules.order.cart.entity.vo; + +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 订单价格详情 + * + * @author Chopper + * @date 2020-04-01 10:42 上午 + */ +@Data +public class PriceDetailVO implements Serializable { + + private static final long serialVersionUID = -960537582096338500L; + + @ApiModelProperty(value = "商品原价") + private Double originalPrice; + + @ApiModelProperty(value = "配送费") + private Double freight; + + @ApiModelProperty(value = "优惠金额") + private Double discountPrice; + + @ApiModelProperty(value = "支付积分") + private Integer payPoint; + + @ApiModelProperty(value = "最终成交金额") + private Double finalePrice; + + + /** + * 构造器,初始化默认值 + */ + public PriceDetailVO(PriceDetailDTO dto) { + this.freight = dto.getFreightPrice(); + this.finalePrice = dto.getFlowPrice(); + this.discountPrice = dto.getDiscountPrice(); + this.payPoint = dto.getPayPoint(); + this.originalPrice = dto.getGoodsPrice(); + } + + public PriceDetailVO(){ + + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/TradeParams.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/TradeParams.java new file mode 100644 index 00000000..29c3cd9c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/TradeParams.java @@ -0,0 +1,37 @@ +package cn.lili.modules.order.cart.entity.vo; + +import cn.lili.modules.order.cart.entity.dto.StoreRemarkDTO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 交易参数 + * + * @author paulG + * @date 2021/2/23 + **/ +@Data +public class TradeParams implements Serializable { + + private static final long serialVersionUID = -8383072817737513063L; + + @ApiModelProperty(value = "购物车购买:CART/立即购买:BUY_NOW/拼团购买:PINTUAN / 积分购买:POINT") + private String way; + + /** + * @see cn.lili.modules.base.entity.enums.ClientTypeEnum + */ + @ApiModelProperty(value = "客户端:H5/移动端 PC/PC端,WECHAT_MP/小程序端,APP/移动应用端") + private String client; + + @ApiModelProperty(value = "店铺备注") + private List remark; + + @ApiModelProperty(value = "是否为其他订单下的订单,如果是则为依赖订单的sn,否则为空") + private String parentOrderSn; + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/TradeVO.java b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/TradeVO.java new file mode 100644 index 00000000..b57806c4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/entity/vo/TradeVO.java @@ -0,0 +1,38 @@ +package cn.lili.modules.order.cart.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 整比交易对象 + * + * @author Chopper + * @date 2020-04-01 10:42 上午 + */ +@Data +public class TradeVO implements Serializable { + + private static final long serialVersionUID = -4563542542090139404L; + + /** + * 购物车列表 + */ + @ApiModelProperty(value = "购物车列表") + private List cartList; + + /** + * 购物车计算后的总价 + */ + @ApiModelProperty(value = "购物车车计算后的总价") + private PriceDetailVO priceDetailVO; + + public TradeVO(List cartList, PriceDetailVO priceDetailVO) { + this.cartList = cartList; + this.priceDetailVO = priceDetailVO; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/CartRenderStep.java b/framework/src/main/java/cn/lili/modules/order/cart/render/CartRenderStep.java new file mode 100644 index 00000000..1fcccf63 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/CartRenderStep.java @@ -0,0 +1,21 @@ +package cn.lili.modules.order.cart.render; + +import cn.lili.modules.order.cart.entity.dto.TradeDTO; + +/** + * 购物车渲染 + * + * @author Chopper + * @date 2020-04-01 10:27 上午 + */ +public interface CartRenderStep { + + + /** + * 渲染一笔交易 + * 0-> 校验商品 1-》 满优惠渲染 2->渲染优惠 3->优惠券渲染 4->计算运费 5->计算价格 6->分销渲染 7->其他渲染 + * + * @param tradeDTO + */ + void render(TradeDTO tradeDTO); +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/TradeBuilder.java b/framework/src/main/java/cn/lili/modules/order/cart/render/TradeBuilder.java new file mode 100644 index 00000000..19429c73 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/TradeBuilder.java @@ -0,0 +1,120 @@ +package cn.lili.modules.order.cart.render; + +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import cn.lili.modules.order.order.entity.dos.Trade; +import cn.lili.modules.order.order.service.TradeService; +import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; +import cn.lili.modules.order.cart.service.CartService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * TradeBuilder + * + * @author Chopper + * @date2020-04-01 9:47 下午 + */ +@Service +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class TradeBuilder { + //购物车渲染 + private final List cartRenderSteps; + //交易 + private final TradeService tradeService; + + /** + * 渲染整比交易 + */ + int[] defaultRender = {0, 1, 2, 4, 5, 6, 7}; + + /** + * 购物车购物车渲染 + */ + int[] cartRender = {0, 1, 2, 5}; + + /** + * 0-> 校验商品 1-》 满优惠渲染 2->渲染优惠 3->优惠券渲染 4->计算运费 5->计算价格 6->分销渲染 7->扩展操作 + */ + private CartService cartService; + + @Autowired + public void setCartService(CartService cartService) { + this.cartService = cartService; + } + + /** + * 构造购物车 + * + * @param checkedWay 购物车类型 + * @return 购物车展示信息 + */ + public TradeDTO buildCart(CartTypeEnum checkedWay) { + TradeDTO tradeDTO = cartService.readDTO(checkedWay); + + //购物车需要将交易中的优惠券取消掉 + if (checkedWay.equals(CartTypeEnum.CART)) { + tradeDTO.setStoreCoupons(null); + tradeDTO.setPlatformCoupon(null); + } + + //按照计划进行渲染 + for (int index : cartRender) { + try { + cartRenderSteps.get(index).render(tradeDTO); + } catch (Exception e) { + log.error("购物车渲染异常:", e); + } + } + return tradeDTO; + } + + /** + * 构造一笔交易 + * + * @param checkedWay 购物车类型 + * @return 购物车展示信息 + */ + public TradeDTO buildTrade(CartTypeEnum checkedWay) { + TradeDTO tradeDTO = cartService.readDTO(checkedWay); + List collect = tradeDTO.getSkuList().parallelStream().filter(i -> Boolean.TRUE.equals(i.getChecked())).collect(Collectors.toList()); + if (checkedWay.equals(CartTypeEnum.PINTUAN)) { + for (CartSkuVO cartSkuVO : collect) { + cartSkuVO.setPintuanId(""); + } + } + tradeDTO.setSkuList(collect); + //按照计划进行渲染 + for (int index : defaultRender) { + cartRenderSteps.get(index).render(tradeDTO); + } + List cartVOList = new ArrayList<>(); + for (CartVO i : tradeDTO.getCartList()) { + i.setSkuList(i.getSkuList().stream().filter(j -> Boolean.TRUE.equals(j.getChecked())).collect(Collectors.toList())); + cartVOList.add(i); + } + tradeDTO.setCartList(cartVOList); + return tradeDTO; + } + + /** + * 创建一笔交易 + * + * @param checkedWay 购物车类型 + * @param parentOrderSn 是否为其他订单下的订单,如果是则为依赖订单的sn,否则为空 + * @return 交易信息 + */ + public Trade createTrade(CartTypeEnum checkedWay, String parentOrderSn) { + TradeDTO tradeDTO = this.buildTrade(checkedWay); + tradeDTO.setParentOrderSn(parentOrderSn); + return tradeService.createTrade(tradeDTO); + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CartPriceRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CartPriceRender.java new file mode 100644 index 00000000..91286e04 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CartPriceRender.java @@ -0,0 +1,139 @@ +package cn.lili.modules.order.cart.render.impl; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.goods.service.CategoryService; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import cn.lili.modules.order.cart.render.CartRenderStep; +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 购物车渲染,将购物车中的各个商品,拆分到每个商家,形成购物车VO + * + * @author Chopper + * @see CartVO + */ +@Order(5) +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CartPriceRender implements CartRenderStep { + + + //商品分类 + private final CategoryService categoryService; + + @Override + public void render(TradeDTO tradeDTO) { + // 构造cartVO + this.buildCart(tradeDTO); + this.buildCartPrice(tradeDTO); + this.buildTradePrice(tradeDTO); + + } + + /** + * 购物车价格 + * + * @param tradeDTO 购物车展示信息 + */ + void buildCart(TradeDTO tradeDTO) { + for (CartVO cart : tradeDTO.getCartList()) { + for (CartSkuVO sku : cart.getSkuList()) { + if (Boolean.FALSE.equals(sku.getChecked())) { + continue; + } + cart.setGoodsNum(sku.getNum()); + if (cart.getStoreId().equals(sku.getStoreId()) && !cart.getSkuList().contains(sku)) { + cart.getSkuList().add(sku); + } + } + } + } + + /** + * 购物车价格 + * + * @param tradeDTO 购物车展示信息 + */ + void buildCartPrice(TradeDTO tradeDTO) { + List cartSkuVOList = tradeDTO.getSkuList(); + //购物车列表 + List cartVOS = tradeDTO.getCartList(); + + // key store id + // value 商品列表 + Map> map = new HashMap<>(); + for (CartSkuVO cartSkuVO : cartSkuVOList) { + // 如果存在商家id + if (map.containsKey(cartSkuVO.getGoodsSku().getStoreId())) { + List list = map.get(cartSkuVO.getGoodsSku().getStoreId()); + list.add(cartSkuVO); + } else { + List list = new ArrayList<>(); + list.add(cartSkuVO); + map.put(cartSkuVO.getGoodsSku().getStoreId(), list); + } + } + + //计算购物车价格 + for (CartVO cart : cartVOS) { + List cartSkuVOS = map.get(cart.getStoreId()); + List priceDetailDTOS = new ArrayList<>(); + if (Boolean.TRUE.equals(cart.getChecked())) { + //累加价格 + for (CartSkuVO cartSkuVO : cartSkuVOS) { + if (Boolean.TRUE.equals(cartSkuVO.getChecked())) { + PriceDetailDTO priceDetailDTO = cartSkuVO.getPriceDetailDTO(); + // 流水金额(入账 出帐金额) = goodsPrice + freight - discountPrice - couponPrice + double flowPrice = CurrencyUtil.sub(CurrencyUtil.add(priceDetailDTO.getGoodsPrice(), priceDetailDTO.getFreightPrice()), CurrencyUtil.add(priceDetailDTO.getDiscountPrice(), priceDetailDTO.getCouponPrice() != null ? priceDetailDTO.getCouponPrice() : 0)); + priceDetailDTO.setFlowPrice(flowPrice); + + // 最终结算金额 = flowPrice - platFormCommission - distributionCommission + double billPrice = CurrencyUtil.sub(CurrencyUtil.sub(flowPrice, priceDetailDTO.getPlatFormCommission()), priceDetailDTO.getDistributionCommission()); + priceDetailDTO.setBillPrice(billPrice); + + // 平台佣金 + String categoryId = cartSkuVO.getGoodsSku().getCategoryPath().substring( + cartSkuVO.getGoodsSku().getCategoryPath().lastIndexOf(",") + 1 + ); + if (CharSequenceUtil.isNotEmpty(categoryId)) { + Double platFormCommission = CurrencyUtil.div(CurrencyUtil.mul(flowPrice, categoryService.getById(categoryId).getCommissionRate()), 100); + priceDetailDTO.setPlatFormCommission(platFormCommission); + } + priceDetailDTOS.add(priceDetailDTO); + } + } + cart.setPriceDetailDTO(PriceDetailDTO.accumulationPriceDTO(priceDetailDTOS, cart.getPriceDetailDTO())); + } + } + } + + + /** + * 初始化购物车 + * + * @param tradeDTO 购物车展示信息 + */ + void buildTradePrice(TradeDTO tradeDTO) { + //购物车列表 + List cartVOS = tradeDTO.getCartList(); + + List priceDetailDTOS = new ArrayList<>(); + for (CartVO cart : cartVOS) { + priceDetailDTOS.add(cart.getPriceDetailDTO()); + } + tradeDTO.setPriceDetailDTO(PriceDetailDTO.accumulationPriceDTO(priceDetailDTOS, tradeDTO.getPriceDetailDTO())); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CartSnRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CartSnRender.java new file mode 100644 index 00000000..90ba8c93 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CartSnRender.java @@ -0,0 +1,38 @@ +package cn.lili.modules.order.cart.render.impl; + +import cn.lili.common.utils.SnowFlake; +import cn.lili.modules.order.cart.entity.dto.StoreRemarkDTO; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.render.CartRenderStep; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +/** + * sn 生成 + * + * @author Chopper + * @date 2020-07-02 14:47 + */ +@Order(7) +@Service +public class CartSnRender implements CartRenderStep { + + @Override + public void render(TradeDTO tradeDTO) { + + //生成各个sn + tradeDTO.setSn(SnowFlake.createStr("T")); + tradeDTO.getCartList().forEach(item -> { + //写入备注 + if (tradeDTO.getStoreRemark() != null) { + for (StoreRemarkDTO remark : tradeDTO.getStoreRemark()) { + if (item.getStoreId().equals(remark.getStoreId())) { + item.setRemark(remark.getRemark()); + } + } + } + item.setSn(SnowFlake.createStr("O")); + }); + + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CheckDataRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CheckDataRender.java new file mode 100644 index 00000000..ce35b594 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CheckDataRender.java @@ -0,0 +1,64 @@ +package cn.lili.modules.order.cart.render.impl; + +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.render.CartRenderStep; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +/** + * 商品有效性校验 + * + * @author Chopper + * @date 2020-07-02 14:47 + */ +@Order(0) +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CheckDataRender implements CartRenderStep { + + private final GoodsSkuService goodsSkuService; + + @Override + public void render(TradeDTO tradeDTO) { + for (CartSkuVO cartSkuVO : tradeDTO.getSkuList()) { + GoodsSku dataSku = goodsSkuService.getGoodsSkuByIdFromCache(cartSkuVO.getGoodsSku().getId()); + if (dataSku == null || dataSku.getUpdateTime().before(cartSkuVO.getGoodsSku().getUpdateTime())) { + //设置购物车未选中 + cartSkuVO.setChecked(false); + //设置购物车此sku商品已失效 + cartSkuVO.setInvalid(true); + //设置失效消息 + cartSkuVO.setErrorMessage("商品信息发生变化,已失效"); + continue; + } + if (!GoodsAuthEnum.PASS.name().equals(dataSku.getIsAuth()) || !GoodsStatusEnum.UPPER.name().equals(dataSku.getMarketEnable())) { + //设置购物车未选中 + cartSkuVO.setChecked(false); + //设置购物车此sku商品已失效 + cartSkuVO.setInvalid(true); + //设置失效消息 + cartSkuVO.setErrorMessage("商品已下架"); + continue; + } + //商品库存不足 + if (dataSku.getQuantity() <= 0) { + //设置购物车未选中 + cartSkuVO.setChecked(false); + //设置购物车此sku商品已失效 + cartSkuVO.setInvalid(true); + //设置失效消息 + cartSkuVO.setErrorMessage("商品库存不足"); + } + //写入初始价格 + cartSkuVO.getPriceDetailDTO().setGoodsPrice(CurrencyUtil.mul(cartSkuVO.getPurchasePrice(), cartSkuVO.getNum())); + } + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CouponRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CouponRender.java new file mode 100644 index 00000000..0ef09132 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/CouponRender.java @@ -0,0 +1,75 @@ +package cn.lili.modules.order.cart.render.impl; + +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.order.cart.entity.dto.MemberCouponDTO; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.render.CartRenderStep; +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 购物促销信息渲染实现 + * + * @author Chopper + * @date 2020-07-02 14:47 + */ +@Order(3) +@Service +public class CouponRender implements CartRenderStep { + + @Override + public void render(TradeDTO tradeDTO) { + + //主要渲染各个优惠的价格 + // this.renderCoupon(tradeDTO); + + } + + /** + * 渲染优惠券 + * + * @param tradeDTO 购物车展示信息 + */ + private void renderCoupon(TradeDTO tradeDTO) { + MemberCouponDTO platformCoupon = tradeDTO.getPlatformCoupon(); + if (platformCoupon != null) { + //计算平台优惠券 + for (CartSkuVO item : tradeDTO.getSkuList()) { + //如果这个sku涉及到平台优惠券价格计算 则操作这个sku的价格 + if (platformCoupon.getSkuDetail().containsKey(item.getGoodsSku().getId())) { + PriceDetailDTO priceDetailDTO = item.getPriceDetailDTO(); + priceDetailDTO.setSiteCouponPoint(platformCoupon.getMemberCoupon().getStoreCommission()); + priceDetailDTO.setSiteCouponPrice(platformCoupon.getSkuDetail().get(item.getGoodsSku().getId())); + priceDetailDTO.setSiteCouponCommission(CurrencyUtil.mul(priceDetailDTO.getSiteCouponPoint(), priceDetailDTO.getSiteCouponPrice())); + } + } + } + + //计算商家优惠券 + Map map = tradeDTO.getStoreCoupons(); + if (map != null && map.size() > 0) { + for (MemberCouponDTO coupon : map.values()) { + for (CartSkuVO item : tradeDTO.getSkuList()) { + if (coupon.getSkuDetail().containsKey(item.getGoodsSku().getId())) { + PriceDetailDTO priceDetailDTO = item.getPriceDetailDTO(); + //写入商品折扣金额 + priceDetailDTO.setDiscountPrice( + CurrencyUtil.add( + priceDetailDTO.getDiscountPrice(), + coupon.getSkuDetail().get( + item.getGoodsSku().getId() + ) + ) + ); + } + } + } + } + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/DistributionPriceRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/DistributionPriceRender.java new file mode 100644 index 00000000..5d50d612 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/DistributionPriceRender.java @@ -0,0 +1,55 @@ +package cn.lili.modules.order.cart.render.impl; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.render.CartRenderStep; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +/** + * 购物促销信息渲染实现 + * + * @author Chopper + * @date 2020-07-02 14:47 + */ +@Service +@Order(6) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionPriceRender implements CartRenderStep { + /** + * 缓存 + */ + private final Cache cache; + + @Override + public void render(TradeDTO tradeDTO) { + //主要渲染各个优惠的价格 + this.renderDistribution(tradeDTO); + } + + /** + * 渲染分销佣金 + * + * @param tradeDTO + */ + private void renderDistribution(TradeDTO tradeDTO) { + + if(cache.get(CachePrefix.DISTRIBUTION.getPrefix()+"_"+tradeDTO.getMemberId())==null){ + return; + } + //循环订单商品列表,如果是分销商品则计算商品佣金 + tradeDTO.setDistributionId(cache.get(CachePrefix.DISTRIBUTION.getPrefix()+"_"+tradeDTO.getMemberId()).toString()); + for (CartSkuVO cartSkuVO: tradeDTO.getSkuList()) { + if(cartSkuVO.getDistributionGoods()!=null){ + cartSkuVO.getPriceDetailDTO().setDistributionCommission(CurrencyUtil.mul(cartSkuVO.getNum(), cartSkuVO.getDistributionGoods().getCommission())); + } + + } + + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/FullDiscountRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/FullDiscountRender.java new file mode 100644 index 00000000..7b24bf15 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/FullDiscountRender.java @@ -0,0 +1,103 @@ +package cn.lili.modules.order.cart.render.impl; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.service.FullDiscountService; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import cn.lili.modules.order.cart.render.CartRenderStep; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * FullDiscountRender + * + * @author Chopper + * @date 2020-04-01 10:27 上午 + */ +@Service +@Order(1) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FullDiscountRender implements CartRenderStep { + + private final FullDiscountService fullDiscountService; + + @Override + public void render(TradeDTO tradeDTO) { + + // 获取购物车中所有的商品 + List cartSkuList = tradeDTO.getSkuList(); + + // 渲染的购物车 + List cartList = new ArrayList<>(); + + // 确定有哪些商家 + List storeIds = new ArrayList<>(); + + // 根据店铺分组 + Map> storeCollect = cartSkuList.parallelStream().collect(Collectors.groupingBy(CartSkuVO::getStoreId)); + for (Map.Entry> storeCart : storeCollect.entrySet()) { + if (!storeCart.getValue().isEmpty()) { + storeIds.add(storeCart.getKey()); + CartVO cartVO = new CartVO(storeCart.getValue().get(0)); + if (CharSequenceUtil.isEmpty(cartVO.getDeliveryMethod())) { + cartVO.setDeliveryMethod(DeliveryMethodEnum.LOGISTICS.name()); + } + cartVO.setSkuList(storeCart.getValue()); + storeCart.getValue().stream().filter(i -> Boolean.TRUE.equals(i.getChecked())).findFirst().ifPresent(cartSkuVO -> cartVO.setChecked(true)); + cartList.add(cartVO); + + } + } + + List fullDiscounts = fullDiscountService.currentPromotion(storeIds); + for (FullDiscountVO fullDiscount : fullDiscounts) { + if (fullDiscount.getPromotionGoodsList() != null || fullDiscount.getNumber() == -1) { + for (CartVO cart : cartList) { + if (fullDiscount.getStoreId().equals(cart.getStoreId())) { + cart.setFullDiscount(fullDiscount); + List skuIds; + if (fullDiscount.getNumber() != -1) { + skuIds = initFullDiscountGoods(fullDiscount, cartSkuList); + } else { + skuIds = cart.getSkuList().stream().map(i -> i.getGoodsSku().getId()).collect(Collectors.toList()); + } + cart.setFullDiscountSkuIds(skuIds); + } + } + } + } + + tradeDTO.setCartList(cartList); + } + + /** + * 获取参与满优惠的商品id + * + * @param fullDiscount 满优惠信息 + * @param cartSkuVOS 购物车商品sku信息 + * @return 参与满优惠的商品id + */ + public List initFullDiscountGoods(FullDiscountVO fullDiscount, List cartSkuVOS) { + List goodsIds = new ArrayList<>(); + for (PromotionGoods promotionGoods : fullDiscount.getPromotionGoodsList()) { + for (CartSkuVO cartSkuVO : cartSkuVOS) { + if (cartSkuVO.getGoodsSku().getId().equals(promotionGoods.getSkuId())) { + goodsIds.add(promotionGoods.getSkuId()); + } + } + } + return goodsIds; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuFreightRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuFreightRender.java new file mode 100644 index 00000000..8a682f30 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuFreightRender.java @@ -0,0 +1,94 @@ +package cn.lili.modules.order.cart.render.impl; + +import cn.hutool.core.util.NumberUtil; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.render.CartRenderStep; +import cn.lili.modules.store.entity.dos.FreightTemplateChild; +import cn.lili.modules.store.entity.dto.FreightTemplateChildDTO; +import cn.lili.modules.store.entity.enums.FreightTemplateEnum; +import cn.lili.modules.store.entity.vos.FreightTemplateVO; +import cn.lili.modules.store.service.FreightTemplateService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * sku 运费计算 + * + * @author Chopper + * @date 2020-07-02 14:47 + */ +@Order(4) +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SkuFreightRender implements CartRenderStep { + + private final FreightTemplateService freightTemplateService; + + @Override + public void render(TradeDTO tradeDTO) { + List cartSkuVOS = tradeDTO.getSkuList(); + for (CartSkuVO cartSkuVO : cartSkuVOS) { + String freightTemplateId = cartSkuVO.getGoodsSku().getFreightTemplateId(); + //免运费则跳出运费计算 + if (Boolean.TRUE.equals(cartSkuVO.getIsFreeFreight()) || freightTemplateId == null) { + continue; + } + //寻找对应对商品运费计算模版 + FreightTemplateVO freightTemplate = freightTemplateService.getFreightTemplate(freightTemplateId); + if (freightTemplate != null && freightTemplate.getFreightTemplateChildList() != null && !freightTemplate.getFreightTemplateChildList().isEmpty()) { + FreightTemplateChild freightTemplateChild = freightTemplate.getFreightTemplateChildList().get(0); + FreightTemplateChildDTO freightTemplateChildDTO = new FreightTemplateChildDTO(freightTemplateChild); + freightTemplateChildDTO.setPricingMethod(freightTemplate.getPricingMethod()); + + //要计算的基数 数量/重量 + Double count = (freightTemplateChildDTO.getPricingMethod().equals(FreightTemplateEnum.NUM.name())) ? + cartSkuVO.getNum() : + cartSkuVO.getGoodsSku().getWeight() * cartSkuVO.getNum(); + + //计算运费 + Double countFreight = countFreight(count, freightTemplateChildDTO); + //写入运费 + cartSkuVO.getPriceDetailDTO().setFreightPrice(countFreight); + //运费逻辑处理 + if (tradeDTO.getPriceDetailDTO().getFreightPrice() != null) { + tradeDTO.getPriceDetailDTO().setFreightPrice(CurrencyUtil.add(tradeDTO.getPriceDetailDTO().getFreightPrice(), countFreight)); + } else { + tradeDTO.getPriceDetailDTO().setFreightPrice(countFreight); + } + } + } + } + + /** + * 计算运费 + * + * @param count 重量/件 + * @param template 计算模版 + * @return 运费 + */ + private Double countFreight(Double count, FreightTemplateChildDTO template) { + try { + Double finalFreight = template.getFirstPrice(); + //不满首重 + if (template.getFirstCompany() >= count) { + return finalFreight; + } + Double continuedCount = count - template.getFirstCompany(); + // 计算续重价格 + return CurrencyUtil.add(finalFreight, + CurrencyUtil.mul(NumberUtil.parseInt(String.valueOf((continuedCount / template.getContinuedCompany()))), template.getContinuedPrice())); + } catch (Exception e) { + return 0D; + } + + + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuPromotionRender.java b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuPromotionRender.java new file mode 100644 index 00000000..44ae91b9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/render/impl/SkuPromotionRender.java @@ -0,0 +1,294 @@ +package cn.lili.modules.order.cart.render.impl; + +import cn.hutool.core.date.DateUtil; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.order.cart.entity.dto.MemberCouponDTO; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import cn.lili.modules.order.cart.entity.vo.PriceDetailVO; +import cn.lili.modules.order.cart.render.CartRenderStep; +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import cn.lili.modules.promotion.entity.dto.GoodsSkuPromotionPriceDTO; +import cn.lili.modules.promotion.entity.dto.PromotionPriceDTO; +import cn.lili.modules.promotion.entity.dto.PromotionPriceParamDTO; +import cn.lili.modules.promotion.entity.dto.StorePromotionPriceDTO; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import cn.lili.modules.promotion.service.PromotionPriceService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 购物促销信息渲染实现 + * + * @author Chopper + * @date 2020-07-02 14:47 + */ +@Service +@Order(2) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SkuPromotionRender implements CartRenderStep { + //促销计算 + private final PromotionPriceService promotionPriceService; + //促销商品 + private final PromotionGoodsService promotionGoodsService; + + @Override + public void render(TradeDTO tradeDTO) { + + //主要渲染各个优惠的价格 + this.renderSkuPromotion(tradeDTO); + + } + + + /** + * 渲染单品优惠 积分/拼团/秒杀 + * + * @param tradeDTO 购物车视图 + */ + private void renderSkuPromotion(TradeDTO tradeDTO) { + + // 渲染促销价格 + this.renderPromotionPrice(tradeDTO); + + //拼团和积分购买需要特殊处理,这里优先特殊处理 + if (tradeDTO.getCartTypeEnum().equals(CartTypeEnum.POINTS)) { + List cartSkuVOList = tradeDTO.getSkuList(); + for (CartSkuVO cartSku : cartSkuVOList) { + PriceDetailDTO priceDetailDTO = cartSku.getPriceDetailDTO(); + //需要支付的积分 + priceDetailDTO.setPayPoint( + CurrencyUtil.mul( + cartSku.getPoint(), cartSku.getNum()).intValue()); + } + } else { + //这里普通购物车也只渲染满优惠,其他优惠都是商品级别的,都写在商品属性里 + List cartVOS = tradeDTO.getCartList(); + + for (CartVO cartVO : cartVOS) { + if (isFull(cartVO)) { + this.renderFullMinus(cartVO); + } + for (CartSkuVO cartSkuVO : cartVO.getSkuList()) { + promotionGoodsService.getCartSkuPromotion(cartSkuVO); + } + } + + } + } + + /** + * 渲染购物车视图的促销价格 + * + * @param tradeDTO 购物车视图 + */ + private void renderPromotionPrice(TradeDTO tradeDTO) { + List promotionPriceParamList = new ArrayList<>(); + List cartList = tradeDTO.getCartList(); + for (CartVO cartVO : cartList) { + if (Boolean.TRUE.equals(cartVO.getChecked())) { + for (CartSkuVO cartSkuVO : cartVO.getSkuList()) { + // 检查当前购物车商品是否有效且为选中 + if (Boolean.TRUE.equals(cartSkuVO.getChecked()) && Boolean.FALSE.equals(cartSkuVO.getInvalid())) { + PromotionPriceParamDTO param = new PromotionPriceParamDTO(); + param.setSkuId(cartSkuVO.getGoodsSku().getId()); + param.setNum(cartSkuVO.getNum()); + // 是否为拼团商品计算 + if (cartSkuVO.getPintuanId() != null) { + param.setPintuanId(cartSkuVO.getPintuanId()); + } + promotionPriceParamList.add(param); + } + } + } + } + //如果包含促销规则 + if (!promotionPriceParamList.isEmpty()) { + // 店铺优惠券集合 + List memberCoupons = new ArrayList<>(); + if (tradeDTO.getStoreCoupons() != null) { + memberCoupons.addAll(tradeDTO.getStoreCoupons().values().parallelStream().map(MemberCouponDTO::getMemberCoupon).collect(Collectors.toList())); + } + + // 平台优惠券 + if (tradeDTO.getPlatformCoupon() != null && tradeDTO.getPlatformCoupon().getMemberCoupon() != null) { + memberCoupons.add(tradeDTO.getPlatformCoupon().getMemberCoupon()); + } + // 检查优惠券集合中是否存在过期优惠券 + this.checkMemberCoupons(memberCoupons); + // 调用价格计算模块,返回价格计算结果 + PromotionPriceDTO promotionPrice = promotionPriceService.calculationPromotionPrice(promotionPriceParamList, memberCoupons); + // 分配计算后的促销 + this.distributionPromotionPrice(tradeDTO, promotionPrice); + } + } + + private void checkMemberCoupons(List memberCoupons) { + long now = DateUtil.date().getTime(); + memberCoupons.removeIf(memberCoupon -> memberCoupon.getEndTime().getTime() < now); + } + + /** + * 分配促销价格到购物车视图 + * + * @param tradeDTO 购物车视图 + * @param promotionPrice 促销价格计算结果 + */ + private void distributionPromotionPrice(TradeDTO tradeDTO, PromotionPriceDTO promotionPrice) { + + for (CartVO cartVO : tradeDTO.getCartList()) { + // 根据店铺分配店铺价格计算结果 + List collect = promotionPrice.getStorePromotionPriceList().parallelStream().map(i -> i.getStoreId().equals(cartVO.getStoreId()) ? i : null).collect(Collectors.toList()); + if (!collect.isEmpty() && collect.get(0) != null) { + StorePromotionPriceDTO storePromotionPriceDTO = collect.get(0); + // 根据商品分配商品结果计算结果 + this.distributionSkuPromotionPrice(cartVO.getSkuList(), storePromotionPriceDTO); + + if (storePromotionPriceDTO != null) { + PriceDetailDTO sSpd = new PriceDetailDTO(); + PriceDetailVO sPd = new PriceDetailVO(); + sSpd.setGoodsPrice(storePromotionPriceDTO.getTotalOriginPrice()); + sSpd.setDiscountPrice(storePromotionPriceDTO.getTotalDiscountPrice()); + sSpd.setCouponPrice(storePromotionPriceDTO.getTotalCouponPrice()); + sSpd.setPayPoint(storePromotionPriceDTO.getTotalPoints().intValue()); + + sPd.setOriginalPrice(storePromotionPriceDTO.getTotalOriginPrice()); + sPd.setFinalePrice(storePromotionPriceDTO.getTotalFinalePrice()); + sPd.setDiscountPrice(storePromotionPriceDTO.getTotalDiscountPrice()); + sPd.setPayPoint(storePromotionPriceDTO.getTotalPoints().intValue()); + cartVO.setPriceDetailDTO(sSpd); + cartVO.setPriceDetailVO(sPd); + cartVO.setWeight(storePromotionPriceDTO.getTotalWeight()); + } + + } + } + + // 根据整个购物车分配价格计算结果 + PriceDetailDTO priceDetailDTO = new PriceDetailDTO(); + + priceDetailDTO.setDiscountPrice(promotionPrice.getTotalDiscountPrice()); + priceDetailDTO.setGoodsPrice(promotionPrice.getTotalOriginPrice()); + priceDetailDTO.setCouponPrice(promotionPrice.getTotalCouponPrice()); + priceDetailDTO.setPayPoint(promotionPrice.getTotalPoints().intValue()); + + tradeDTO.setPriceDetailDTO(priceDetailDTO); + } + + /** + * 分配促销价格到购物车视图的每个sku + * + * @param skuList sku列表 + * @param storePromotionPriceDTO 店铺促销结果计算结果 + */ + private void distributionSkuPromotionPrice(List skuList, StorePromotionPriceDTO storePromotionPriceDTO) { + if (storePromotionPriceDTO != null) { + for (CartSkuVO cartSkuVO : skuList) { + // 获取当前购物车商品的商品计算结果 + List collect = storePromotionPriceDTO.getGoodsSkuPromotionPriceList().parallelStream().filter(i -> i.getSkuId().equals(cartSkuVO.getGoodsSku().getId())).collect(Collectors.toList()); + if (!collect.isEmpty()) { + GoodsSkuPromotionPriceDTO goodsSkuPromotionPriceDTO = collect.get(0); + PriceDetailDTO spd = new PriceDetailDTO(); + spd.setDiscountPrice(goodsSkuPromotionPriceDTO.getTotalDiscountPrice()); + spd.setGoodsPrice(goodsSkuPromotionPriceDTO.getTotalOriginalPrice()); + spd.setCouponPrice(goodsSkuPromotionPriceDTO.getCouponPrice()); + spd.setJoinPromotion(goodsSkuPromotionPriceDTO.getJoinPromotion()); + spd.setPayPoint(goodsSkuPromotionPriceDTO.getTotalPoints().intValue()); + PriceDetailVO pd = new PriceDetailVO(); + pd.setFinalePrice(goodsSkuPromotionPriceDTO.getFinalePrice()); + pd.setOriginalPrice(goodsSkuPromotionPriceDTO.getOriginalPrice()); + pd.setDiscountPrice(goodsSkuPromotionPriceDTO.getDiscountPrice()); + pd.setPayPoint(goodsSkuPromotionPriceDTO.getTotalPoints().intValue()); + cartSkuVO.setPriceDetailDTO(spd); + cartSkuVO.setPriceDetailVO(pd); + } + + } + } + + } + + /** + * 渲染拼团 + * + * @param cartSkuList 购物车sku集合 + */ + private void renderPintuan(List cartSkuList) { + for (CartSkuVO cartSku : cartSkuList) { + PriceDetailDTO priceDetailDTO = cartSku.getPriceDetailDTO(); +// PromotionGoods promotionGoods = cartSku.getPromotion(); + //参与平台则以拼团价处理 + if (StringUtils.isNotEmpty(cartSku.getPintuanId())) { +// Double discountPrice = CurrencyUtil.sub(cartSku.getGoodsSku().getPrice(), promotionGoods.getPrice()); +// priceDetailDTO.setDiscountPrice(discountPrice); + } else { + //否则代表单独购买,则以原价购买 + priceDetailDTO.setDiscountPrice(0d); + } + } + } + + /** + * 渲染满减优惠 + * + * @param cartVO 购物车展示信息 + */ + private void renderFullMinus(CartVO cartVO) { + + //获取参与活动的商品总价 + FullDiscountVO fullDiscount = cartVO.getFullDiscount(); + + + if (Boolean.TRUE.equals(fullDiscount.getIsCoupon())) { + cartVO.getGiftCouponList().add(fullDiscount.getCouponId()); + } + if (Boolean.TRUE.equals(fullDiscount.getIsGift())) { + cartVO.setGiftList(Arrays.asList(fullDiscount.getGiftId().split(","))); + } + if (Boolean.TRUE.equals(fullDiscount.getIsPoint())) { + cartVO.setGiftPoint(cartVO.getGiftPoint() + fullDiscount.getPoint()); + } + + } + + + /** + * 是否满足满优惠 + * + * @param cart 购物车展示信息 + * @return 是否满足满优惠 + */ + private boolean isFull(CartVO cart) { + Double price = cart.getPriceDetailDTO().getGoodsPrice(); + if (cart.getFullDiscount() != null) { + if (cart.getFullDiscount().getFullMoney() <= price) { + cart.setPromotionNotice("正在参与满优惠活动(" + cart.getFullDiscount().getPromotionName() + ")" + cart.getFullDiscount().notice()); + //如果满足,判定是否免邮,免邮的话需要渲染一边sku + if (Boolean.TRUE.equals(cart.getFullDiscount().getIsFreeFreight())) { + for (CartSkuVO skuVO : cart.getSkuList()) { + skuVO.setIsFreeFreight(true); + } + } + return true; + } else { + cart.setPromotionNotice("还差" + CurrencyUtil.sub(cart.getFullDiscount().getFullMoney(), price) + " 即可参与活动(" + cart.getFullDiscount().getPromotionName() + ")" + cart.getFullDiscount().notice()); + return false; + } + } else { + cart.setPromotionNotice(""); + } + return false; + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/service/CartService.java b/framework/src/main/java/cn/lili/modules/order/cart/service/CartService.java new file mode 100644 index 00000000..09531030 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/service/CartService.java @@ -0,0 +1,183 @@ +package cn.lili.modules.order.cart.service; + + +import cn.lili.modules.member.entity.dos.MemberAddress; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.entity.vo.TradeParams; +import cn.lili.modules.order.order.entity.dos.Trade; +import cn.lili.modules.order.order.entity.vo.ReceiptVO; + +import java.util.List; + +/** + * 购物车业务层 + * + * @author Chopper + * @date 2020-03-23 12:29 下午 + */ +public interface CartService { + + /** + * 获取整笔交易 + * + * @param checkedWay 购物车类型 + * @return 购物车视图 + */ + TradeDTO readDTO(CartTypeEnum checkedWay); + + /** + * 获取整个交易中勾选的购物车和商品 + * + * @param way 获取方式 + * @return 交易信息 + */ + TradeDTO getCheckedTradeDTO(CartTypeEnum way); + + /** + * 获取可使用的优惠券数量 + * + * @param checkedWay 购物车类型 + * @return 可使用的优惠券数量 + */ + Long getCanUseCoupon(CartTypeEnum checkedWay); + + /** + * 获取整个交易中勾选的购物车和商品 + * + * @return 交易信息 + */ + TradeDTO getAllTradeDTO(); + + /** + * 购物车加入一个商品 + * + * @param skuId 要写入的skuId + * @param num 要加入购物车的数量 + * @param cartType 购物车类型 + */ + void add(String skuId, Integer num, String cartType); + + /** + * 更新商品数量 + * + * @param skuId 要写入的skuId + * @param num 要加入购物车的数量 + */ + void updateNum(String skuId, int num); + + + /** + * 更新选中状态 + * + * @param skuId 要写入的skuId + * @param checked 是否选中 + */ + void checked(String skuId, boolean checked); + + + /** + * 更新某个店铺的所有商品的选中状态 + * + * @param storeId 店铺Id + * @param checked 是否选中 + */ + void checkedStore(String storeId, boolean checked); + + + /** + * 更新全部的选中状态 + * + * @param checked 是否选中 + */ + void checkedAll(boolean checked); + + + /** + * 批量删除 + * + * @param skuIds 要写入的skuIds + */ + void delete(String[] skuIds); + + /** + * 清空购物车 + */ + void clean(); + + /** + * 清空购物车无效数据 + * + * @param way 购物车类型 + */ + void cleanChecked(CartTypeEnum way); + + /** + * 重新写入 + * + * @param tradeDTO 购物车构建器最终要构建的成品 + */ + void resetTradeDTO(TradeDTO tradeDTO); + + + /** + * 选择收货地址 + * + * @param shippingAddressId 收货地址id + * @param way 购物车类型 + */ + void shippingAddress(String shippingAddressId, String way); + + /** + * 选择发票 + * + * @param receiptVO 发票信息 + * @param way 购物车类型 + */ + void shippingReceipt(ReceiptVO receiptVO, String way); + + + /** + * 选择配送方式 + * + * @param storeId 店铺id + * @param deliveryMethod 配送方式 + * @param way 购物车类型 + */ + void shippingMethod(String storeId, String deliveryMethod, String way); + + /** + * 获取购物车商品数量 + * + * @param checked 是否选择 + * @return 购物车商品数量 + */ + Long getCartNum(Boolean checked); + + /** + * 选择优惠券 + * + * @param couponId 优惠券id + * @param way 购物车类型 + * @param use true使用 false 弃用 + */ + void selectCoupon(String couponId, String way, boolean use); + + /** + * 创建交易 + * + * @param tradeParams 创建交易参数 + * @return 交易信息 + */ + Trade createTrade(TradeParams tradeParams); + + /** + * 检查商品是否在配送范围 + * + * @param skuList 商品列表 + * @param memberAddress 配送地址 + */ + void checkAddressScope(List skuList, MemberAddress memberAddress); + +} diff --git a/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java new file mode 100644 index 00000000..64e06460 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/cart/service/CartServiceImpl.java @@ -0,0 +1,578 @@ +package cn.lili.modules.order.cart.service; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.member.entity.dos.MemberAddress; +import cn.lili.modules.order.cart.entity.dto.MemberCouponDTO; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.enums.CartTypeEnum; +import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum; +import cn.lili.modules.order.cart.entity.enums.TradeCacheEnum; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import cn.lili.modules.order.cart.entity.vo.TradeParams; +import cn.lili.modules.order.cart.render.TradeBuilder; +import cn.lili.modules.order.order.entity.dos.Trade; +import cn.lili.modules.order.order.entity.vo.ReceiptVO; +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import cn.lili.modules.promotion.entity.enums.CouponScopeTypeEnum; +import cn.lili.modules.promotion.entity.enums.MemberCouponStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.service.MemberAddressService; +import cn.lili.modules.promotion.service.MemberCouponService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.service.EsGoodsSearchService; +import cn.lili.modules.store.entity.dos.FreightTemplateChild; +import cn.lili.modules.store.entity.vos.FreightTemplateVO; +import cn.lili.modules.store.service.FreightTemplateService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 购物车业务层实现 + * + * @author Chopper + * @date 2020-03-23 12:29 下午 + */ +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CartServiceImpl implements CartService { + + static String errorMessage = "购物车异常,请稍后重试"; + //缓存 + private final Cache cache; + //会员优惠券 + private final MemberCouponService memberCouponService; + //规格商品 + private final GoodsSkuService goodsSkuService; + //促销商品 + private final PromotionGoodsService promotionGoodsService; + //会员地址 + private final MemberAddressService memberAddressService; + //ES商品 + private final EsGoodsSearchService esGoodsSearchService; + //运费模板 + private final FreightTemplateService freightTemplateService; + //交易 + private TradeBuilder tradeBuilder; + + @Override + public void add(String skuId, Integer num, String cartType) { + CartTypeEnum cartTypeEnum = getCartType(cartType); + GoodsSku dataSku = checkGoods(skuId, num); + try { + //购物车方式购买需要保存之前的选择,其他方式购买,则直接抹除掉之前的记录 + TradeDTO tradeDTO; + if (cartTypeEnum.equals(CartTypeEnum.CART)) { + //如果存在,则变更数量不做新增,否则新增一个商品进入集合 + tradeDTO = this.readDTO(cartTypeEnum); + List cartSkuVOS = tradeDTO.getSkuList(); + CartSkuVO cartSkuVO = cartSkuVOS.stream().filter(i -> i.getGoodsSku().getId().equals(skuId)).findFirst().orElse(null); + //购物车中已经存在,更新数量 + if (cartSkuVO != null && dataSku.getUpdateTime().equals(cartSkuVO.getGoodsSku().getUpdateTime())) { + //判断是商品否被修改 + int oldNum = cartSkuVO.getNum(); + int newNum = oldNum + num; + this.checkSetGoodsQuantity(cartSkuVO, skuId, newNum); + //计算购物车小计 + cartSkuVO.setSubTotal(CurrencyUtil.mul(cartSkuVO.getPurchasePrice(),cartSkuVO.getNum())); + } else { + //先清理一下 如果商品无效的话 + cartSkuVOS.remove(cartSkuVO); + //购物车中不存在此商品,则新建立一个 + cartSkuVO = new CartSkuVO(dataSku); + + cartSkuVO.setCartType(cartTypeEnum); + promotionGoodsService.updatePromotion(cartSkuVO); + //再设置加入购物车的数量 + this.checkSetGoodsQuantity(cartSkuVO, skuId, num); + //计算购物车小计 + cartSkuVO.setSubTotal(CurrencyUtil.mul(cartSkuVO.getPurchasePrice(),cartSkuVO.getNum())); + cartSkuVOS.add(cartSkuVO); + } + + //新加入的商品都是选中的 + cartSkuVO.setChecked(true); + } else { + tradeDTO = new TradeDTO(cartTypeEnum); + AuthUser currentUser = UserContext.getCurrentUser(); + tradeDTO.setMemberId(currentUser.getId()); + tradeDTO.setMemberName(currentUser.getUsername()); + List cartSkuVOS = tradeDTO.getSkuList(); + + //购物车中不存在此商品,则新建立一个 + CartSkuVO cartSkuVO = new CartSkuVO(dataSku); + cartSkuVO.setCartType(cartTypeEnum); + promotionGoodsService.updatePromotion(cartSkuVO); + //再设置加入购物车的数量 + this.checkSetGoodsQuantity(cartSkuVO, skuId, num); + //计算购物车小计 + cartSkuVO.setSubTotal(CurrencyUtil.mul(cartSkuVO.getPurchasePrice(),cartSkuVO.getNum())); + cartSkuVOS.add(cartSkuVO); + } + tradeDTO.setCartTypeEnum(cartTypeEnum); + this.resetTradeDTO(tradeDTO); + } catch (Exception e) { + log.error("购物车渲染异常",e); + throw new ServiceException(errorMessage); + } + } + + + @Override + public void updateNum(String skuId, int num) { + try { + checkGoods(skuId, num); + TradeDTO tradeDTO = this.readDTO(CartTypeEnum.CART); + CartSkuVO cartSkuVO = null; + for (CartSkuVO skuVO : tradeDTO.getSkuList()) { + if (skuVO.getGoodsSku().getId().equals(skuId)) { + cartSkuVO = skuVO; + } + } + if (cartSkuVO != null) { + this.checkSetGoodsQuantity(cartSkuVO, skuId, num); + } + String originKey = this.getOriginKey(CartTypeEnum.CART); + cache.put(originKey, tradeDTO); + }catch (ServiceException se){ + log.error("购物车渲染异常", se); + } catch (Exception e) { + log.error("购物车渲染异常",e); + throw new ServiceException(errorMessage); + } + } + + /** + * 读取当前会员购物原始数据key + * + * @param cartTypeEnum 获取方式 + * @return 当前会员购物原始数据key + */ + private String getOriginKey(CartTypeEnum cartTypeEnum) { + + String cacheKey = ""; + //如果会员登录了,则要以会员id为key + AuthUser currentUser = UserContext.getCurrentUser(); + if (cartTypeEnum.equals(CartTypeEnum.CART)) { + cacheKey = TradeCacheEnum.CART_DATA.getPrefix() + currentUser.getId(); + } else if (cartTypeEnum.equals(CartTypeEnum.BUY_NOW)) { + cacheKey = TradeCacheEnum.BUY_NOW_CART_DATA.getPrefix() + currentUser.getId(); + } else if (cartTypeEnum.equals(CartTypeEnum.PINTUAN)) { + cacheKey = TradeCacheEnum.PINTUAN.getPrefix() + currentUser.getId(); + } + return cacheKey; + } + + @Override + public TradeDTO readDTO(CartTypeEnum checkedWay) { + TradeDTO tradeDTO = (TradeDTO) cache.get(this.getOriginKey(checkedWay)); + if (tradeDTO == null) { + tradeDTO = new TradeDTO(checkedWay); + AuthUser currentUser = UserContext.getCurrentUser(); + tradeDTO.setMemberId(currentUser.getId()); + tradeDTO.setMemberName(currentUser.getUsername()); + } + if (tradeDTO.getMemberAddress() == null) { + tradeDTO.setMemberAddress(this.memberAddressService.getDefaultMemberAddress()); + } + return tradeDTO; + } + + @Override + public void checked(String skuId, boolean checked) { + TradeDTO tradeDTO = this.readDTO(CartTypeEnum.CART); + List cartSkuVOS = tradeDTO.getSkuList(); + for (CartSkuVO cartSkuVO : cartSkuVOS) { + if (cartSkuVO.getGoodsSku().getId().equals(skuId)) { + cartSkuVO.setChecked(checked); + } + } + cache.put(this.getOriginKey(CartTypeEnum.CART), tradeDTO); + } + + @Override + public void checkedStore(String storeId, boolean checked) { + TradeDTO tradeDTO = this.readDTO(CartTypeEnum.CART); + List cartSkuVOS = tradeDTO.getSkuList(); + for (CartSkuVO cartSkuVO : cartSkuVOS) { + if (cartSkuVO.getStoreId().equals(storeId)) { + cartSkuVO.setChecked(checked); + } + } + cache.put(this.getOriginKey(CartTypeEnum.CART), tradeDTO); + } + + @Override + public void checkedAll(boolean checked) { + TradeDTO tradeDTO = this.readDTO(CartTypeEnum.CART); + List cartSkuVOS = tradeDTO.getSkuList(); + for (CartSkuVO cartSkuVO : cartSkuVOS) { + cartSkuVO.setChecked(checked); + } + cache.put(this.getOriginKey(CartTypeEnum.CART), tradeDTO); + } + + @Override + public void delete(String[] skuIds) { + TradeDTO tradeDTO = this.readDTO(CartTypeEnum.CART); + List cartSkuVOS = tradeDTO.getSkuList(); + List deleteVos = new ArrayList<>(); + for (CartSkuVO cartSkuVO : cartSkuVOS) { + for (String skuId : skuIds) { + if (cartSkuVO.getGoodsSku().getId().equals(skuId)) { + deleteVos.add(cartSkuVO); + } + } + } + cartSkuVOS.removeAll(deleteVos); + cache.put(this.getOriginKey(CartTypeEnum.CART), tradeDTO); + } + + @Override + public void clean() { + cache.remove(this.getOriginKey(CartTypeEnum.CART)); + } + + public void cleanChecked(TradeDTO tradeDTO) { + List cartSkuVOS = tradeDTO.getSkuList(); + List deleteVos = new ArrayList<>(); + for (CartSkuVO cartSkuVO : cartSkuVOS) { + if (Boolean.TRUE.equals(cartSkuVO.getChecked())) { + deleteVos.add(cartSkuVO); + } + } + cartSkuVOS.removeAll(deleteVos); + // 清除添加过的备注 + tradeDTO.setStoreRemark(null); + cache.put(this.getOriginKey(tradeDTO.getCartTypeEnum()), tradeDTO); + } + + @Override + public void cleanChecked(CartTypeEnum way) { + if (way.equals(CartTypeEnum.CART)) { + TradeDTO tradeDTO = this.readDTO(CartTypeEnum.CART); + this.cleanChecked(tradeDTO); + } else { + cache.remove(this.getOriginKey(way)); + } + } + + @Override + public void resetTradeDTO(TradeDTO tradeDTO) { + cache.put(this.getOriginKey(tradeDTO.getCartTypeEnum()), tradeDTO); + } + + @Override + public TradeDTO getCheckedTradeDTO(CartTypeEnum way) { + return tradeBuilder.buildTrade(way); + } + + /** + * 获取可使用的优惠券数量 + * + * @param checkedWay 购物车购买:CART/立即购买:BUY_NOW/拼团购买:PINTUAN / 积分购买:POINT + * @return 可使用的优惠券数量 + */ + @Override + public Long getCanUseCoupon(CartTypeEnum checkedWay) { + TradeDTO tradeDTO = this.readDTO(checkedWay); + long count = 0L; + double totalPrice = tradeDTO.getSkuList().stream().mapToDouble(i -> i.getPurchasePrice() * i.getNum()).sum(); + if (tradeDTO.getSkuList() != null && !tradeDTO.getSkuList().isEmpty()) { + List ids = tradeDTO.getSkuList().parallelStream().filter(i -> Boolean.TRUE.equals(i.getChecked())).map(i -> i.getGoodsSku().getId()).collect(Collectors.toList()); + List storeIds = new ArrayList<>(); + List esGoodsList = esGoodsSearchService.getEsGoodsBySkuIds(ids); + for (EsGoodsIndex esGoodsIndex : esGoodsList) { + if (esGoodsIndex.getPromotionMap() != null) { + List couponIds = esGoodsIndex.getPromotionMap().keySet().parallelStream().filter(i -> i.contains(PromotionTypeEnum.COUPON.name())).map(i -> i.substring(i.lastIndexOf("-") + 1)).collect(Collectors.toList()); + if (!couponIds.isEmpty()) { + List currentGoodsCanUse = memberCouponService.getCurrentGoodsCanUse(tradeDTO.getMemberId(), couponIds, totalPrice); + count = currentGoodsCanUse.size(); + } + } + storeIds.add(esGoodsIndex.getStoreId()); + } + List allScopeMemberCoupon = memberCouponService.getAllScopeMemberCoupon(tradeDTO.getMemberId(), storeIds); + if (allScopeMemberCoupon != null && !allScopeMemberCoupon.isEmpty()) { + // 过滤满足消费门槛 + count += allScopeMemberCoupon.stream().filter(i -> i.getConsumeThreshold() <= totalPrice).count(); + } + } + return count; + } + + @Override + public TradeDTO getAllTradeDTO() { + return tradeBuilder.buildCart(CartTypeEnum.CART); + } + + /** + * 校验商品有效性,判定失效和库存 + * + * @param skuId 商品skuId + * @param num 数量 + */ + private GoodsSku checkGoods(String skuId, Integer num) { + GoodsSku dataSku = this.goodsSkuService.getGoodsSkuByIdFromCache(skuId); + if (dataSku == null) { + throw new ServiceException("商品已失效,请重新选购!"); + } + if (!GoodsAuthEnum.PASS.name().equals(dataSku.getIsAuth()) || !GoodsStatusEnum.UPPER.name().equals(dataSku.getMarketEnable())) { + throw new ServiceException("商品已下架,请重新选购!"); + } + //读取sku的可用库存 + Integer enableQuantity = goodsSkuService.getStock(skuId); + + //如果sku的可用库存小于等于0或者小于用户购买的数量,则不允许购买 + if (enableQuantity <= 0 || enableQuantity < num) { + throw new ServiceException("商品库存已不足,请选购其他商品。"); + } + return dataSku; + } + + /** + * 检查并设置购物车商品数量 + * + * @param cartSkuVO 购物车商品对象 + * @param skuId 商品id + * @param num 购买数量 + */ + private void checkSetGoodsQuantity(CartSkuVO cartSkuVO, String skuId, Integer num) { + Integer enableStock = goodsSkuService.getStock(skuId); + if (enableStock <= num) { + cartSkuVO.setNum(enableStock); + } else { + cartSkuVO.setNum(num); + } + + if (cartSkuVO.getNum() > 100) { + cartSkuVO.setNum(99); + } + } + + @Override + public void shippingAddress(String shippingAddressId, String way) { + + //默认购物车 + CartTypeEnum cartTypeEnum = CartTypeEnum.CART; + if (CharSequenceUtil.isNotEmpty(way)) { + cartTypeEnum = CartTypeEnum.valueOf(way); + } + + TradeDTO tradeDTO = this.readDTO(cartTypeEnum); + MemberAddress memberAddress = memberAddressService.getById(shippingAddressId); + this.checkAddressScope(tradeDTO.getSkuList(), memberAddress); + tradeDTO.setMemberAddress(memberAddress); + this.resetTradeDTO(tradeDTO); + } + + /** + * 选择发票 + * + * @param receiptVO 发票信息 + * @param way 购物车类型 + */ + @Override + public void shippingReceipt(ReceiptVO receiptVO, String way) { + CartTypeEnum cartTypeEnum = CartTypeEnum.CART; + if (CharSequenceUtil.isNotEmpty(way)) { + cartTypeEnum = CartTypeEnum.valueOf(way); + } + TradeDTO tradeDTO = this.readDTO(cartTypeEnum); + tradeDTO.setNeedReceipt(true); + tradeDTO.setReceiptVO(receiptVO); + this.resetTradeDTO(tradeDTO); + } + + /** + * 选择配送方式 + * + * @param storeId 店铺id + * @param deliveryMethod 配送方式 + * @param way 购物车类型 + */ + @Override + public void shippingMethod(String storeId, String deliveryMethod, String way) { + CartTypeEnum cartTypeEnum = CartTypeEnum.CART; + if (CharSequenceUtil.isNotEmpty(way)) { + cartTypeEnum = CartTypeEnum.valueOf(way); + } + TradeDTO tradeDTO = this.readDTO(cartTypeEnum); + for (CartVO cartVO : tradeDTO.getCartList()) { + if (cartVO.getStoreId().equals(storeId)) { + cartVO.setDeliveryMethod(DeliveryMethodEnum.valueOf(deliveryMethod).name()); + } + } + this.resetTradeDTO(tradeDTO); + } + + /** + * 获取购物车商品数量 + * + * @param checked 是否选择 + * @return 购物车商品数量 + */ + @Override + public Long getCartNum(Boolean checked) { + TradeDTO tradeDTO = this.getCheckedTradeDTO(CartTypeEnum.CART); + List collect = tradeDTO.getSkuList().stream().filter(i -> Boolean.FALSE.equals(i.getInvalid())).collect(Collectors.toList()); + long count = 0L; + if (!tradeDTO.getSkuList().isEmpty()) { + if (checked != null) { + count = collect.stream().filter(i -> i.getChecked().equals(checked)).count(); + } else { + count = collect.size(); + } + } + return count; + } + + @Override + public void selectCoupon(String couponId, String way, boolean use) { + CartTypeEnum cartTypeEnum = getCartType(way); + TradeDTO tradeDTO = tradeBuilder.buildTrade(cartTypeEnum); + + MemberCoupon memberCoupon = memberCouponService.getOne(new LambdaQueryWrapper().eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name()).eq(MemberCoupon::getId, couponId)); + if (memberCoupon == null) { + throw new ServiceException("当前优惠券可用数量不足"); + } + //使用优惠券 与否 + if (use && checkCoupon(memberCoupon, tradeDTO)) { + this.useCoupon(tradeDTO, memberCoupon); + } else if (!use) { + if (Boolean.TRUE.equals(memberCoupon.getIsPlatform())) { + tradeDTO.setPlatformCoupon(null); + } else { + tradeDTO.getStoreCoupons().remove(memberCoupon.getStoreId()); + } + } + this.resetTradeDTO(tradeDTO); + } + + + @Override + public Trade createTrade(TradeParams tradeParams) { + CartTypeEnum cartTypeEnum = getCartType(tradeParams.getWay()); + TradeDTO tradeDTO = this.readDTO(cartTypeEnum); + tradeDTO.setClientType(tradeParams.getClient()); + tradeDTO.setStoreRemark(tradeParams.getRemark()); + List collect = tradeDTO.getSkuList().parallelStream().filter(i -> Boolean.TRUE.equals(i.getChecked())).collect(Collectors.toList()); + MemberAddress memberAddress = tradeDTO.getMemberAddress(); + this.checkAddressScope(collect, memberAddress); + this.resetTradeDTO(tradeDTO); + Trade trade = tradeBuilder.createTrade(cartTypeEnum, tradeParams.getParentOrderSn()); + this.cleanChecked(tradeDTO); + return trade; + } + + /** + * 检查商品是否在配送范围 + * + * @param skuList 商品列表 + * @param memberAddress 配送地址 + */ + @Override + public void checkAddressScope(List skuList, MemberAddress memberAddress) { + if (memberAddress == null) { + return; + } + for (CartSkuVO cartSkuVO : skuList) { + if (Boolean.TRUE.equals(cartSkuVO.getIsFreeFreight())) { + break; + } + String freightTemplateId = cartSkuVO.getGoodsSku().getFreightTemplateId(); + FreightTemplateVO freightTemplate = freightTemplateService.getFreightTemplate(freightTemplateId); + String[] addressId = memberAddress.getConsigneeAddressIdPath().split(","); + if (freightTemplate != null && freightTemplate.getFreightTemplateChildList() != null && !freightTemplate.getFreightTemplateChildList().isEmpty()) { + FreightTemplateChild freightTemplateChild = freightTemplate.getFreightTemplateChildList().get(0); + // 检查当前配送地址的城市id是否存在与配送模版的城市id里面 + if (!freightTemplateChild.getAreaId().contains(addressId[1])) { + throw new ServiceException("当前选择地址暂不支持配送!"); + } + } + } + } + + private CartTypeEnum getCartType(String way) { + //默认购物车 + CartTypeEnum cartTypeEnum = CartTypeEnum.CART; + if (CharSequenceUtil.isNotEmpty(way)) { + try { + cartTypeEnum = CartTypeEnum.valueOf(way); + } catch (IllegalArgumentException e) { + log.error("获取购物车类型出现错误:", e); + } + } + return cartTypeEnum; + } + + private void useCoupon(TradeDTO tradeDTO, MemberCoupon memberCoupon) { + //如果是平台优惠券 + if (Boolean.TRUE.equals(memberCoupon.getIsPlatform())) { + if (memberCoupon.getConsumeThreshold() <= tradeDTO.getPriceDetailDTO().getGoodsPrice()) { + tradeDTO.setPlatformCoupon(new MemberCouponDTO(memberCoupon)); + tradeDTO.setStoreCoupons(new HashMap<>()); + } + } else { + CartSkuVO cartVO = tradeDTO.getSkuList().stream().filter(i -> i.getStoreId().equals(memberCoupon.getStoreId())).findFirst().orElse(null); + // 优惠券消费门槛 <= 商品购买时的成交价(单品) * 购买数量 + if (cartVO != null && memberCoupon.getConsumeThreshold() <= CurrencyUtil.mul(cartVO.getPurchasePrice(), cartVO.getNum())) { + tradeDTO.getStoreCoupons().put(memberCoupon.getStoreId(), new MemberCouponDTO(memberCoupon)); + tradeDTO.setPlatformCoupon(null); + } + } + } + + /** + * 校验是否可以使用优惠券 + * + * @param memberCoupon 用于计算优惠券结算详情 + * @param tradeDTO 购物车信息 + * @return 是否可以使用优惠券 + */ + private boolean checkCoupon(MemberCoupon memberCoupon, TradeDTO tradeDTO) { + List cartSkuVOS; + if (Boolean.FALSE.equals(memberCoupon.getIsPlatform())) { + cartSkuVOS = tradeDTO.getSkuList().stream().filter(i -> i.getStoreId().equals(memberCoupon.getStoreId())).collect(Collectors.toList()); + } else { + cartSkuVOS = tradeDTO.getSkuList(); + } + + // 当初购物车商品中是否存在符合优惠券条件的商品sku + if (memberCoupon.getScopeType().equals(CouponScopeTypeEnum.PORTION_GOODS_CATEGORY.name())) { + // 分类路径是否包含 + return cartSkuVOS.stream().anyMatch(i -> i.getGoodsSku().getCategoryPath().indexOf("," + memberCoupon.getScopeId() + ",") <= 0); + } else if (memberCoupon.getScopeType().equals(CouponScopeTypeEnum.PORTION_GOODS.name())) { + // 范围关联ID是否包含 + return cartSkuVOS.stream().anyMatch(i -> memberCoupon.getScopeId().indexOf("," + i.getGoodsSku().getId() + ",") <= 0); + } else if (memberCoupon.getScopeType().equals(CouponScopeTypeEnum.PORTION_SHOP_CATEGORY.name())) { + // 分类路径是否包含 + return cartSkuVOS.stream().anyMatch(i -> i.getGoodsSku().getStoreCategoryPath().indexOf("," + memberCoupon.getScopeId() + ",") <= 0); + } + return true; + } + + @Autowired + public void setTradeBuilder(TradeBuilder tradeBuilder) { + this.tradeBuilder = tradeBuilder; + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/aop/AfterSaleLogPoint.java b/framework/src/main/java/cn/lili/modules/order/order/aop/AfterSaleLogPoint.java new file mode 100644 index 00000000..abd8a8fa --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/aop/AfterSaleLogPoint.java @@ -0,0 +1,30 @@ +package cn.lili.modules.order.order.aop; + +import java.lang.annotation.*; + +/** + * 售后日志AOP注解 + * + * @author Chopper + * @date 2020/11/17 7:22 下午 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AfterSaleLogPoint { + + /** + * 日志名称 + * + * @return + */ + String description(); + + /** + * 售后SN + * + * @return + */ + String sn(); + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/aop/AfterSaleOperationLogAspect.java b/framework/src/main/java/cn/lili/modules/order/order/aop/AfterSaleOperationLogAspect.java new file mode 100644 index 00000000..ecd11da7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/aop/AfterSaleOperationLogAspect.java @@ -0,0 +1,97 @@ +package cn.lili.modules.order.order.aop; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.SpelUtil; +import cn.lili.common.utils.ThreadPoolUtil; +import cn.lili.modules.order.trade.entity.dos.AfterSaleLog; +import cn.lili.modules.order.order.service.AfterSaleLogService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * 订单操作日志 + * + * @author Chopper + * @date 2020/11/17 7:22 下午 + */ +@Slf4j +@Aspect +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AfterSaleOperationLogAspect { + + + private final AfterSaleLogService afterSaleLogService; + + @AfterReturning(returning = "rvt", pointcut = "@annotation(cn.lili.modules.order.order.aop.AfterSaleLogPoint)") + public void afterReturning(JoinPoint joinPoint, Object rvt) { + try { + AuthUser auth = UserContext.getCurrentUser(); + //日志对象拼接 + //默认操作人员,系统操作 + String userName = "系统操作", id = "-1", role = UserEnums.SYSTEM.getRole(); + if (auth != null) { + //日志对象拼接 + userName = UserContext.getCurrentUser().getUsername(); + id = UserContext.getCurrentUser().getId(); + role = UserContext.getCurrentUser().getRole().getRole(); + } + Map afterSaleLogPoints = spelFormat(joinPoint, rvt); + AfterSaleLog afterSaleLog = new AfterSaleLog(afterSaleLogPoints.get("sn"), id, role, userName, afterSaleLogPoints.get("description")); + //调用线程保存 + ThreadPoolUtil.getPool().execute(new SaveAfterSaleLogThread(afterSaleLog, afterSaleLogService)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param joinPoint 切点 + * @return 方法描述 + */ + public static Map spelFormat(JoinPoint joinPoint, Object rvt) { + + Map result = new HashMap<>(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + AfterSaleLogPoint afterSaleLogPoint = signature.getMethod().getAnnotation(AfterSaleLogPoint.class); + String description = SpelUtil.compileParams(joinPoint, rvt, afterSaleLogPoint.description()); + String sn = SpelUtil.compileParams(joinPoint, rvt, afterSaleLogPoint.sn()); + result.put("description", description); + result.put("sn", sn); + return result; + + } + + /** + * 保存日志 + */ + private static class SaveAfterSaleLogThread implements Runnable { + + private final AfterSaleLog afterSaleLog; + private final AfterSaleLogService afterSaleLogService; + + public SaveAfterSaleLogThread(AfterSaleLog afterSaleLog, AfterSaleLogService afterSaleLogService) { + this.afterSaleLog = afterSaleLog; + this.afterSaleLogService = afterSaleLogService; + } + + @Override + public void run() { + afterSaleLogService.save(afterSaleLog); + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/aop/OrderLogPoint.java b/framework/src/main/java/cn/lili/modules/order/order/aop/OrderLogPoint.java new file mode 100644 index 00000000..431e9a5e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/aop/OrderLogPoint.java @@ -0,0 +1,30 @@ +package cn.lili.modules.order.order.aop; + +import java.lang.annotation.*; + +/** + * 订单日志AOP注解 + * + * @author Chopper + * @date 2020/11/17 7:22 下午 + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface OrderLogPoint { + + /** + * 日志名称 + * + * @return + */ + String description(); + + /** + * 订单编号 + * + * @return + */ + String orderSn(); + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/aop/OrderOperationLogAspect.java b/framework/src/main/java/cn/lili/modules/order/order/aop/OrderOperationLogAspect.java new file mode 100644 index 00000000..e8552576 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/aop/OrderOperationLogAspect.java @@ -0,0 +1,94 @@ +package cn.lili.modules.order.order.aop; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.SpelUtil; +import cn.lili.common.utils.ThreadPoolUtil; +import cn.lili.modules.order.trade.entity.dos.OrderLog; +import cn.lili.modules.order.trade.service.OrderLogService; +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +/** + * 订单操作日志 + * + * @author Chopper + * @date: 2020/11/17 7:22 下午 + */ +@Aspect +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderOperationLogAspect { + + private final OrderLogService orderLogService; + + @After("@annotation(cn.lili.modules.order.order.aop.OrderLogPoint)") + public void doAfter(JoinPoint joinPoint) { + try { + + //日志对象拼接 + //默认操作人员,系统操作 + String userName = "系统操作", id = "-1", role = UserEnums.SYSTEM.getRole(); + if (UserContext.getCurrentUser() != null) { + //日志对象拼接 + userName = UserContext.getCurrentUser().getUsername(); + id = UserContext.getCurrentUser().getId(); + role = UserContext.getCurrentUser().getRole().getRole(); + } + Map orderLogPoints = spelFormat(joinPoint); + OrderLog orderLog = new OrderLog(orderLogPoints.get("orderSn"), id, role, userName, orderLogPoints.get("description")); + //调用线程保存 + ThreadPoolUtil.getPool().execute(new SaveOrderLogThread(orderLog, orderLogService)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param joinPoint 切点 + * @return 方法描述 + * @throws Exception + */ + private static Map spelFormat(JoinPoint joinPoint) throws Exception { + + Map result = new HashMap<>(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + OrderLogPoint orderLogPoint = signature.getMethod().getAnnotation(OrderLogPoint.class); + String description = SpelUtil.compileParams(joinPoint, orderLogPoint.description()); + String orderSn = SpelUtil.compileParams(joinPoint, orderLogPoint.orderSn()); + + result.put("description", description); + result.put("orderSn", orderSn); + return result; + } + + /** + * 保存日志 + */ + private static class SaveOrderLogThread implements Runnable { + + private final OrderLog orderLog; + private final OrderLogService orderLogService; + + public SaveOrderLogThread(OrderLog orderLog, OrderLogService orderLogService) { + this.orderLog = orderLog; + this.orderLogService = orderLogService; + } + + @Override + public void run() { + orderLogService.save(orderLog); + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/AfterSale.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/AfterSale.java new file mode 100644 index 00000000..c93d1bdc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/AfterSale.java @@ -0,0 +1,153 @@ +package cn.lili.modules.order.order.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; + +/** + * 售后 + * + * @author Chopper + * @date 2020/11/17 7:30 下午 + */ +@Data +@Entity +@Table(name = "li_after_sale") +@TableName("li_after_sale") +@ApiModel(value = "售后") +public class AfterSale extends BaseEntity { + + private static final long serialVersionUID = -5339221840646353416L; + + //基础信息 + + @ApiModelProperty(value = "售后服务单号") + private String sn; + + @ApiModelProperty(value = "订单编号") + private String orderSn; + + @ApiModelProperty(value = "订单货物编号") + private String orderItemSn; + + @ApiModelProperty(value = "交易编号") + private String tradeSn; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "商家ID") + private String storeId; + + @ApiModelProperty(value = "商家名称") + private String storeName; + + //商品信息 + + @ApiModelProperty(value = "商品ID") + private String goodsId; + @ApiModelProperty(value = "货品ID") + private String skuId; + @ApiModelProperty(value = "申请数量") + private Integer num; + @ApiModelProperty(value = "商品图片") + private String goodsImage; + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "规格json") + @Column(columnDefinition = "TEXT") + private String specs; + @ApiModelProperty(value = "实际金额") + private Double flowPrice; + + + //交涉信息 + + @ApiModelProperty(value = "申请原因") + private String reason; + + @ApiModelProperty(value = "问题描述") + private String problemDesc; + + @ApiModelProperty(value = "评价图片") + private String afterSaleImage; + + /** + * @see cn.lili.modules.order.trade.entity.enums.AfterSaleTypeEnum + */ + @ApiModelProperty(value = "售后类型", allowableValues = "RETURN_GOODS,RETURN_MONEY") + private String serviceType; + + /** + * @see cn.lili.modules.order.trade.entity.enums.AfterSaleStatusEnum + */ + @ApiModelProperty(value = "售后单状态", allowableValues = "APPLY,PASS,REFUSE,BUYER_RETURN,SELLER_RE_DELIVERY,BUYER_CONFIRM,SELLER_CONFIRM,COMPLETE") + private String serviceStatus; + + // 退款信息 + + /** + * @see cn.lili.modules.order.trade.entity.enums.AfterSaleRefundWayEnum + */ + @ApiModelProperty(value = "退款方式", allowableValues = "ORIGINAL,OFFLINE") + private String refundWay; + + @ApiModelProperty(value = "账号类型", allowableValues = "ALIPAY,WECHATPAY,BANKTRANSFER") + private String accountType; + + @ApiModelProperty(value = "银行账户") + private String bankAccountNumber; + + @ApiModelProperty(value = "银行开户名") + private String bankAccountName; + + @ApiModelProperty(value = "银行开户行") + private String bankDepositName; + + @ApiModelProperty(value = "商家备注") + private String auditRemark; + + @ApiModelProperty(value = "订单支付方式返回的交易号") + private String payOrderNo; + + @ApiModelProperty(value = "申请退款金额") + private Double applyRefundPrice; + + @ApiModelProperty(value = "实际退款金额") + private Double actualRefundPrice; + + @ApiModelProperty(value = "退还积分") + private Integer refundPoint; + + @ApiModelProperty(value = "退款时间") + private Date refundTime; + + // 买家物流信息 + @ApiModelProperty(value = "发货单号") + private String mLogisticsNo; + + @ApiModelProperty(value = "物流公司CODE") + private String mLogisticsCode; + + @ApiModelProperty(value = "物流公司名称") + private String mLogisticsName; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "买家发货时间") + private Date mDeliverTime; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/AfterSaleReason.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/AfterSaleReason.java new file mode 100644 index 00000000..88cc7ab9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/AfterSaleReason.java @@ -0,0 +1,31 @@ +package cn.lili.modules.order.order.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; + +@Data +@Entity +@Table(name = "li_after_sale_reason") +@TableName("li_after_sale_reason") +@ApiModel(value = "售后原因") +public class AfterSaleReason extends BaseEntity { + + @NotNull + @ApiModelProperty(value = "售后原因") + private String reason; + + /** + * @see cn.lili.modules.order.trade.entity.enums.AfterSaleTypeEnum + */ + @ApiModelProperty(value = "原因类型", allowableValues = "CANCEL,RETURN_GOODS,RETURN_MONEY,COMPLAIN") + @NotNull + private String serviceType; + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Order.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Order.java new file mode 100644 index 00000000..e3dbb029 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Order.java @@ -0,0 +1,258 @@ +package cn.lili.modules.order.order.entity.dos; + +import cn.hutool.json.JSONUtil; +import cn.lili.base.BaseEntity; +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum; +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import cn.lili.modules.order.order.entity.enums.DeliverStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderTypeEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; +import java.util.Optional; + +/** + * 订单 + * + * @author Chopper + * @date 2020/11/17 7:30 下午 + */ +@Data +@Entity +@Table(name = "li_order") +@TableName("li_order") +@ApiModel(value = "订单") +public class Order extends BaseEntity { + + + private static final long serialVersionUID = 2233811628066468683L; + @ApiModelProperty("订单编号") + private String sn; + + @ApiModelProperty("交易编号 关联Trade") + private String tradeSn; + + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "用户名") + private String memberName; + + /** + * @see OrderStatusEnum + */ + @ApiModelProperty(value = "订单状态") + private String orderStatus; + + /** + * @see PayStatusEnum + */ + @ApiModelProperty(value = "付款状态") + private String payStatus; + /** + * @see DeliverStatusEnum + */ + @ApiModelProperty(value = "货运状态") + private String deliverStatus; + + @ApiModelProperty(value = "第三方付款流水号") + private String receivableNo; + + @ApiModelProperty(value = "支付方式") + private String paymentMethod; + + @ApiModelProperty(value = "支付时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date paymentTime; + + @ApiModelProperty(value = "收件人姓名") + private String consigneeName; + + @ApiModelProperty(value = "收件人手机") + private String consigneeMobile; + + /** + * @see DeliveryMethodEnum + */ + @ApiModelProperty(value = "配送方式") + private String deliveryMethod; + + @ApiModelProperty(value = "地址名称, ','分割") + private String consigneeAddressPath; + + @ApiModelProperty(value = "地址id,','分割 ") + private String consigneeAddressIdPath; + + @ApiModelProperty(value = "详细地址") + private String consigneeDetail; + + @ApiModelProperty(value = "总价格") + private Double flowPrice; + + @ApiModelProperty(value = "商品价格") + private Double goodsPrice; + + @ApiModelProperty(value = "运费") + private Double freightPrice; + + @ApiModelProperty(value = "优惠的金额") + private Double discountPrice; + + //修改金额 + @ApiModelProperty(value = "修改价格") + private Double updatePrice; + + @ApiModelProperty(value = "发货单号") + private String logisticsNo; + + @ApiModelProperty(value = "物流公司CODE") + private String logisticsCode; + + @ApiModelProperty(value = "物流公司名称") + private String logisticsName; + + @ApiModelProperty(value = "订单商品总重量") + private Double weight; + + @ApiModelProperty(value = "商品数量") + private Integer goodsNum; + + @ApiModelProperty(value = "买家订单备注") + private String remark; + + @ApiModelProperty(value = "订单取消原因") + private String cancelReason; + + @ApiModelProperty(value = "完成时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date completeTime; + + @ApiModelProperty(value = "送货时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date logisticsTime; + + @ApiModelProperty(value = "支付方式返回的交易号") + private String payOrderNo; + + /** + * @see ClientTypeEnum + */ + @ApiModelProperty(value = "订单来源") + private String clientType; + + @ApiModelProperty(value = "是否需要发票") + private Boolean needReceipt; + + @ApiModelProperty(value = "是否为其他订单下的订单,如果是则为依赖订单的sn,否则为空") + private String parentOrderSn; + + @ApiModelProperty(value = "是否为某订单类型的订单,如果是则为订单类型的id,否则为空") + private String promotionId; + + /** + * @see OrderTypeEnum + */ + @ApiModelProperty(value = "订单类型") + private String orderType; + + @Column(columnDefinition = "TEXT") + @ApiModelProperty(value = "价格详情") + private String priceDetail; + + @ApiModelProperty(value = "订单是否支持原路退回") + private Boolean canReturn; + + @ApiModelProperty(value = "提货码") + private String qrCode; + + @ApiModelProperty(value = "分销员ID") + private String distributionId; + + @ApiModelProperty(value = "使用的店铺会员优惠券id(,区分)") + private String useStoreMemberCouponIds; + + @ApiModelProperty(value = "使用的平台会员优惠券id") + private String usePlatformMemberCouponId; + + public Order() { + + } + + public Order(CartVO cartVO, TradeDTO tradeDTO) { + String oldId = this.getId(); + if (tradeDTO.getMemberAddress() != null) { + BeanUtil.copyProperties(tradeDTO.getMemberAddress(), this); + } + BeanUtil.copyProperties(tradeDTO, this); + BeanUtil.copyProperties(cartVO.getPriceDetailDTO(), this); + BeanUtil.copyProperties(cartVO, this); + this.setId(oldId); + this.setOrderType(OrderTypeEnum.NORMAL.name()); + if (cartVO.getSkuList().get(0).getPromotions() != null) { + Optional pintuanId = cartVO.getSkuList().get(0).getPromotions().stream().filter(i -> i.getPromotionType().equals(PromotionTypeEnum.PINTUAN.name())).map(PromotionGoods::getPromotionId).findFirst(); + if (pintuanId.isPresent()) { + promotionId = pintuanId.get(); + this.setOrderType(OrderTypeEnum.PINTUAN.name()); + if (tradeDTO.getParentOrderSn() == null) { + this.setParentOrderSn(""); + } + } + } + this.setOrderStatus(OrderStatusEnum.UNPAID.name()); + this.setPayStatus(PayStatusEnum.UNPAID.name()); + this.setDeliverStatus(DeliverStatusEnum.UNDELIVERED.name()); + this.setConsigneeAddressIdPath(tradeDTO.getMemberAddress().getConsigneeAddressIdPath()); + this.setConsigneeAddressPath(tradeDTO.getMemberAddress().getConsigneeAddressPath()); + this.setConsigneeDetail(tradeDTO.getMemberAddress().getDetail()); + this.setConsigneeMobile(tradeDTO.getMemberAddress().getMobile()); + this.setConsigneeName(tradeDTO.getMemberAddress().getName()); + if (tradeDTO.getPlatformCoupon() != null) { + this.setUsePlatformMemberCouponId(tradeDTO.getPlatformCoupon().getMemberCoupon().getId()); + } + if (tradeDTO.getStoreCoupons() != null && !tradeDTO.getStoreCoupons().isEmpty()) { + StringBuilder storeCouponIds = new StringBuilder(); + for (String s : tradeDTO.getStoreCoupons().keySet()) { + storeCouponIds.append(s).append(","); + } + this.setUseStoreMemberCouponIds(storeCouponIds.toString()); + } + this.setTradeSn(tradeDTO.getSn()); + this.setRemark(cartVO.getRemark()); + this.setFreightPrice(tradeDTO.getPriceDetailDTO().getFreightPrice()); + } + + public PriceDetailDTO getPriceDetailDTO() { + + try { + return JSONUtil.toBean(priceDetail, PriceDetailDTO.class); + } catch (Exception e) { + return null; + } + } + + public void setPriceDetailDTO(PriceDetailDTO priceDetail) { + this.priceDetail = JSONUtil.toJsonStr(priceDetail); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderComplaint.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderComplaint.java new file mode 100644 index 00000000..4529f36c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderComplaint.java @@ -0,0 +1,115 @@ +package cn.lili.modules.order.order.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.order.order.entity.enums.ComplaintStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; + +/** + * 订单交易投诉 + * + * @author paulG + * @since 2020/12/4 + **/ +@Data +@Entity +@Table(name = "li_order_complaint") +@TableName("li_order_complaint") +@ApiModel(value = "订单交易投诉") +public class OrderComplaint extends BaseEntity { + + private static final long serialVersionUID = 7185050229757228184L; + + + @ApiModelProperty(value = "投诉主题") + private String complainTopic; + + @ApiModelProperty(value = "投诉内容") + private String content; + + @ApiModelProperty(value = "投诉凭证图片") + private String images; + + /** + * @see ComplaintStatusEnum + */ + @ApiModelProperty(value = "交易投诉状态") + private String complainStatus; + + @ApiModelProperty(value = "申诉商家内容") + private String appealContent; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "申诉商家时间") + private Date appealTime; + + @ApiModelProperty(value = "申诉商家上传的图片") + private String appealImages; + + @ApiModelProperty(value = "订单号") + private String orderSn; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "下单时间") + private Date orderTime; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品id") + private String goodsId; + + @ApiModelProperty(value = "sku主键") + private String skuId; + + @ApiModelProperty(value = "商品价格") + private Double goodsPrice; + + @ApiModelProperty(value = "商品图片") + private String goodsImage; + + @ApiModelProperty(value = "购买的商品数量") + private Integer num; + + @ApiModelProperty(value = "运费") + private Double freightPrice; + + @ApiModelProperty(value = "订单金额") + private Double orderPrice; + + @ApiModelProperty(value = "物流单号") + private String logisticsNo; + + @ApiModelProperty(value = "商家id") + private String storeId; + + @ApiModelProperty(value = "商家名称") + private String storeName; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "收货人") + private String consigneeName; + + @ApiModelProperty(value = "收货地址") + private String consigneeAddressPath; + + @ApiModelProperty(value = "收货人手机") + private String consigneeMobile; + + @ApiModelProperty(value = "仲裁结果") + private String arbitrationResult; + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderComplaintCommunication.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderComplaintCommunication.java new file mode 100644 index 00000000..5ed5fd88 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderComplaintCommunication.java @@ -0,0 +1,59 @@ +package cn.lili.modules.order.order.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 交易投诉通信 + * + * @author paulG + * @since 2020/12/5 + **/ +@Data +@Entity +@Table(name = "li_order_complaint_communication") +@TableName("li_order_complaint_communication") +@ApiModel(value = "订单交易投诉通信") +@AllArgsConstructor +@NoArgsConstructor +public class OrderComplaintCommunication extends BaseEntity { + + private static final long serialVersionUID = -2384351827382795547L; + + /** + * 投诉id + */ + @ApiModelProperty(value = "投诉id") + private String complainId; + /** + * 对话内容 + */ + @ApiModelProperty(value = "对话内容") + private String content; + /** + * 所属,买家/卖家 + */ + @ApiModelProperty(value = "所属,买家/卖家") + private String owner; + /** + * 对话所属名称 + */ + @ApiModelProperty(value = "对话所属名称") + private String ownerName; + /** + * 对话所属id,卖家id/买家id + */ + @ApiModelProperty(value = "对话所属id,卖家id/买家id") + private String ownerId; + + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderItem.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderItem.java new file mode 100644 index 00000000..cd59e1b5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/OrderItem.java @@ -0,0 +1,149 @@ +package cn.lili.modules.order.order.entity.dos; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.base.BaseEntity; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.SnowFlake; +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import cn.lili.modules.order.order.entity.enums.CommentStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderComplaintStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderItemAfterSaleStatusEnum; +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.stream.Collectors; + +/** + * 子订单 + * + * @author Chopper + * @date 2020/11/17 7:30 下午 + */ +@Data +@Entity +@Table(name = "li_order_item") +@TableName("li_order_item") +@ApiModel(value = "子订单") +@NoArgsConstructor +@AllArgsConstructor +public class OrderItem extends BaseEntity { + + private static final long serialVersionUID = 2108971190191410182L; + + @ApiModelProperty(value = "订单编号") + private String orderSn; + + @ApiModelProperty(value = "子订单编号") + private String sn; + + @ApiModelProperty(value = "单价") + private Double unitPrice; + + @ApiModelProperty(value = "小记") + private Double subTotal; + + @ApiModelProperty(value = "商品ID") + private String goodsId; + @ApiModelProperty(value = "货品ID") + private String skuId; + @ApiModelProperty(value = "销售量") + private Integer num; + @ApiModelProperty(value = "交易编号") + private String tradeSn; + @ApiModelProperty(value = "图片") + private String image; + @ApiModelProperty(value = "商品名称") + private String goodsName; + @ApiModelProperty(value = "分类ID") + private String categoryId; + @ApiModelProperty(value = "快照id") + private String snapshotId; + @ApiModelProperty(value = "规格json") + @Column(columnDefinition = "TEXT") + private String specs; + @ApiModelProperty(value = "促销类型") + private String promotionType; + @ApiModelProperty(value = "促销id") + private String promotionId; + @ApiModelProperty(value = "销售金额") + private Double goodsPrice; + + @ApiModelProperty(value = "实际金额") + private Double flowPrice; + + /** + * @see CommentStatusEnum + */ + @ApiModelProperty(value = "评论状态:未评论(UNFINISHED),待追评(WAIT_CHASE),评论完成(FINISHED),") + private String commentStatus; + + /** + * @see OrderItemAfterSaleStatusEnum + */ + @ApiModelProperty(value = "售后状态") + private String afterSaleStatus; + + @Column(columnDefinition = "TEXT") + @ApiModelProperty(value = "价格详情") + private String priceDetail; + + /** + * @see OrderComplaintStatusEnum + */ + @ApiModelProperty(value = "投诉状态") + private String complainStatus; + + @ApiModelProperty(value = "交易投诉id") + private String complainId; + + public OrderItem(CartSkuVO cartSkuVO, CartVO cartVO, TradeDTO tradeDTO) { + String oldId = this.getId(); + BeanUtil.copyProperties(cartSkuVO.getGoodsSku(), this); + BeanUtil.copyProperties(cartSkuVO.getPriceDetailDTO(), this); + BeanUtil.copyProperties(cartSkuVO, this); + this.setId(oldId); + if (cartSkuVO.getPriceDetailDTO().getJoinPromotion() != null && !cartSkuVO.getPriceDetailDTO().getJoinPromotion().isEmpty()) { + this.setPromotionType(CollUtil.join(cartSkuVO.getPriceDetailDTO().getJoinPromotion().stream().map(BasePromotion::getPromotionName).collect(Collectors.toList()), ",")); + this.setPromotionId(CollUtil.join(cartSkuVO.getPriceDetailDTO().getJoinPromotion().stream().map(BasePromotion::getId).collect(Collectors.toList()), ",")); + } + this.setAfterSaleStatus(OrderItemAfterSaleStatusEnum.NEW.name()); + this.setCommentStatus(CommentStatusEnum.NEW.name()); + this.setComplainStatus(OrderComplaintStatusEnum.NEW.name()); + this.setPriceDetailDTO(cartSkuVO.getPriceDetailDTO()); + this.setOrderSn(cartVO.getSn()); + this.setTradeSn(tradeDTO.getSn()); + this.setImage(cartSkuVO.getGoodsSku().getThumbnail()); + this.setGoodsName(cartSkuVO.getGoodsSku().getGoodsName()); + this.setSkuId(cartSkuVO.getGoodsSku().getId()); + this.setCategoryId(cartSkuVO.getGoodsSku().getCategoryPath().substring( + cartSkuVO.getGoodsSku().getCategoryPath().lastIndexOf(",") + 1 + )); + this.setGoodsPrice(cartSkuVO.getGoodsSku().getPrice()); + this.setUnitPrice(cartSkuVO.getPurchasePrice()); + this.setSubTotal(cartSkuVO.getSubTotal()); + this.setSn(SnowFlake.createStr("OI")); + + + } + + public PriceDetailDTO getPriceDetailDTO() { + return JSONUtil.toBean(priceDetail, PriceDetailDTO.class); + } + + public void setPriceDetailDTO(PriceDetailDTO priceDetail) { + this.priceDetail = JSONUtil.toJsonStr(priceDetail); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Receipt.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Receipt.java new file mode 100644 index 00000000..f4b4df72 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Receipt.java @@ -0,0 +1,62 @@ +package cn.lili.modules.order.order.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 发票 + * + * @author Bulbasaur + * @date 2020/11/28 11:38 + */ +@Data +@Entity +@Table(name = "li_receipt") +@TableName("li_receipt") +@ApiModel(value = "发票") +public class Receipt extends BaseEntity { + + private static final long serialVersionUID = -8210927482915675995L; + + @ApiModelProperty(value = "订单编号") + private String orderSn; + + @ApiModelProperty(value = "发票抬头") + private String receiptTitle; + + @ApiModelProperty(value = "纳税人识别号") + private String taxpayerId; + + @ApiModelProperty(value = "发票内容") + private String receiptContent; + + @ApiModelProperty(value = "发票金额") + private Double receiptPrice; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "商家ID") + private String storeId; + + @ApiModelProperty(value = "商家名称") + private String storeName; + + @ApiModelProperty(value = "发票状态 0未开 1已开") + private Integer receiptStatus; + + @Column(columnDefinition = "TEXT") + @ApiModelProperty(value = "发票详情") + private String receiptDetail; + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/StoreFlow.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/StoreFlow.java new file mode 100644 index 00000000..c4747861 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/StoreFlow.java @@ -0,0 +1,131 @@ +package cn.lili.modules.order.order.entity.dos; + +import cn.lili.modules.order.order.entity.enums.FlowTypeEnum; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 商家订单流水 + * + * @author Chopper + * @date 2020/11/17 7:31 下午 + */ +@Data +@Entity +@Table(name = "li_store_flow") +@TableName("li_store_flow") +@ApiModel(value = "商家订单流水") +public class StoreFlow { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + private static final long serialVersionUID = -5998757398902747939L; + + @ApiModelProperty(value = "流水编号") + private String sn; + + @ApiModelProperty(value = "订单sn") + private String orderSn; + + @ApiModelProperty(value = "子订单sn") + private String orderItemSn; + + @ApiModelProperty(value = "售后SN") + private String refundSn; + + @ApiModelProperty(value = "商家id") + private String storeId; + + @ApiModelProperty(value = "商家名称 ") + private String storeName; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + + @ApiModelProperty(value = "商品ID") + private String goodsId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "货品ID") + private String skuId; + + @ApiModelProperty(value = "图片") + private String image; + + @ApiModelProperty(value = "分类ID") + private String categoryId; + + @ApiModelProperty(value = "规格json") + @Column(columnDefinition = "TEXT") + private String specs; + + + /** + * @see FlowTypeEnum + */ + @ApiModelProperty(value = "流水类型:PAY/REFUND 支付/退款", allowableValues = "PAY,REFUND") + private String flowType; + + + @ApiModelProperty(value = "平台优惠券 使用金额") + private Double siteCouponPrice; + + @ApiModelProperty(value = "站点优惠券佣金比例") + private Double siteCouponPoint; + + @ApiModelProperty(value = "站点优惠券佣金") + private Double siteCouponCommission; + + @ApiModelProperty(value = "单品分销返现支出") + private Double distributionRebate; + + @ApiModelProperty(value = "平台收取交易佣金") + private Double commissionPrice; + + @ApiModelProperty(value = "流水金额") + private Double finalPrice; + + @ApiModelProperty(value = "最终结算金额") + private Double billPrice; + + @ApiModelProperty(value = "第三方交易流水号") + private String transactionId; + + @ApiModelProperty(value = "支付方式名称") + private String paymentName; + + @ApiModelProperty(value = "销售量") + private Integer num; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Trade.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Trade.java new file mode 100644 index 00000000..20966d8c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dos/Trade.java @@ -0,0 +1,91 @@ +package cn.lili.modules.order.order.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 交易 + * + * @author Chopper + * @date 2020/11/17 7:34 下午 + */ +@Data +@Entity +@Table(name = "li_trade") +@TableName("li_trade") +@ApiModel(value = "交易") +@NoArgsConstructor +public class Trade extends BaseEntity { + + private static final long serialVersionUID = 5177608752643561827L; + + @ApiModelProperty(value = "交易编号") + private String sn; + + @ApiModelProperty(value = "买家id") + private String memberId; + + @ApiModelProperty(value = "买家用户名") + private String memberName; + + @ApiModelProperty(value = "支付方式") + private String paymentMethod; + + /** + * @see cn.lili.modules.order.order.entity.enums.PayStatusEnum + */ + @ApiModelProperty(value = "付款状态") + private String payStatus; + + @ApiModelProperty(value = "总价格") + private Double flowPrice; + + @ApiModelProperty(value = "原价") + private Double goodsPrice; + + @ApiModelProperty(value = "运费") + private Double freightPrice; + + @ApiModelProperty(value = "优惠的金额") + private Double discountPrice; + + /** + * @see DeliveryMethodEnum + */ + @ApiModelProperty(value = "配送方式") + private String deliveryMethod; + + @ApiModelProperty(value = "收货人姓名") + private String consigneeName; + + @ApiModelProperty(value = "收件人手机") + private String consigneeMobile; + + @ApiModelProperty(value = "地址名称, ','分割") + private String consigneeAddressPath; + + @ApiModelProperty(value = "地址id,','分割 ") + private String consigneeAddressIdPath; + + public Trade(TradeDTO tradeDTO) { + String originId = this.getId(); + if (tradeDTO.getMemberAddress() != null) { + BeanUtil.copyProperties(tradeDTO.getMemberAddress(), this); + this.setConsigneeMobile(tradeDTO.getMemberAddress().getMobile()); + this.setConsigneeName(tradeDTO.getMemberAddress().getName()); + } + BeanUtil.copyProperties(tradeDTO, this); + BeanUtil.copyProperties(tradeDTO.getPriceDetailDTO(), this); + this.setId(originId); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dto/AfterSaleDTO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/AfterSaleDTO.java new file mode 100644 index 00000000..e2fba22b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/AfterSaleDTO.java @@ -0,0 +1,62 @@ +package cn.lili.modules.order.order.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 售后dto + * + * @author Chopper + * @date 2021/3/20 10:48 + */ +@Data +public class AfterSaleDTO { + + @ApiModelProperty(value = "订单SN") + private String orderItemSn; + + @ApiModelProperty(value = "商品ID") + private String goodsId; + + @ApiModelProperty(value = "货品ID") + private String skuId; + + @ApiModelProperty(value = "申请退款金额") + private Double applyRefundPrice; + + @ApiModelProperty(value = "申请数量") + private Integer num; + + @ApiModelProperty(value = "申请原因") + private String reason; + + @ApiModelProperty(value = "问题描述") + private String problemDesc; + + @ApiModelProperty(value = "售后图片") + private String images; + + /** + * @see cn.lili.modules.order.trade.entity.enums.AfterSaleTypeEnum + */ + @ApiModelProperty(value = "售后类型", allowableValues = "RETURN_GOODS,EXCHANGE_GOODS,RETURN_MONEY") + private String serviceType; + + /** + * @see cn.lili.modules.order.trade.entity.enums.AfterSaleRefundWayEnum + */ + @ApiModelProperty(value = "退款方式", allowableValues = "ORIGINAL,OFFLINE") + private String refundWay; + + @ApiModelProperty(value = "账号类型", allowableValues = "ALIPAY,WECHATPAY,BANKTRANSFER") + private String accountType; + + @ApiModelProperty(value = "银行开户行") + private String bankDepositName; + + @ApiModelProperty(value = "银行开户名") + private String bankAccountName; + + @ApiModelProperty(value = "银行卡号") + private String bankAccountNumber; +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dto/AfterSalePriceDetailDTO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/AfterSalePriceDetailDTO.java new file mode 100644 index 00000000..613912f5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/AfterSalePriceDetailDTO.java @@ -0,0 +1,100 @@ +package cn.lili.modules.order.order.entity.dto; + + +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 商城退款流水 + * + * @author Chopper + * @date 2020/11/17 7:25 下午 + */ +@Data +public class AfterSalePriceDetailDTO implements Serializable { + + + private static final long serialVersionUID = 8808470688518188146L; + @ApiModelProperty(value = "商品总金额(商品原价)") + private Double goodsPrice; + + @ApiModelProperty(value = "配送费") + private Double freightPrice; + + //============discount price============ + + @ApiModelProperty(value = "支付积分") + private Integer payPoint; + + @ApiModelProperty(value = "优惠金额") + private Double discountPrice; + + @ApiModelProperty(value = "优惠券金额") + private Double couponPrice; + + //===========end discount price ============= + + + //=========distribution========== + + @ApiModelProperty(value = "单品分销返现支出") + private Double distributionCommission; + + + @ApiModelProperty(value = "平台收取交易佣金") + private Double platFormCommission; + + //=========end distribution========== + + + //========= platform coupon========== + + @ApiModelProperty(value = "平台优惠券 使用金额") + private Double siteCouponPrice; + + @ApiModelProperty(value = "站点优惠券佣金比例") + private Double siteCouponPoint; + + @ApiModelProperty(value = "站点优惠券佣金") + private Double siteCouponCommission; + //=========end platform coupon========== + + @ApiModelProperty(value = "流水金额(入账 出帐金额) = goodsPrice - discountPrice - couponPrice") + private Double flowPrice; + + @ApiModelProperty(value = "最终结算金额 = flowPrice - platFormCommission - distributionCommission") + private Double billPrice; + + /** + * 参与的促销活动 + */ + @ApiModelProperty(value = "参与的促销活动") + private List joinPromotion; + + + public AfterSalePriceDetailDTO() { + goodsPrice = 0d; + freightPrice = 0d; + + payPoint = 0; + discountPrice = 0d; + + distributionCommission = 0d; + platFormCommission = 0d; + + siteCouponPrice = 0d; + siteCouponPoint = 0d; + siteCouponCommission = 0d; + + flowPrice = 0d; + billPrice = 0d; + + joinPromotion = new ArrayList<>(); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderComplaintDTO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderComplaintDTO.java new file mode 100644 index 00000000..a6588751 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderComplaintDTO.java @@ -0,0 +1,39 @@ +package cn.lili.modules.order.order.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * 交易投诉DTO + * + * @author Bulbasaur + * @date 2021/1/7 19:39 + */ +@Data +public class OrderComplaintDTO { + + @NotBlank + @ApiModelProperty(value = "投诉主题") + private String complainTopic; + + @NotBlank + @ApiModelProperty(value = "投诉内容") + private String content; + + @ApiModelProperty(value = "投诉凭证图片") + private String images; + + @NotBlank + @ApiModelProperty(value = "订单号") + private String orderSn; + + @NotBlank + @ApiModelProperty(value = "商品id") + private String goodsId; + + @NotBlank + @ApiModelProperty(value = "sku主键") + private String skuId; +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderMessage.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderMessage.java new file mode 100644 index 00000000..91e49264 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderMessage.java @@ -0,0 +1,37 @@ +package cn.lili.modules.order.order.entity.dto; + +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 信息队列传输订单信息实体 + * + * @author paulG + * @since 2020/12/9 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class OrderMessage { + + + /** + * 订单号 + */ + private String orderSn; + + /** + * 新状态 + * + * @see OrderStatusEnum + */ + private OrderStatusEnum newStatus; + + /** + * 支付方式 + */ + private String paymentMethod; + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderReceiptDTO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderReceiptDTO.java new file mode 100644 index 00000000..460305fb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderReceiptDTO.java @@ -0,0 +1,22 @@ +package cn.lili.modules.order.order.entity.dto; + +import cn.lili.modules.order.order.entity.dos.Receipt; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + + +/** + * 订单发票 + * + * @author lili + * @date 2020/11/28 11:38 + */ +@Data +@ApiModel(value = "订单发票") +public class OrderReceiptDTO extends Receipt { + + @ApiModelProperty(value = "订单状态") + private String orderStatus; + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderSearchParams.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderSearchParams.java new file mode 100644 index 00000000..8e561ce6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/OrderSearchParams.java @@ -0,0 +1,211 @@ +package cn.lili.modules.order.order.entity.dto; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderTagEnum; +import cn.lili.modules.order.order.entity.enums.OrderTypeEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 订单查询参数 + * + * @author Chopper + * @date 2020/11/17 4:33 下午 + */ +@Data +public class OrderSearchParams extends PageVO { + + private static final long serialVersionUID = -6380573339089959194L; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "订单编号") + private String orderSn; + + @ApiModelProperty(value = "页面标签", + example = "ALL:全部," + + "WAIT_PAY:待付款," + + "WAIT_ROG:待收货," + + "CANCELLED:已取消," + + "COMPLETE:已完成") + private String tag; + + @ApiModelProperty(value = "商家ID") + private String storeId; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "收货人") + private String shipName; + + @ApiModelProperty(value = "买家昵称") + private String buyerName; + + @ApiModelProperty(value = "订单状态") + private String orderStatus; + + @ApiModelProperty(value = "付款状态") + private String payStatus; + + @ApiModelProperty(value = "关键字 商品名称/买家名称/店铺名称") + private String keywords; + + @ApiModelProperty(value = "付款方式") + private String paymentType; + + /** + * @see OrderTypeEnum + */ + @ApiModelProperty(value = "订单类型") + private String orderType; + + @ApiModelProperty(value = "支付方式") + private String paymentMethod; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "支付时间") + private Date paymentTime; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "下单开始时间") + private Date startDate; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "下单结束时间") + private Date endDate; + + @ApiModelProperty(value = "订单来源") + private String clientType; + + public QueryWrapper queryWrapper() { + QueryWrapper wrapper = new QueryWrapper<>(); + + //关键字查询 + if (StringUtils.isNotEmpty(keywords)) { + wrapper.like("o.sn", keywords); + wrapper.like("oi.goods_name", keywords); + } + // 按卖家查询 + if (StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name())) { + wrapper.eq("o.store_id", UserContext.getCurrentUser().getStoreId()); + } + if (StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MANAGER.name()) + && StringUtils.isNotEmpty(storeId)) { + wrapper.eq("o.store_id", storeId); + } + // 按买家查询 + if (StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MEMBER.name())) { + wrapper.eq("o.member_id", UserContext.getCurrentUser().getId()); + } + // 按照买家查询 + if (StringUtils.isNotEmpty(memberId)) { + wrapper.like("o.member_id", memberId); + } + + // 按订单编号查询 + if (StringUtils.isNotEmpty(orderSn)) { + wrapper.like("o.sn", orderSn); + } + + // 按时间查询 + if (startDate != null) { + wrapper.ge("o.create_time", startDate); + } + if (endDate != null) { + wrapper.le("o.create_time", DateUtil.endOfDate(endDate)); + } + // 按购买人用户名 + if (StringUtils.isNotEmpty(buyerName)) { + wrapper.like("o.member_name", buyerName); + } + + // 按订单类型 + if (StringUtils.isNotEmpty(orderType)) { + wrapper.eq("o.order_type", orderType); + } + + // 按支付方式 + if (StringUtils.isNotEmpty(paymentMethod)) { + wrapper.eq("o.payment_method", paymentMethod); + } + + // 按标签查询 + if (StringUtils.isNotEmpty(tag)) { + String orderStatusColumn = "o.order_status"; + OrderTagEnum tagEnum = OrderTagEnum.valueOf(tag); + switch (tagEnum) { + //待付款 + case WAIT_PAY: + wrapper.eq(orderStatusColumn, OrderStatusEnum.UNPAID.name()); + break; + //待发货 + case WAIT_SHIP: + wrapper.eq(orderStatusColumn, OrderStatusEnum.UNDELIVERED.name()); + break; + //待收货 + case WAIT_ROG: + wrapper.eq(orderStatusColumn, OrderStatusEnum.DELIVERED.name()); + break; + //已取消 + case CANCELLED: + wrapper.eq(orderStatusColumn, OrderStatusEnum.CANCELLED.name()); + break; + //已完成 + case COMPLETE: + wrapper.eq(orderStatusColumn, OrderStatusEnum.COMPLETED.name()); + break; + default: + break; + } + } + + if (StringUtils.isNotEmpty(shipName)) { + wrapper.like("o.ship_name", shipName); + } + // 按商品名称查询 + if (StringUtils.isNotEmpty(goodsName)) { + wrapper.like("oi.goods_name", goodsName); + } + + //付款方式 + if (StringUtils.isNotEmpty(paymentType)) { + wrapper.like("o.payment_type", paymentType); + } + + //订单状态 + if (StringUtils.isNotEmpty(orderStatus)) { + wrapper.eq("o.order_status", orderStatus); + } + + //付款状态 + if (StringUtils.isNotEmpty(payStatus)) { + wrapper.eq("o.pay_status", payStatus); + } + + //订单来源 + if (StringUtils.isNotEmpty(clientType)) { + wrapper.like("o.client_type", clientType); + } + wrapper.eq("o.delete_flag", false); + wrapper.groupBy("o.id"); + wrapper.orderByDesc("o.id"); + + return wrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dto/PriceDetailDTO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/PriceDetailDTO.java new file mode 100644 index 00000000..e7eec6e4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/PriceDetailDTO.java @@ -0,0 +1,205 @@ +package cn.lili.modules.order.order.entity.dto; + + +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 商城流水,细节到orderItem + * + * @author Chopper + * @date 2020/11/17 7:25 下午 + */ +@Data +public class PriceDetailDTO implements Serializable { + + //用于订单价格修改金额计算使用 + @ApiModelProperty(value = "订单原始总价格" ) + private Double originalPrice; + + private static final long serialVersionUID = 8808470688518188146L; + @ApiModelProperty(value = "商品总金额(商品原价)") + private Double goodsPrice; + + @ApiModelProperty(value = "配送费") + private Double freightPrice; + + //============discount price============ + + @ApiModelProperty(value = "支付积分") + private Integer payPoint; + + @ApiModelProperty(value = "优惠金额") + private Double discountPrice; + + @ApiModelProperty(value = "优惠券金额") + private Double couponPrice; + + //===========end discount price ============= + + + //=========distribution========== + + @ApiModelProperty(value = "单品分销返现支出") + private Double distributionCommission; + + + @ApiModelProperty(value = "平台收取交易佣金") + private Double platFormCommission; + + //=========end distribution========== + + + //========= platform coupon========== + + @ApiModelProperty(value = "平台优惠券 使用金额") + private Double siteCouponPrice; + + @ApiModelProperty(value = "站点优惠券佣金比例") + private Double siteCouponPoint; + + @ApiModelProperty(value = "站点优惠券佣金") + private Double siteCouponCommission; + //=========end platform coupon========== + + //========= update price ========== + @ApiModelProperty(value = "订单修改金额") + private Double updatePrice; + + //=========end update price========== + @ApiModelProperty(value = "流水金额(入账 出帐金额) = goodsPrice + freight - discountPrice - couponPrice + updatePrice") + private Double flowPrice; + + @ApiModelProperty(value = "最终结算金额 = flowPrice - platFormCommission - distributionCommission") + private Double billPrice; + + /** + * 参与的促销活动 + */ + @ApiModelProperty(value = "参与的促销活动") + private List joinPromotion; + + + public PriceDetailDTO() { + goodsPrice = 0d; + freightPrice = 0d; + + payPoint = 0; + discountPrice = 0d; + + distributionCommission = 0d; + platFormCommission = 0d; + + siteCouponPrice = 0d; + siteCouponPoint = 0d; + siteCouponCommission = 0d; + + updatePrice = 0d; + + flowPrice = 0d; + billPrice = 0d; + + joinPromotion = new ArrayList<>(); + } + + /** + * 累加 + * + * @param priceDetailDTOS + * @return + */ + public static PriceDetailDTO accumulationPriceDTO(List priceDetailDTOS, PriceDetailDTO originPriceDetail) { + + + double goodsPrice = 0d; + double freightPrice = 0d; + + int payPoint = 0; + double discountPrice = 0d; + + double distributionCommission = 0d; + double platFormCommission = 0d; + + double siteCouponPrice = 0d; + double siteCouponPoint = 0d; + double siteCouponCommission = 0d; + + double updatePrice = 0d; + + double flowPrice = 0d; + double billPrice = 0d; + + for (PriceDetailDTO price : priceDetailDTOS) { + + goodsPrice = CurrencyUtil.add(goodsPrice, price.getGoodsPrice()); + freightPrice = CurrencyUtil.add(freightPrice, price.getFreightPrice()); + payPoint = payPoint + price.getPayPoint(); + discountPrice = CurrencyUtil.add(discountPrice, price.getDiscountPrice()); + + updatePrice = CurrencyUtil.add(updatePrice, price.getUpdatePrice()); + + + distributionCommission = CurrencyUtil.add(distributionCommission, price.getDistributionCommission()); + platFormCommission = CurrencyUtil.add(platFormCommission, price.getPlatFormCommission()); + + siteCouponPrice = CurrencyUtil.add(siteCouponPrice, price.getSiteCouponPrice()); + siteCouponPoint = CurrencyUtil.add(siteCouponPoint, price.getSiteCouponPoint()); + siteCouponCommission = CurrencyUtil.add(siteCouponCommission, price.getSiteCouponCommission()); + + flowPrice = CurrencyUtil.add(flowPrice, price.getFlowPrice()); + billPrice = CurrencyUtil.add(billPrice, price.getBillPrice()); + + } + originPriceDetail.setGoodsPrice(goodsPrice); + originPriceDetail.setFreightPrice(freightPrice); + originPriceDetail.setPayPoint(payPoint); + originPriceDetail.setUpdatePrice(updatePrice); + originPriceDetail.setDiscountPrice(discountPrice); + + originPriceDetail.setDistributionCommission(distributionCommission); + originPriceDetail.setPlatFormCommission(platFormCommission); + + originPriceDetail.setSiteCouponPrice(siteCouponPrice); + originPriceDetail.setSiteCouponPoint(siteCouponPoint); + originPriceDetail.setSiteCouponCommission(siteCouponCommission); + + originPriceDetail.setFlowPrice(flowPrice); + originPriceDetail.setBillPrice(billPrice); + + return originPriceDetail; + } + + /** + * 累加 + * + * @param priceDetailDTOS + * @return + */ + public static Double sumGoodsPrice(List priceDetailDTOS) { + + + double goodsPrice = 0d; + + for (PriceDetailDTO price : priceDetailDTOS) { + + goodsPrice = CurrencyUtil.add(goodsPrice, price.getGoodsPrice()); + + } + + return goodsPrice; + } + + /** + * 自身计价 + */ + public void count() { + this.flowPrice = CurrencyUtil.sub(CurrencyUtil.add(goodsPrice, freightPrice), discountPrice); + //this.billPrice = CurrencyUtil.sub(CurrencyUtil.sub(CurrencyUtil.sub(flowPrice, platFormCommission)), distributionCommission); + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dto/ReceiptDTO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/ReceiptDTO.java new file mode 100644 index 00000000..e0b82ff4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/ReceiptDTO.java @@ -0,0 +1,35 @@ +package cn.lili.modules.order.order.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; + +/** + * 发票子内容 + * + * @author Bulbasaur + * @date 2020/11/28 11:44 + */ +@Data +public class ReceiptDTO { + + @ApiModelProperty(value = "发票ID") + private String receiptId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "规格") + @Column(columnDefinition = "TEXT") + private String specs; + + @ApiModelProperty(value = "数量") + private Integer num; + + @ApiModelProperty(value = "单价") + private Double goodPrice; + + @ApiModelProperty(value = "小计") + private Double subtotal; +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/dto/ReceiptSearchParams.java b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/ReceiptSearchParams.java new file mode 100644 index 00000000..496d259b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/dto/ReceiptSearchParams.java @@ -0,0 +1,71 @@ +package cn.lili.modules.order.order.entity.dto; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 发票搜索参数 + * + * @author paulG + * @date 2021/1/12 + **/ +@Data +public class ReceiptSearchParams { + + @ApiModelProperty(value = "发票抬头") + private String receiptTitle; + + @ApiModelProperty(value = "纳税人识别号") + private String taxpayerId; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "商家ID") + private String storeId; + + @ApiModelProperty(value = "订单号") + private String orderSn; + + @ApiModelProperty(value = "发票状态") + private String receiptStatus; + + public QueryWrapper wrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StrUtil.isNotEmpty(receiptTitle)) { + queryWrapper.like("r.receipt_title", receiptTitle); + } + if (StrUtil.isNotEmpty(taxpayerId)) { + queryWrapper.like("r.taxpayer_id", taxpayerId); + } + if (StrUtil.isNotEmpty(memberId)) { + queryWrapper.eq("r.member_id", memberId); + } + if (StrUtil.isNotEmpty(storeName)) { + queryWrapper.like("r.store_name", storeName); + } + if (StrUtil.isNotEmpty(storeId)) { + queryWrapper.eq("r.store_id", storeId); + } + if (StrUtil.isNotEmpty(memberName)) { + queryWrapper.like("r.member_name", memberName); + } + if (StrUtil.isNotEmpty(receiptStatus)) { + queryWrapper.like("r.receipt_status", receiptStatus); + } + if (StrUtil.isNotEmpty(orderSn)) { + queryWrapper.like("r.order_sn", orderSn); + } + queryWrapper.eq("r.delete_flag", false); + return queryWrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/CommentStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/CommentStatusEnum.java new file mode 100644 index 00000000..eb1f7f43 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/CommentStatusEnum.java @@ -0,0 +1,40 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 评论状态枚举 + * + * @author Chopper + * @date 2020/11/17 7:25 下午 + */ +public enum CommentStatusEnum { + + /** + * 新订单,不能进行评论 + */ + NEW("新订单,不能进行评论"), + /** + * 未完成的评论 + */ + UNFINISHED("未完成评论"), + + /** + * 待追评的评论信息 + */ + WAIT_CHASE("待追评评论"), + + /** + * 已经完成评论 + */ + FINISHED("已经完成评论"); + + private final String description; + + CommentStatusEnum(String description) { + this.description = description; + } + + public String description() { + return this.description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/CommunicationOwnerEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/CommunicationOwnerEnum.java new file mode 100644 index 00000000..de7cd1ba --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/CommunicationOwnerEnum.java @@ -0,0 +1,36 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 投诉角色枚举 + * + * @author paulG + * @date 2020/12/7 + **/ +public enum CommunicationOwnerEnum { + + /** + * 买家 + */ + BUYER("买家"), + + /** + * 卖家 + */ + STORE("卖家"), + + /** + * 平台 + */ + PLATFORM("平台"); + + private final String description; + + CommunicationOwnerEnum(String description) { + this.description = description; + } + + public String description() { + return this.description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/ComplaintStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/ComplaintStatusEnum.java new file mode 100644 index 00000000..e2f4c1a3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/ComplaintStatusEnum.java @@ -0,0 +1,47 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 交易投诉状态 + * + * @author paulG + * @date 2020/12/4 + **/ +public enum ComplaintStatusEnum { + + /** + * 新投诉 + */ + NEW("新投诉"), + /** + * 已撤销 + */ + CANCEL("已撤销"), + /** + * 待申诉 + */ + WAIT_APPEAL("待申诉"), + /** + * 对话中 + */ + COMMUNICATION("对话中"), + /** + * 等待仲裁 + */ + WAIT_ARBITRATION("等待仲裁"), + /** + * 已完成 + */ + COMPLETE("已完成"); + + private final String description; + + ComplaintStatusEnum(String description) { + this.description = description; + } + + public String description() { + return this.description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/DeliverStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/DeliverStatusEnum.java new file mode 100644 index 00000000..6f157857 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/DeliverStatusEnum.java @@ -0,0 +1,29 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 发货状态枚举 + * + * @author Chopper + * @date 2020/11/17 7:25 下午 + */ +public enum DeliverStatusEnum { + + /** + * 发货状态 + */ + UNDELIVERED("未发货"), + DELIVERED("已发货"), + RECEIVED("已收货"); + + + private final String description; + + DeliverStatusEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/FlowTypeEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/FlowTypeEnum.java new file mode 100644 index 00000000..c2c7fa9b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/FlowTypeEnum.java @@ -0,0 +1,27 @@ +package cn.lili.modules.order.order.entity.enums; + + +/** + * 流水类型枚举 + * + * @author Chopper + * @date 2020/11/17 7:25 下午 + */ +public enum FlowTypeEnum { + + /** + * 流水类型 + */ + PAY("支付"), + REFUND("退款"); + + private final String description; + + FlowTypeEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderComplaintStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderComplaintStatusEnum.java new file mode 100644 index 00000000..facd49e3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderComplaintStatusEnum.java @@ -0,0 +1,47 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 订单的投诉状态 + * + * @author paulG + * @date 2020/12/5 + **/ +public enum OrderComplaintStatusEnum { + + /** + * 新订单,不能申请投诉 + */ + NEW("待审核"), + /** + * 未申请 + */ + NO_APPLY("未申请"), + /** + * 申请中 + */ + APPLYING("申请中"), + /** + * 已完成 + */ + COMPLETE("已完成"), + /** + * 已失效 + */ + EXPIRED("已失效"), + /** + * 取消 + */ + CANCEL("取消"); + + private final String description; + + OrderComplaintStatusEnum(String description) { + this.description = description; + } + + public String description() { + return this.description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderItemAfterSaleStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderItemAfterSaleStatusEnum.java new file mode 100644 index 00000000..d1b2a33a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderItemAfterSaleStatusEnum.java @@ -0,0 +1,30 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 订单可申请售后状态枚举 + * + * @author Chopper + * @date 2020/11/17 7:26 下午 + */ +public enum OrderItemAfterSaleStatusEnum { + + /** + * 订单申请售后状态 + */ + NEW("新订单,不能申请售后"), + NOT_APPLIED("未申请"), + ALREADY_APPLIED("已申请"), + EXPIRED("已失效不允许申请售后"); + + + private final String description; + + OrderItemAfterSaleStatusEnum(String description) { + this.description = description; + } + + public String description() { + return this.description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderMetaKeyEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderMetaKeyEnum.java new file mode 100644 index 00000000..814b9055 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderMetaKeyEnum.java @@ -0,0 +1,35 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 订单元Key枚举 + * + * @author Chopper + * @date 2020/11/17 7:26 下午 + */ +public enum OrderMetaKeyEnum { + + /** + * 订单属性 + */ + POINT("使用的积分"), + DISCOUNT_PRICE("优惠金额"), + GIFT_POINT("赠送的积分"), + GIFT_COUPON("赠送的优惠券"), + GIFT_SKU("赠品"); + + private final String description; + + OrderMetaKeyEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + public String description() { + return this.description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderOperateEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderOperateEnum.java new file mode 100644 index 00000000..b9283f9a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderOperateEnum.java @@ -0,0 +1,57 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 订单的操作方式枚举 + * + * @author Chopper + * @date 2020/11/17 7:26 下午 + */ +public enum OrderOperateEnum { + + + /** + * 确认 + */ + CONFIRM("确认"), + + /** + * 支付 + */ + PAY("支付"), + + /** + * 发货 + */ + SHIP("发货"), + + /** + * 确认收货 + */ + ROG("确认收货"), + + /** + * 取消 + */ + CANCEL("取消"), + + /** + * 评论 + */ + COMMENT("评论"), + + /** + * 完成 + */ + COMPLETE("完成"); + + private final String description; + + OrderOperateEnum(String description) { + this.description = description; + } + + public String description() { + return this.description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderOutStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderOutStatusEnum.java new file mode 100644 index 00000000..a00ce1b3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderOutStatusEnum.java @@ -0,0 +1,32 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 订单出库状态枚举 + * + * @author Chopper + * @date 2020/11/17 7:26 下午 + */ +public enum OrderOutStatusEnum { + + /** 等待出库 */ + WAIT("等待出库"), + + /** 出库成功 */ + SUCCESS("出库成功"), + + /** 出库失败 */ + FAIL("出库失败"); + + private final String description; + + OrderOutStatusEnum(String description){ + this.description = description; + } + + public String description(){ + return this.description; + } + + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderOutTypeEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderOutTypeEnum.java new file mode 100644 index 00000000..b3d2b564 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderOutTypeEnum.java @@ -0,0 +1,38 @@ +package cn.lili.modules.order.order.entity.enums; + +import java.util.ArrayList; +import java.util.List; + +/** + * 订单出库的类型枚举 + * + * @author Chopper + * @date 2020/11/17 7:27 下午 + */ +public enum OrderOutTypeEnum { + + /** + * 出库类型枚举 + */ + GOODS("商品"), + SECKILL_GOODS("限时抢购商品"); + + + private final String description; + + OrderOutTypeEnum(String description) { + this.description = description; + } + + public String description() { + return this.description; + } + + + public static List getAll() { + List all = new ArrayList<>(); + all.add(OrderOutTypeEnum.GOODS.name()); + all.add(OrderOutTypeEnum.SECKILL_GOODS.name()); + return all; + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderStatusEnum.java new file mode 100644 index 00000000..a2536c7b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderStatusEnum.java @@ -0,0 +1,40 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 订单状态枚举 + * + * @author Chopper + * @date 2020/11/17 7:27 下午 + */ +public enum OrderStatusEnum { + + /** + * 订单状态 + */ + UNPAID("未付款"), + PAID("已付款"), + UNDELIVERED("待发货"), + DELIVERED("已发货"), + COMPLETED("已完成"), + /** + * 虚拟订单需要核验商品 + */ + TAKE("待核验"), + CANCELLED("已取消"); + + private final String description; + + OrderStatusEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + public String description() { + return this.description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderTagEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderTagEnum.java new file mode 100644 index 00000000..d7b4aaad --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderTagEnum.java @@ -0,0 +1,58 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 前端订单页面TAB标签枚举 + * + * @author Chopper + * @date 2020/11/17 7:28 下午 + */ +public enum OrderTagEnum { + + + /** + * 所有订单 + */ + ALL("全部"), + + /** + * 待付款 + */ + WAIT_PAY("待付款"), + + /** + * 待收货 + */ + WAIT_SHIP("待发货"), + + /** + * 待收货 + */ + WAIT_ROG("待收货"), + + /** + * 已完成 + */ + COMPLETE("已完成"), + + /** + * 已取消 + */ + CANCELLED("已取消"); + + private final String description; + + + OrderTagEnum(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + public static OrderTagEnum defaultType() { + return ALL; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderTypeEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderTypeEnum.java new file mode 100644 index 00000000..78a3b610 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/OrderTypeEnum.java @@ -0,0 +1,30 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 订单类型枚举 + * + * @author Chopper + * @date 2020/11/17 7:28 下午 + */ +public enum OrderTypeEnum { + + /** + * 普通订单 + */ + NORMAL, + + /** + * 赠品订单 + */ + GIFT, + + /** + * 虚拟订单 + */ + FICTITIOUS, + /** + * 拼团订单 + */ + PINTUAN + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/enums/PayStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/PayStatusEnum.java new file mode 100644 index 00000000..00c3ea44 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/enums/PayStatusEnum.java @@ -0,0 +1,28 @@ +package cn.lili.modules.order.order.entity.enums; + +/** + * 订单状态枚举 + * + * @author Chopper + * @date 2020/11/17 7:28 下午 + */ +public enum PayStatusEnum { + + /** + * 支付状态 + */ + UNPAID("未付款"), + PAID("已付款"); + + private final String description; + + PayStatusEnum(String description) { + this.description = description; + } + + public String description() { + return this.description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleAllowOperation.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleAllowOperation.java new file mode 100644 index 00000000..566a29c3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleAllowOperation.java @@ -0,0 +1,74 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.trade.entity.enums.AfterSaleStatusEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 售后可操作类型 + * + * @author Chopper + * @date 2021/3/12 10:33 上午 + */ +@Data +public class AfterSaleAllowOperation { + + @ApiModelProperty(value = "可以确认售后") + private Boolean confirm = false; + + @ApiModelProperty(value = "可以回寄物流") + private Boolean return_goods = false; + + @ApiModelProperty(value = "可以收货") + private Boolean rog = false; + + @ApiModelProperty(value = "可以退款") + private Boolean refund = false; + + @ApiModelProperty(value = "买家确认收货") + private Boolean buyer_confirm; + + @ApiModelProperty(value = "可以取消") + private Boolean cancel; + + + /** + * 根据各种状态构建对象 + * + * @param afterSale + */ + public AfterSaleAllowOperation(AfterSale afterSale) { + //售后单状态 + String serviceStatus = afterSale.getServiceStatus(); + + //新提交售后 + if (serviceStatus.equals(AfterSaleStatusEnum.APPLY.name())) { + confirm = true; + } + + //待确认收货 + if (serviceStatus.equals(AfterSaleStatusEnum.BUYER_RETURN.name())) { + rog = true; + } + + //待平台线下退款 + if (serviceStatus.equals(AfterSaleStatusEnum.WAIT_REFUND.name())) { + refund = true; + } + + //待平台线下退款 + if (serviceStatus.equals(AfterSaleStatusEnum.WAIT_REFUND.name())) { + refund = true; + } + + //待平台线下退款 + if (serviceStatus.equals(AfterSaleStatusEnum.APPLY.name()) + ||serviceStatus.equals(AfterSaleStatusEnum.PASS.name())) { + cancel = true; + } + + + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleApplyVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleApplyVO.java new file mode 100644 index 00000000..5259e528 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleApplyVO.java @@ -0,0 +1,50 @@ +package cn.lili.modules.order.order.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 售后申请VO + * + * @author Chopper + * @date 2021/3/12 10:33 上午 + */ +@Data +public class AfterSaleApplyVO { + + @ApiModelProperty(value = "申请退款金额单价") + private Double applyRefundPrice; + + @ApiModelProperty(value = "可申请数量") + private Integer num; + + @ApiModelProperty(value = "订单子项编号") + private String orderItemSn; + + @ApiModelProperty(value = "商品ID") + private String goodsId; + + @ApiModelProperty(value = "货品ID") + private String skuId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品图片") + private String image; + + @ApiModelProperty(value = "商品价格") + private Double goodsPrice; + + /** + * @see cn.lili.modules.order.trade.entity.enums.AfterSaleRefundWayEnum + */ + @ApiModelProperty(value = "退款方式", allowableValues = "ORIGINAL,OFFLINE") + private String refundWay; + + /** + * @see cn.lili.modules.order.trade.entity.enums + */ + @ApiModelProperty(value = "账号类型", allowableValues = "ALIPAY,WECHATPAY,MEMBERWALLET,BANKTRANSFER") + private String accountType; +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleSearchParams.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleSearchParams.java new file mode 100644 index 00000000..6243455b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleSearchParams.java @@ -0,0 +1,139 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 售后搜索参数 + * + * @author paulG + * @date 2020/12/4 + **/ +@Data +public class AfterSaleSearchParams extends PageVO { + + @ApiModelProperty(value = "售后服务单号") + private String sn; + + @ApiModelProperty(value = "订单编号") + private String orderSn; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "商家名称") + private String storeName; + + @ApiModelProperty(value = "商家ID") + private String storeId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "申请退款金额,可以为范围,如10_1000") + private String applyRefundPrice; + + @ApiModelProperty(value = "实际退款金额,可以为范围,如10_1000") + private String actualRefundPrice; + + /** + * @see cn.lili.modules.order.trade.entity.enums.AfterSaleTypeEnum + */ + @ApiModelProperty(value = "售后类型", allowableValues = "CANCEL,RETURN_GOODS,EXCHANGE_GOODS,REISSUE_GOODS") + private String serviceType; + + /** + * @see cn.lili.modules.order.trade.entity.enums.AfterSaleStatusEnum + */ + @ApiModelProperty(value = "售后单状态", allowableValues = "APPLY,PASS,REFUSE,BUYER_RETURN,SELLER_RE_DELIVERY,BUYER_CONFIRM,SELLER_CONFIRM,COMPLETE") + private String serviceStatus; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "开始时间") + private Date startDate; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "结束时间") + private Date endDate; + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StringUtils.isNotEmpty(sn)) { + queryWrapper.like("sn", sn); + } + if (StringUtils.isNotEmpty(orderSn)) { + queryWrapper.like("order_sn", orderSn); + } + // 按买家查询 + if (StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MEMBER.name())) { + queryWrapper.eq("member_id", UserContext.getCurrentUser().getId()); + } + // 按卖家查询 + if (StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name())) { + queryWrapper.eq("store_id", UserContext.getCurrentUser().getStoreId()); + } + + if (StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MANAGER.name()) + && StringUtils.isNotEmpty(storeId) + ) { + + queryWrapper.eq("store_id", storeId); + } + if (StringUtils.isNotEmpty(memberName)) { + queryWrapper.like("member_name", memberName); + } + if (StringUtils.isNotEmpty(storeName)) { + queryWrapper.like("store_name", storeName); + } + if (StringUtils.isNotEmpty(goodsName)) { + queryWrapper.like("goods_name", goodsName); + } + // 按时间查询 + if (startDate != null) { + queryWrapper.ge("create_time", startDate); + } + if (endDate != null) { + queryWrapper.le("create_time", endDate); + } + if (StringUtils.isNotEmpty(serviceStatus)) { + queryWrapper.eq("service_status", serviceStatus); + } + if (StringUtils.isNotEmpty(serviceType)) { + queryWrapper.eq("service_type", serviceType); + } + this.betweenWrapper(queryWrapper); + queryWrapper.eq("delete_flag", false); + + queryWrapper.orderByDesc("create_time"); + return queryWrapper; + } + + private void betweenWrapper(QueryWrapper queryWrapper) { + if (StringUtils.isNotEmpty(applyRefundPrice)) { + String[] s = applyRefundPrice.split("_"); + if (s.length > 1) { + queryWrapper.ge("apply_refund_price", s[1]); + } else { + queryWrapper.le("apply_refund_price", s[0]); + } + } + if (StringUtils.isNotEmpty(actualRefundPrice)) { + String[] s = actualRefundPrice.split("_"); + if (s.length > 1) { + queryWrapper.ge("actual_refund_price", s[1]); + } else { + queryWrapper.le("actual_refund_price", s[0]); + } + } + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleVO.java new file mode 100644 index 00000000..060bce3e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AfterSaleVO.java @@ -0,0 +1,22 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.lili.modules.order.order.entity.dos.AfterSale; +import jdk.nashorn.internal.objects.annotations.Getter; + +/** + * 售后VO + * + * @author Chopper + * @date 2021/3/12 10:32 上午 + */ +public class AfterSaleVO extends AfterSale { + /** + * 初始化自身状态 + */ + @Getter + public AfterSaleAllowOperation getAfterSaleAllowOperationVO() { + + //设置订单的可操作状态 + return new AfterSaleAllowOperation(this); + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AllowOperation.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AllowOperation.java new file mode 100644 index 00000000..1a00f5d1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/AllowOperation.java @@ -0,0 +1,126 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.enums.DeliverStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderTypeEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 订单可进行的操作 + * + * @author Chopper + * @date 2020/11/17 7:29 下午 + */ +@Data +public class AllowOperation implements Serializable { + + private static final long serialVersionUID = -5109440403955543227L; + + @ApiModelProperty(value = "可以取消") + private Boolean cancel = false; + + @ApiModelProperty(value = "可以支付") + private Boolean pay = false; + + @ApiModelProperty(value = "可以发货") + private Boolean ship = false; + + @ApiModelProperty(value = "可以收货") + private Boolean rog = false; + + @ApiModelProperty(value = "是否允许查看物流信息") + private Boolean showLogistics = false; + + @ApiModelProperty(value = "是否允许更改收货人信息") + private Boolean editConsignee = false; + + @ApiModelProperty(value = "是否允许更改价格") + private Boolean editPrice = false; + + @ApiModelProperty(value = "是否可以进行核销") + private Boolean take = false; + + + /** + * 根据各种状态构建对象 + * + * @param order + */ + public AllowOperation(Order order) { + + //获取订单类型 + String status = order.getOrderStatus(); + String payStatus = order.getPayStatus(); + //编辑订单价格 未付款并且是新订单 + if (payStatus.equals(PayStatusEnum.UNPAID.name()) && status.equals(OrderStatusEnum.UNPAID.name())) { + this.editPrice = true; + } + + //新订单 + if (CharSequenceUtil.equalsAny(status, OrderStatusEnum.UNPAID.name(), OrderStatusEnum.PAID.name(), OrderStatusEnum.UNDELIVERED.name())) { + this.cancel = true; + } + //新订单,允许支付 + this.pay = status.equals(OrderStatusEnum.UNPAID.name()) && payStatus.equals(PayStatusEnum.UNPAID.name()); + + //订单未发货,就可以编辑收货人信息 + this.editConsignee = order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()) && !status.equals(OrderStatusEnum.CANCELLED.name()); + + //是否允许被发货 + this.ship = editConsignee && status.equals(OrderStatusEnum.UNDELIVERED.name()); + + //是否允许被收货 + this.rog = status.equals(OrderStatusEnum.DELIVERED.name()); + + //是否允许查看物流信息 + this.showLogistics = order.getDeliverStatus().equals(DeliverStatusEnum.DELIVERED.name()) && status.equals(OrderStatusEnum.DELIVERED.name()); + + this.take = order.getOrderType().equals(OrderTypeEnum.FICTITIOUS.name()) && order.getOrderStatus().equals(OrderStatusEnum.TAKE.name()); + } + + /** + * 根据各种状态构建对象 + * + * @param order + */ + public AllowOperation(OrderSimpleVO order) { + + //获取订单类型 + String status = order.getOrderStatus(); + String payStatus = order.getPayStatus(); + //编辑订单价格 未付款并且是新订单 + if (payStatus.equals(PayStatusEnum.UNPAID.name()) && status.equals(OrderStatusEnum.UNPAID.name())) { + this.editPrice = true; + } + + //新订单 + if (CharSequenceUtil.equalsAny(status, OrderStatusEnum.UNPAID.name(), OrderStatusEnum.PAID.name(), OrderStatusEnum.UNDELIVERED.name())) { + this.cancel = true; + + } + //新订单,允许支付 + this.pay = status.equals(OrderStatusEnum.UNPAID.name()); + + //订单未发货,就可以编辑收货人信息 + this.editConsignee = order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()); + + //是否允许被发货 + this.ship = editConsignee && status.equals(OrderStatusEnum.UNDELIVERED.name()); + + //是否允许被收货 + this.rog = status.equals(OrderStatusEnum.DELIVERED.name()); + + //是否允许查看物流信息 + this.showLogistics = order.getDeliverStatus().equals(DeliverStatusEnum.DELIVERED.name()) && status.equals(OrderStatusEnum.DELIVERED.name()); + + this.take = order.getOrderType().equals(OrderTypeEnum.FICTITIOUS.name()) && order.getOrderStatus().equals(OrderStatusEnum.TAKE.name()); + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintCommunicationSearchParams.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintCommunicationSearchParams.java new file mode 100644 index 00000000..bbcfcb4d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintCommunicationSearchParams.java @@ -0,0 +1,57 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.hutool.core.util.StrUtil; +import cn.lili.modules.order.order.entity.dos.OrderComplaintCommunication; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 订单投诉搜索参数 + * + * @author paulG + * @date 2020/12/5 + **/ +@Data +public class OrderComplaintCommunicationSearchParams { + + /** + * 投诉id + */ + @ApiModelProperty(value = "投诉id") + private String complainId; + + /** + * 所属,买家/卖家 + */ + @ApiModelProperty(value = "所属,买家/卖家") + private String owner; + /** + * 对话所属名称 + */ + @ApiModelProperty(value = "对话所属名称") + private String ownerName; + /** + * 对话所属id,卖家id/买家id + */ + @ApiModelProperty(value = "对话所属id,卖家id/买家id") + private String ownerId; + + public LambdaQueryWrapper lambdaQueryWrapper() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotEmpty(complainId)) { + queryWrapper.eq(OrderComplaintCommunication::getComplainId, complainId); + } + if (StrUtil.isNotEmpty(owner)) { + queryWrapper.eq(OrderComplaintCommunication::getOwner, owner); + } + if (StrUtil.isNotEmpty(ownerName)) { + queryWrapper.eq(OrderComplaintCommunication::getOwnerName, ownerName); + } + if (StrUtil.isNotEmpty(ownerId)) { + queryWrapper.eq(OrderComplaintCommunication::getOwnerId, ownerId); + } + return queryWrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintCommunicationVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintCommunicationVO.java new file mode 100644 index 00000000..53b55196 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintCommunicationVO.java @@ -0,0 +1,22 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.lili.modules.order.order.entity.dos.OrderComplaintCommunication; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 投诉通信VO + * + * @author paulG + * @date 2020/12/5 + **/ +@Data +@NoArgsConstructor +public class OrderComplaintCommunicationVO extends OrderComplaintCommunication { + + private static final long serialVersionUID = -8460949951683122695L; + + public OrderComplaintCommunicationVO(String complainId, String content, String owner, String ownerName, String ownerId) { + super(complainId, content, owner, ownerName, ownerId); + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintOperationParams.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintOperationParams.java new file mode 100644 index 00000000..83b4db04 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintOperationParams.java @@ -0,0 +1,33 @@ +package cn.lili.modules.order.order.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 交易投诉 参数 + * + * @author paulG + * @date 2020/12/4 + **/ +@Data +public class OrderComplaintOperationParams { + + + @ApiModelProperty(value = "要更改的状态状态") + private String complainStatus; + + @ApiModelProperty("交易投诉主键") + private String complainId; + + @ApiModelProperty("商家申诉内容") + private String appealContent; + + @ApiModelProperty("商家申诉上传的图片") + private List images; + + @ApiModelProperty("仲裁结果") + private String arbitrationResult; + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintSearchParams.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintSearchParams.java new file mode 100644 index 00000000..707ede7c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintSearchParams.java @@ -0,0 +1,66 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.hutool.core.util.StrUtil; +import cn.lili.modules.order.order.entity.dos.OrderComplaint; +import cn.lili.modules.order.order.entity.enums.ComplaintStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 订单投诉查询参数 + * + * @author paulG + * @date 2020/12/4 + **/ +@Data +public class OrderComplaintSearchParams { + + /** + * @see ComplaintStatusEnum + */ + @ApiModelProperty(value = "交易投诉状态") + private String status; + + @ApiModelProperty(value = "订单号") + private String orderSn; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "商家id") + private String storeId; + + @ApiModelProperty(value = "商家名称") + private String storeName; + + public LambdaQueryWrapper lambdaQueryWrapper() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotEmpty(status)) { + queryWrapper.eq(OrderComplaint::getComplainStatus, status); + } + if (StrUtil.isNotEmpty(orderSn)) { + queryWrapper.eq(OrderComplaint::getOrderSn, orderSn); + } + if (StrUtil.isNotEmpty(storeName)) { + queryWrapper.like(OrderComplaint::getStoreName, storeName); + } + if (StrUtil.isNotEmpty(storeId)) { + queryWrapper.eq(OrderComplaint::getStoreId, storeId); + } + if (StrUtil.isNotEmpty(memberName)) { + queryWrapper.like(OrderComplaint::getMemberName, memberName); + } + if (StrUtil.isNotEmpty(memberId)) { + queryWrapper.eq(OrderComplaint::getMemberId, memberId); + } + queryWrapper.eq(OrderComplaint::getDeleteFlag, false); + queryWrapper.orderByDesc(OrderComplaint::getCreateTime); + return queryWrapper; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintVO.java new file mode 100644 index 00000000..8cbccce8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderComplaintVO.java @@ -0,0 +1,39 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.hutool.core.bean.BeanUtil; +import cn.lili.modules.order.order.entity.dos.OrderComplaint; +import cn.lili.modules.order.order.entity.dos.OrderComplaintCommunication; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 订单交易投诉VO + * + * @author paulG + * @date 2020/12/4 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OrderComplaintVO extends OrderComplaint { + + + private static final long serialVersionUID = -7013465343480854816L; + + @ApiModelProperty(value = "投诉对话") + private List orderComplaintCommunications; + + @ApiModelProperty(value = "投诉图片") + private String[] orderComplaintImages; + + @ApiModelProperty(value = "申诉商家上传的图片") + private String[] appealImagesList; + + public OrderComplaintVO(OrderComplaint orderComplaint) { + BeanUtil.copyProperties(orderComplaint, this); + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderDetailVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderDetailVO.java new file mode 100644 index 00000000..a814754e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderDetailVO.java @@ -0,0 +1,134 @@ +package cn.lili.modules.order.order.entity.vo; + + +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.dos.Receipt; +import cn.lili.modules.order.order.entity.enums.DeliverStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum; +import cn.lili.modules.order.trade.entity.dos.OrderLog; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 订单详情VO + * + * @author Chopper + * @date 2020/11/17 7:29 下午 + */ +@Data +@NoArgsConstructor +public class OrderDetailVO implements Serializable { + + + private static final long serialVersionUID = -6293102172184734928L; + + /** + * 订单 + */ + private Order order; + + /** + * 子订单信息 + */ + private List orderItems; + + /** + * 订单状态 + */ + private String orderStatusValue; + + /** + * 付款状态 + */ + private String payStatusValue; + + /** + * 物流状态 + */ + private String deliverStatusValue; + + /** + * 物流类型 + */ + private String deliveryMethodValue; + + /** + * 支付类型 + */ + private String paymentMethodValue; + + /** + * 发票 + */ + private Receipt receipt; + + /** + * 获取订单日志 + */ + private List orderLogs; + @ApiModelProperty(value = "价格详情") + private String priceDetail; + + public OrderDetailVO(Order order, List orderItems, List orderLogs,Receipt receipt) { + this.order = order; + this.orderItems = orderItems; + this.orderLogs = orderLogs; + this.receipt = receipt; + } + + /** + * 可操作类型 + */ + public AllowOperation getAllowOperationVO() { + return new AllowOperation(this.order); + } + + public String getOrderStatusValue() { + try { + return OrderStatusEnum.valueOf(order.getOrderStatus()).description(); + } catch (Exception e) { + return ""; + } + } + + public String getPayStatusValue() { + try { + return PayStatusEnum.valueOf(order.getPayStatus()).description(); + } catch (Exception e) { + return ""; + } + + } + + public String getDeliverStatusValue() { + try { + return DeliverStatusEnum.valueOf(order.getDeliverStatus()).getDescription(); + } catch (Exception e) { + return ""; + } + } + + public String getDeliveryMethodValue() { + try { + return DeliveryMethodEnum.valueOf(order.getDeliveryMethod()).getDescription(); + } catch (Exception e) { + return ""; + } + } + + public String getPaymentMethodValue() { + try { + return PaymentMethodEnum.valueOf(order.getPaymentMethod()).paymentName(); + } catch (Exception e) { + return ""; + } + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderItemVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderItemVO.java new file mode 100644 index 00000000..000a01ed --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderItemVO.java @@ -0,0 +1,71 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.lili.modules.order.order.entity.enums.CommentStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderComplaintStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderItemAfterSaleStatusEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 子订单VO + * + * @author Chopper + * @date 2020-08-17 20:28 + */ +@Data +public class OrderItemVO { + + @ApiModelProperty(value = "编号") + private String sn; + + @ApiModelProperty(value = "商品ID") + private String goodsId; + + @ApiModelProperty(value = "货品ID") + private String skuId; + + @ApiModelProperty(value = "销售量") + private String num; + + @ApiModelProperty(value = "图片") + private String image; + + @ApiModelProperty(value = "商品名称") + private String name; + + @ApiModelProperty(value = "商品名称") + private Double goodsPrice; + + /** + * @see OrderItemAfterSaleStatusEnum + */ + @ApiModelProperty(value = "售后状态", allowableValues = "NOT_APPLIED(未申请),ALREADY_APPLIED(已申请),EXPIRED(已失效不允许申请售后)") + private String afterSaleStatus; + + /** + * @see OrderComplaintStatusEnum + */ + @ApiModelProperty(value = "投诉状态") + private String complainStatus; + + /** + * @see CommentStatusEnum + */ + @ApiModelProperty(value = "评论状态:未评论(UNFINISHED),待追评(WAIT_CHASE),评论完成(FINISHED),") + private String commentStatus; + + + public OrderItemVO(String sn, String goodsId, String skuId, String num, String image, String name, String afterSaleStatus, String complainStatus, String commentStatus, Double goodsPrice) { + this.sn = sn; + this.goodsId = goodsId; + this.skuId = skuId; + this.num = num; + this.image = image; + this.name = name; + this.afterSaleStatus = afterSaleStatus; + this.complainStatus = complainStatus; + this.commentStatus = commentStatus; + this.goodsPrice = goodsPrice; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderSimpleVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderSimpleVO.java new file mode 100644 index 00000000..8fc8ea67 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderSimpleVO.java @@ -0,0 +1,172 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.order.order.entity.enums.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import jdk.nashorn.internal.objects.annotations.Getter; +import lombok.Data; +import lombok.Setter; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 订单简略信息 + * 用于订单列表查看 + * + * @author Chopper + * @date 2020-08-17 20:28 + */ +@Data +public class OrderSimpleVO { + + @ApiModelProperty("sn") + private String sn; + + @ApiModelProperty(value = "总价格") + private Double flowPrice; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "创建时间") + private Date createTime; + + /** + * @see OrderStatusEnum + */ + @ApiModelProperty(value = "订单状态") + private String orderStatus; + + /** + * @see PayStatusEnum + */ + @ApiModelProperty(value = "付款状态") + private String payStatus; + + @ApiModelProperty(value = "支付方式") + private String paymentMethod; + + @ApiModelProperty(value = "支付时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date paymentTime; + + @ApiModelProperty(value = "用户名") + private String memberName; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "店铺ID") + private String storeId; + + /** + * @see ClientTypeEnum + */ + @ApiModelProperty(value = "订单来源") + private String clientType; + + /** + * 子订单信息 + */ + private List orderItems; + + @ApiModelProperty(hidden = true, value = "item goods_id") + @Setter + private String groupGoodsId; + + @ApiModelProperty(hidden = true, value = "item sku id") + @Setter + private String groupSkuId; + + @ApiModelProperty(hidden = true, value = "item 数量") + @Setter + private String groupNum; + + @ApiModelProperty(hidden = true, value = "item 图片") + @Setter + private String groupImages; + + @ApiModelProperty(hidden = true, value = "item 名字") + @Setter + private String groupName; + + @ApiModelProperty(hidden = true, value = "item 编号") + @Setter + private String groupOrderItemsSn; + + @ApiModelProperty(hidden = true, value = "item 商品价格") + @Setter + private String groupGoodsPrice; + /** + * @see OrderItemAfterSaleStatusEnum + */ + @ApiModelProperty(hidden = true, value = "item 售后状态", allowableValues = "NOT_APPLIED(未申请),ALREADY_APPLIED(已申请),EXPIRED(已失效不允许申请售后)") + @Setter + private String groupAfterSaleStatus; + + /** + * @see OrderComplaintStatusEnum + */ + @ApiModelProperty(hidden = true, value = "item 投诉状态") + @Setter + private String groupComplainStatus; + + /** + * @see CommentStatusEnum + */ + @ApiModelProperty(hidden = true, value = "item 评价状态") + @Setter + private String groupCommentStatus; + + + /** + * @see OrderTypeEnum + */ + @ApiModelProperty(value = "订单类型") + private String orderType; + + /** + * @see DeliverStatusEnum + */ + @ApiModelProperty(value = "货运状态") + private String deliverStatus; + + @Getter + public List getOrderItems() { + if (StringUtils.isEmpty(groupGoodsId)) { + return new ArrayList<>(); + } + List orderItemVOS = new ArrayList<>(); + String[] orderItemsSn = groupOrderItemsSn.split(","); + String[] goodsId = groupGoodsId.split(","); + String[] skuId = groupSkuId.split(","); + String[] num = groupNum.split(","); + String[] image = groupImages.split(","); + String[] name = groupName.split(","); + String[] afterSaleStatus = groupAfterSaleStatus.split(","); + String[] complainStatus = groupComplainStatus.split(","); + String[] commentStatus = groupCommentStatus.split(","); + String[] goodsPrice = groupGoodsPrice.split(","); +// String goodsId, String skuId, Integer num, String image, String name, String afterSaleStatus + for (int i = 0; i < goodsId.length; i++) { + orderItemVOS.add(new OrderItemVO(orderItemsSn[i], goodsId[i], skuId[i], num[i], image[i], name[i], afterSaleStatus[i], complainStatus[i], commentStatus[i], Double.parseDouble(goodsPrice[i]))); + } + return orderItemVOS; + + } + + /** + * 初始化自身状态 + */ + @Getter + public AllowOperation getAllowOperationVO() { + //设置订单的可操作状态 + return new AllowOperation(this); + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderVO.java new file mode 100644 index 00000000..b04687bf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/OrderVO.java @@ -0,0 +1,26 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 订单vo + * + * @author Bulbasaur + * @date 2020/11/28 11:38 + */ +@Data +public class OrderVO extends Order { + + + private static final long serialVersionUID = 5820637554656388777L; + + @ApiModelProperty(value = "订单商品项目") + private List orderItems; + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/PaymentLog.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/PaymentLog.java new file mode 100644 index 00000000..67cec7ce --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/PaymentLog.java @@ -0,0 +1,89 @@ +package cn.lili.modules.order.order.entity.vo; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.order.order.entity.enums.OrderTypeEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; + +/** + * 订单 + * + * @author Chopper + * @date 2020/11/17 7:30 下午 + */ +@Data +@Entity +@Table(name = "li_order") +@TableName("li_order") +@ApiModel(value = "订单") +public class PaymentLog extends BaseEntity { + + + private static final long serialVersionUID = 2233811628066468683L; + @ApiModelProperty("订单编号") + private String sn; + + @ApiModelProperty("交易编号 关联Trade") + private String tradeSn; + + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "用户名") + private String memberName; + + /** + * @see PayStatusEnum + */ + @ApiModelProperty(value = "付款状态") + private String payStatus; + + @ApiModelProperty(value = "第三方付款流水号") + private String receivableNo; + + @ApiModelProperty(value = "支付方式") + private String paymentMethod; + + @ApiModelProperty(value = "支付时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date paymentTime; + + + @ApiModelProperty(value = "总价格") + private Double flowPrice; + + @ApiModelProperty(value = "支付方式返回的交易号") + private String payOrderNo; + + /** + * @see ClientTypeEnum + */ + @ApiModelProperty(value = "订单来源") + private String clientType; + + /** + * @see OrderTypeEnum + */ + @ApiModelProperty(value = "订单类型") + private String orderType; + + + public PaymentLog() { + + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/ReceiptVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/ReceiptVO.java new file mode 100644 index 00000000..cbea30f0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/ReceiptVO.java @@ -0,0 +1,26 @@ +package cn.lili.modules.order.order.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 发票 + * + * @author Bulbasaur + * @date 2020/11/28 11:38 + */ +@Data +@ApiModel(value = "发票") +public class ReceiptVO { + + @ApiModelProperty(value = "发票抬头") + private String receiptTitle; + + @ApiModelProperty(value = "纳税人识别号") + private String taxpayerId; + + @ApiModelProperty(value = "发票内容") + private String receiptContent; + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/entity/vo/StoreAppealVO.java b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/StoreAppealVO.java new file mode 100644 index 00000000..f4aac4b1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/entity/vo/StoreAppealVO.java @@ -0,0 +1,27 @@ +package cn.lili.modules.order.order.entity.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 订单交易投诉VO + * + * @author paulG + * @date 2020/12/4 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class StoreAppealVO { + + @ApiModelProperty(value = "投诉id") + private String orderComplaintId; + + @ApiModelProperty(value = "申诉商家内容") + private String appealContent; + + @ApiModelProperty(value = "申诉商家上传的图片") + private String appealImages; +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/mapper/AfterSaleMapper.java b/framework/src/main/java/cn/lili/modules/order/order/mapper/AfterSaleMapper.java new file mode 100644 index 00000000..5a039982 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/mapper/AfterSaleMapper.java @@ -0,0 +1,25 @@ +package cn.lili.modules.order.order.mapper; + +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.vo.AfterSaleVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 售后数据处理层 + * + * @author Chopper + * @date 2020/11/17 7:34 下午 + */ +public interface AfterSaleMapper extends BaseMapper { + + @Select("SELECT * FROM li_after_sale ${ew.customSqlSegment}") + IPage queryByParams(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Select("SELECT * FROM li_after_sale WHERE sn=#{sn}") + AfterSaleVO getAfterSaleVO(String sn); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/mapper/AfterSaleReasonMapper.java b/framework/src/main/java/cn/lili/modules/order/order/mapper/AfterSaleReasonMapper.java new file mode 100644 index 00000000..2e0f2d7b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/mapper/AfterSaleReasonMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.order.order.mapper; + +import cn.lili.modules.order.order.entity.dos.AfterSaleReason; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 售后原因数据处理层 + * + * @author Chopper + * @date 2020/11/17 7:34 下午 + */ +public interface AfterSaleReasonMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderComplainCommunicationMapper.java b/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderComplainCommunicationMapper.java new file mode 100644 index 00000000..8bb93ab8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderComplainCommunicationMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.order.order.mapper; + +import cn.lili.modules.order.order.entity.dos.OrderComplaintCommunication; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 交易投诉通信数据处理层 + * + * @author paulG + * @since 2020/12/5 + **/ +public interface OrderComplainCommunicationMapper extends BaseMapper { + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderComplaintMapper.java b/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderComplaintMapper.java new file mode 100644 index 00000000..38e2a196 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderComplaintMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.order.order.mapper; + +import cn.lili.modules.order.order.entity.dos.OrderComplaint; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 订单投诉数据处理层 + * + * @author paulG + * @since 2020/12/5 + **/ +public interface OrderComplaintMapper extends BaseMapper { +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderItemMapper.java b/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderItemMapper.java new file mode 100644 index 00000000..aa0b35d9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderItemMapper.java @@ -0,0 +1,24 @@ +package cn.lili.modules.order.order.mapper; + +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 子订单数据处理层 + * + * @author Chopper + * @date 2020/11/17 7:34 下午 + */ +public interface OrderItemMapper extends BaseMapper { + + @Select("SELECT * FROM li_order_item AS oi INNER JOIN li_order AS o ON oi.order_sn=o.sn ${ew.customSqlSegment}") + List waitEvaluate(@Param(Constants.WRAPPER) Wrapper queryWrapper); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderMapper.java b/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderMapper.java new file mode 100644 index 00000000..e976a1a7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/mapper/OrderMapper.java @@ -0,0 +1,46 @@ +package cn.lili.modules.order.order.mapper; + +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import cn.lili.modules.order.order.entity.vo.PaymentLog; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +/** + * 订单数据处理层 + * + * @author Chopper + * @date 2020/11/17 7:35 下午 + */ +public interface OrderMapper extends BaseMapper { + + @Update({"update li_order set order_status = #{status} where sn in #{orderSn}"}) + void batchUpdateStatus(String status, String orderSns); + + @Update({"update li_order set order_status = #{status} where sn = #{orderSn}"}) + void updateStatus(String status, String orderSn); + + @Select("select o.sn,o.flow_price,o.create_time,o.order_status,o.pay_status,o.payment_method,o.payment_time,o.member_name,o.store_name as store_name,o.store_id as store_id,o.client_type,o.order_type,o.deliver_status " + + ",GROUP_CONCAT(oi.goods_id) as group_goods_id," + + " GROUP_CONCAT(oi.sku_id) as group_sku_id," + + " GROUP_CONCAT(oi.num) as group_num" + + ",GROUP_CONCAT(oi.image) as group_images" + + ",GROUP_CONCAT(oi.goods_name) as group_name " + + ",GROUP_CONCAT(oi.after_sale_status) as group_after_sale_status" + + ",GROUP_CONCAT(oi.complain_status) as group_complain_status" + + ",GROUP_CONCAT(oi.comment_status) as group_comment_status" + + ",GROUP_CONCAT(oi.sn) as group_order_items_sn " + + ",GROUP_CONCAT(oi.goods_price) as group_goods_price " + + " FROM li_order o INNER JOIN li_order_item AS oi on o.sn = oi.order_sn ${ew.customSqlSegment} ") + IPage queryByParams(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + + @Select("select * from li_order ${ew.customSqlSegment} ") + IPage queryPaymentLogs(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/mapper/ReceiptMapper.java b/framework/src/main/java/cn/lili/modules/order/order/mapper/ReceiptMapper.java new file mode 100644 index 00000000..3a7ba953 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/mapper/ReceiptMapper.java @@ -0,0 +1,31 @@ +package cn.lili.modules.order.order.mapper; + +import cn.lili.modules.order.order.entity.dos.Receipt; +import cn.lili.modules.order.order.entity.dto.OrderReceiptDTO; +import cn.lili.modules.order.order.entity.dto.ReceiptSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 发票数据处理层 + * + * @author Chopper + * @date 2020/11/17 7:35 下午 + */ +public interface ReceiptMapper extends BaseMapper { + + /** + * 查询发票信息 + * + * @param page 分页 + * @param queryWrapper 查询条件 + * @return + */ + @Select("select r.*,o.order_status from li_receipt r inner join li_order o ON o.sn=r.order_sn ${ew.customSqlSegment}") + IPage getReceipt(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/mapper/StoreFlowMapper.java b/framework/src/main/java/cn/lili/modules/order/order/mapper/StoreFlowMapper.java new file mode 100644 index 00000000..73e0abc7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/mapper/StoreFlowMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.order.order.mapper; + +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 商家订单流水数据处理层 + * + * @author Chopper + * @date 2020/11/17 7:35 下午 + */ +public interface StoreFlowMapper extends BaseMapper { +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/mapper/TradeMapper.java b/framework/src/main/java/cn/lili/modules/order/order/mapper/TradeMapper.java new file mode 100644 index 00000000..dad60aac --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/mapper/TradeMapper.java @@ -0,0 +1,17 @@ +package cn.lili.modules.order.order.mapper; + +import cn.lili.modules.order.order.entity.dos.Trade; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Update; + +/** + * 交易数据处理层 + * + * @author Chopper + * @date 2020/11/17 7:35 下午 + */ +public interface TradeMapper extends BaseMapper { + + @Update("UPDATE li_trade SET flow_price =(SELECT SUM(flow_price) FROM li_order WHERE trade_sn=#{tradeSn}) WHERE sn=#{tradeSn}") + void updateTradePrice(String tradeSn); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/AfterSaleLogService.java b/framework/src/main/java/cn/lili/modules/order/order/service/AfterSaleLogService.java new file mode 100644 index 00000000..9cd4bd68 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/AfterSaleLogService.java @@ -0,0 +1,23 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.modules.order.trade.entity.dos.AfterSaleLog; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 订单日志业务层 + * + * @author Chopper + * @date 2020/11/17 7:37 下午 + */ +public interface AfterSaleLogService extends IService { + + /** + * 获取售后日志 + * + * @param sn 售后编号 + * @return 售后日志列表 + */ + List getAfterSaleLog(String sn); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/AfterSaleReasonService.java b/framework/src/main/java/cn/lili/modules/order/order/service/AfterSaleReasonService.java new file mode 100644 index 00000000..cdd65be7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/AfterSaleReasonService.java @@ -0,0 +1,31 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.modules.order.order.entity.dos.AfterSaleReason; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 售后原因业务层 + * + * @author Chopper + * @date 2020/11/17 7:37 下午 + */ +public interface AfterSaleReasonService extends IService { + + /** + * 获取售后原因列表 + * @param serviceType + * @return + */ + List afterSaleReasonList(String serviceType); + + + /** + * 修改售后原因 + * @param afterSaleReason 售后原因 + * @return 售后原因 + */ + AfterSaleReason editAfterSaleReason(AfterSaleReason afterSaleReason); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/AfterSaleService.java b/framework/src/main/java/cn/lili/modules/order/order/service/AfterSaleService.java new file mode 100644 index 00000000..41baf6ff --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/AfterSaleService.java @@ -0,0 +1,141 @@ +package cn.lili.modules.order.order.service; + + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.dto.AfterSaleDTO; +import cn.lili.modules.order.order.entity.vo.AfterSaleApplyVO; +import cn.lili.modules.order.order.entity.vo.AfterSaleSearchParams; +import cn.lili.modules.order.order.entity.vo.AfterSaleVO; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO; +import cn.lili.modules.system.entity.vo.Traces; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Date; + +/** + * 售后业务层 + * + * @author Chopper + * @date 2020/11/17 7:36 下午 + */ +public interface AfterSaleService extends IService { + + /** + * 分页查询售后信息 + * + * @param saleSearchParams 查询参数 + * @return 分页售后信息 + */ + IPage getAfterSalePages(AfterSaleSearchParams saleSearchParams); + + /** + * 查询售后信息 + * + * @param sn 售后单号 + * @return 售后信息 + */ + AfterSaleVO getAfterSale(String sn); + + /** + * 获取申请售后页面信息 + * + * @param sn 订单编号 + * @return + */ + AfterSaleApplyVO getAfterSaleDTO(String sn); + + /** + * 售后申请 + * + * @param afterSaleDTO 售后对象 + * @return 售后信息 + */ + AfterSale saveAfterSale(AfterSaleDTO afterSaleDTO); + + /** + * 商家审核售后申请 + * + * @param afterSaleSn 售后编号 + * @param serviceStatus 状态 PASS:审核通过,REFUSE:审核未通过 + * @param remark 商家备注 + */ + AfterSale review(String afterSaleSn, String serviceStatus, String remark,Double actualRefundPrice); + + /** + * 买家退货,物流填写 + * + * @param afterSaleSn 售后服务单号 + * @param logisticsNo 物流单号 + * @param logisticsId 物流公司ID + */ + AfterSale buyerDelivery(String afterSaleSn, String logisticsNo, String logisticsId, Date mDeliverTime); + + /* 获取买家退货物流踪迹 + * @param afterSaleSn 售后服务单号 + * @return 物流踪迹 + */ + Traces deliveryTraces(String afterSaleSn); + + /** + * 商家收货 + * + * @param afterSaleSn 售后编号 + * @param serviceStatus 状态 PASS:审核通过,REFUSE:审核未通过 + * @param remark 商家备注 + * @return 售后服务 + */ + AfterSale storeConfirm(String afterSaleSn, String serviceStatus, String remark); + + /** + * 平台退款-线下支付 + * + * @param afterSaleSn 售后单号 + * @param remark 备注 + * @return 售后服务 + */ + AfterSale refund(String afterSaleSn, String remark); + + /** + * 买家确认解决问题 + * + * @param afterSaleSn 售后订单sn + * @return 售后服务 + */ + AfterSale complete(String afterSaleSn); + + /** + * 买家取消售后 + * + * @param afterSaleSn 售后订单sn + * @return 售后服务 + */ + AfterSale cancel(String afterSaleSn); + + /** + * 获取待处理售后数量 + * + * @param serviceType 售后类型 + * @return 待处理售后数量 + */ + Integer applyNum(String serviceType); + + /** + * 根据售后单号获取店铺退货收货地址信息 + * + * @param sn 售后单号 + * @return 店铺退货收件地址 + */ + StoreAfterSaleAddressDTO getStoreAfterSaleAddressDTO(String sn); + + /** + * 获取统计的售后 + * + * @param statisticsQueryParam 统计搜索参数 + * @param pageVO 分页 + * @return 售后分页列表 + */ + IPage getStatistics(StatisticsQueryParam statisticsQueryParam, PageVO pageVO); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/OrderComplaintCommunicationService.java b/framework/src/main/java/cn/lili/modules/order/order/service/OrderComplaintCommunicationService.java new file mode 100644 index 00000000..10a1920a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/OrderComplaintCommunicationService.java @@ -0,0 +1,36 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.OrderComplaintCommunication; +import cn.lili.modules.order.order.entity.vo.OrderComplaintCommunicationSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderComplaintCommunicationVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 订单投诉通信业务层 + * + * @author paulG + * @date 2020/12/5 + **/ +public interface OrderComplaintCommunicationService extends IService { + + /** + * 添加订单投诉通信 + * + * @param communicationVO 投诉通信VO + * @return 状态 + */ + boolean addCommunication(OrderComplaintCommunicationVO communicationVO); + + /** + * 获取通信记录 + * + * @param searchParams 参数 + * @param pageVO 分页 + * @return + */ + IPage getCommunication(OrderComplaintCommunicationSearchParams searchParams, PageVO pageVO); + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/OrderComplaintService.java b/framework/src/main/java/cn/lili/modules/order/order/service/OrderComplaintService.java new file mode 100644 index 00000000..4d742153 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/OrderComplaintService.java @@ -0,0 +1,90 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.OrderComplaint; +import cn.lili.modules.order.order.entity.dto.OrderComplaintDTO; +import cn.lili.modules.order.order.entity.vo.OrderComplaintOperationParams; +import cn.lili.modules.order.order.entity.vo.OrderComplaintSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderComplaintVO; +import cn.lili.modules.order.order.entity.vo.StoreAppealVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 交易投诉业务层 + * + * @author paulG + * @date 2020/12/4 + **/ +public interface OrderComplaintService extends IService { + + /** + * 分页获取交易投诉信息 + * + * @param searchParams 查询参数 + * @param pageVO 分页参数 + * @return 交易投诉信息 + */ + IPage getOrderComplainByPage(OrderComplaintSearchParams searchParams, PageVO pageVO); + + /** + * 获取交易投诉详情 + * + * @param id 交易投诉ID + * @return 交易投诉详情 + */ + OrderComplaintVO getOrderComplainById(String id); + + /** + * 获取交易投诉详情 + * + * @param storeId 店铺id + * @return 交易投诉详情 + */ + OrderComplaint getOrderComplainByStoreId(String storeId); + + /** + * 添加交易投诉 + * + * @param orderComplaintDTO 交易投诉信息 + * @return 添加结果 + */ + OrderComplaint addOrderComplain(OrderComplaintDTO orderComplaintDTO); + + /** + * 更新交易投诉 + * + * @param orderComplainVO 交易投诉信息 + * @return 更新结果 + */ + boolean updateOrderComplain(OrderComplaintVO orderComplainVO); + + /** + * 修改交易投诉状态 + * + * @param operationParam 操作参数 + * @return 修改的交易投诉 + */ + OrderComplaint updateOrderComplainByStatus(OrderComplaintOperationParams operationParam); + + /** + * 获取新投诉数量 + * + * @return 新投诉 + */ + Integer newComplainNum(); + + /** + * 取消交易投诉 + * + * @param id 交易投诉ID + * @return 操作状态 + */ + boolean cancel(String id); + + /** + * 店铺申诉 + * @param storeAppealVO + */ + boolean appeal(StoreAppealVO storeAppealVO); +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/OrderItemService.java b/framework/src/main/java/cn/lili/modules/order/order/service/OrderItemService.java new file mode 100644 index 00000000..dc305bee --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/OrderItemService.java @@ -0,0 +1,69 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.enums.CommentStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderComplaintStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderItemAfterSaleStatusEnum; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Date; +import java.util.List; + +/** + * 子订单业务层 + * + * @author Chopper + * @date 2020/11/17 7:36 下午 + */ +public interface OrderItemService extends IService { + + /** + * 更新评论状态 + * + * @param orderItemSn 子订单编号 + * @param commentStatusEnum 评论状态枚举 + */ + void updateCommentStatus(String orderItemSn, CommentStatusEnum commentStatusEnum); + + /** + * 更新可申请售后状态 + * + * @param orderItemSn 子订单编号 + * @param orderItemAfterSaleStatusEnum 售后状态枚举 + */ + void updateAfterSaleStatus(String orderItemSn, OrderItemAfterSaleStatusEnum orderItemAfterSaleStatusEnum); + + /** + * 更新订单可投诉状态 + * + * @param orderSn 订单sn + * @param skuId 商品skuId + * @param complainId 订单交易投诉ID + * @param complainStatusEnum 修改状态 + */ + void updateOrderItemsComplainStatus(String orderSn, String skuId, String complainId, OrderComplaintStatusEnum complainStatusEnum); + + /** + * 根据子订单编号获取子订单信息 + * + * @param sn 子订单编号 + * @return 子订单 + */ + OrderItem getBySn(String sn); + + /** + * 根据订单编号获取子订单列表 + * + * @param orderSn 订单编号 + * @return 子订单列表 + */ + List getByOrderSn(String orderSn); + + /** + * 获取待评价订单列表 + * + * @param date 结束时间 + * @return 子订单列表 + */ + List waitEvaluate(Date date); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/OrderPriceService.java b/framework/src/main/java/cn/lili/modules/order/order/service/OrderPriceService.java new file mode 100644 index 00000000..baa70f7e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/OrderPriceService.java @@ -0,0 +1,28 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.modules.order.order.entity.dos.Order; + +/** + * @author liushuai(liushuai711 @ gmail.com) + * @version v4.1 + * @Description: + * @since 2021/4/28 3:47 下午 + */ +public interface OrderPriceService { + + /** + * 价格修改 + * 日志功能内部实现 + * + * @param orderSn 订单编号 + * @param orderPrice 订单价格 + */ + Order updatePrice(String orderSn, Double orderPrice); + + /** + * 管理员订单付款 + * + * @param orderSn 订单编号 + */ + void adminPayOrder(String orderSn); +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/OrderService.java b/framework/src/main/java/cn/lili/modules/order/order/service/OrderService.java new file mode 100644 index 00000000..1f8da09e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/OrderService.java @@ -0,0 +1,179 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dto.MemberAddressDTO; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import cn.lili.modules.order.order.entity.dto.OrderSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.system.entity.vo.Traces; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 子订单业务层 + * + * @author Chopper + * @date 2020/11/17 7:36 下午 + */ +public interface OrderService extends IService { + + + /** + * 系统取消订单 + * + * @param orderSn 订单编号 + * @param reason 错误原因 + */ + void systemCancel(String orderSn, String reason); + + /** + * 根据sn查询 + * + * @param orderSn 订单编号 + * @return 订单信息 + */ + Order getBySn(String orderSn); + + /** + * 订单查询 + * + * @param orderSearchParams 查询参数 + */ + IPage queryByParams(OrderSearchParams orderSearchParams); + + /** + * 订单详细 + * + * @param orderSn 订单SN + * @return 订单详细 + */ + OrderDetailVO queryDetail(String orderSn); + + /** + * 创建交易 + * + * @param tradeDTO 交易DTO + */ + void intoDB(TradeDTO tradeDTO); + + /** + * 订单付款 + * + * @param orderSn 订单编号 + * @param paymentMethod 支付方法 + * @param receivableNo 第三方流水 + */ + void payOrder(String orderSn, String paymentMethod, String receivableNo); + + /** + * 订单确认成功 + * + * @param orderSn + */ + void afterOrderConfirm(String orderSn); + + /** + * 取消订单 + * + * @param orderSn 订单SN + * @param reason 取消理由 + */ + Order cancel(String orderSn, String reason); + + + /** + * 发货信息修改 + * 日志功能内部实现 + * + * @param orderSn 订单编号 + * @param memberAddressDTO 收货地址信息 + */ + Order updateConsignee(String orderSn, MemberAddressDTO memberAddressDTO); + + /** + * 订单发货 + * + * @param orderSn 订单编号 + * @param invoiceNumber 发货单号 + * @param logisticsId 物流公司 + */ + Order delivery(String orderSn, String invoiceNumber, String logisticsId); + + /** + * 获取物流踪迹 + * + * @param orderSn 订单编号 + * @return 物流踪迹 + */ + Traces getTraces(String orderSn); + + /** + * 订单核验 + * + * @param orderSn 订单编号 + * @param qrCode 提货码 + */ + Order take(String orderSn, String qrCode); + + /** + * 订单完成 + * + * @param orderSn 订单编号 + */ + void complete(String orderSn); + + /** + * 通过trade 获取订单列表 + * + * @param tradeSn 交易编号 + * @return 订单列表 + */ + List getByTradeSn(String tradeSn); + + /** + * 发送更新订单状态的信息 + * + * @param orderMessage 订单传输信息 + */ + void sendUpdateStatusMessage(OrderMessage orderMessage); + + /** + * 根据订单sn逻辑删除订单 + * + * @param sn 订单sn + */ + void deleteOrder(String sn); + + /** + * 获取统计的订单 + * + * @param statisticsQueryParam + * @param pageVO + * @return + */ + IPage getStatistics(StatisticsQueryParam statisticsQueryParam, PageVO pageVO); + + + /** + * 开具发票 + * + * @param sn 订单sn + * @return + */ + Boolean invoice(String sn); + + /** + * 自动成团订单处理 + * + * @param pintuanId 拼团活动id + * @param parentOrderSn 拼团订单sn + */ + void agglomeratePintuanOrder(String pintuanId, String parentOrderSn); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/ReceiptService.java b/framework/src/main/java/cn/lili/modules/order/order/service/ReceiptService.java new file mode 100644 index 00000000..2caeed81 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/ReceiptService.java @@ -0,0 +1,61 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.Receipt; +import cn.lili.modules.order.order.entity.dto.OrderReceiptDTO; +import cn.lili.modules.order.order.entity.dto.ReceiptSearchParams; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 发票业务层 + * + * @author Bulbasaur + * @date 2020/11/17 7:37 下午 + */ +public interface ReceiptService extends IService { + + + /** + * 根据条件获取发票信息列表 + * + * @param searchParams 发票查询参数 + * @param pageVO 分页参数 + * @return 发票信息列表 + */ + IPage getReceiptData(ReceiptSearchParams searchParams, PageVO pageVO); + + /** + * 根据订单编号获取发票信息 + * + * @param orderSn 订单编号 + * @return 发票 + */ + Receipt getByOrderSn(String orderSn); + + /** + * 获取发票详情 + * + * @param id 发票id + * @return 发票详情 + */ + Receipt getDetail(String id); + + /** + * 保存发票 + * + * @param receipt 发票信息 + * @return 是否保存成功 + */ + Receipt saveReceipt(Receipt receipt); + + /** + * 开具发票 + * + * @param receiptId 发票id + * @return + */ + Receipt invoicing(String receiptId); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/StoreFlowService.java b/framework/src/main/java/cn/lili/modules/order/order/service/StoreFlowService.java new file mode 100644 index 00000000..f513cbe4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/StoreFlowService.java @@ -0,0 +1,46 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Date; + +/** + * 商家订单流水业务层 + * + * @author Chopper + * @date 2020/11/17 7:37 下午 + */ +public interface StoreFlowService extends IService { + + /** + * 支付订单 + * + * @param orderSn 订单编号 + */ + void payOrder(String orderSn); + + /** + * 订单退款 + * + * @param afterSale 售后单 + */ + void refundOrder(AfterSale afterSale); + + /** + * 获取商家流水 + * + * @param StoreId 商家ID + * @param type 收入、退款 + * @param distribution 是否查看分销相关数据 + * @param pageVO 分页 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return + */ + IPage getStoreFlow(String StoreId, String type, boolean distribution, PageVO pageVO, Date startTime, Date endTime); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/service/TradeService.java b/framework/src/main/java/cn/lili/modules/order/order/service/TradeService.java new file mode 100644 index 00000000..91457521 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/service/TradeService.java @@ -0,0 +1,40 @@ +package cn.lili.modules.order.order.service; + +import cn.lili.modules.order.order.entity.dos.Trade; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 交易业务层 + * + * @author Chopper + * @date 2020/11/17 7:37 下午 + */ +public interface TradeService extends IService { + + /** + * 创建交易 + * + * @param tradeDTO 购物车视图 + * @return 交易 + */ + Trade createTrade(TradeDTO tradeDTO); + + /** + * 获取交易详情 + * + * @param sn 交易编号 + * @return 交易详情 + */ + Trade getBySn(String sn); + + /** + * 整笔交易付款 + * + * @param tradeSn 交易编号 + * @param receivableNo 第三方流水号 + * @param paymentName 支付方式 + */ + void payTrade(String tradeSn, String paymentName, String receivableNo); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/AfterSaleLogServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/AfterSaleLogServiceImpl.java new file mode 100644 index 00000000..565bca53 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/AfterSaleLogServiceImpl.java @@ -0,0 +1,30 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.lili.modules.order.order.service.AfterSaleLogService; +import cn.lili.modules.order.trade.entity.dos.AfterSaleLog; +import cn.lili.modules.order.trade.mapper.AfterSaleLogMapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 订单日志业务层实现 + * + * @author Chopper + * @date 2020/11/17 7:37 下午 + */ +@Service +@Transactional +public class AfterSaleLogServiceImpl extends ServiceImpl implements AfterSaleLogService { + + @Override + public List getAfterSaleLog(String sn) { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq("sn", sn); + return this.list(queryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/AfterSaleReasonServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/AfterSaleReasonServiceImpl.java new file mode 100644 index 00000000..df63cafe --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/AfterSaleReasonServiceImpl.java @@ -0,0 +1,42 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.lili.modules.order.order.entity.dos.AfterSaleReason; +import cn.lili.modules.order.order.mapper.AfterSaleReasonMapper; +import cn.lili.modules.order.order.service.AfterSaleReasonService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; + +/** + * 售后原因业务层实现 + * + * @author Chopper + * @date 2020/11/17 7:38 下午 + */ +@Service +@Transactional +public class AfterSaleReasonServiceImpl extends ServiceImpl implements AfterSaleReasonService { + + + @Override + public List afterSaleReasonList(String serviceType) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(AfterSaleReason::getServiceType, serviceType); + return this.list(lambdaQueryWrapper); + } + + @Override + public AfterSaleReason editAfterSaleReason(AfterSaleReason afterSaleReason) { + LambdaUpdateWrapper lambdaQueryWrapper = Wrappers.lambdaUpdate(); + lambdaQueryWrapper.eq(AfterSaleReason::getId, afterSaleReason.getId()); + lambdaQueryWrapper.set(AfterSaleReason::getReason, afterSaleReason.getReason()); + lambdaQueryWrapper.set(AfterSaleReason::getServiceType, afterSaleReason.getServiceType()); + this.update(lambdaQueryWrapper); + return afterSaleReason; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/AfterSaleServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/AfterSaleServiceImpl.java new file mode 100644 index 00000000..aae3910e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/AfterSaleServiceImpl.java @@ -0,0 +1,502 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.aop.syslog.annotation.SystemLogPoint; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.AfterSaleTagsEnum; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.*; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.order.order.aop.AfterSaleLogPoint; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.dto.AfterSaleDTO; +import cn.lili.modules.order.order.entity.enums.OrderItemAfterSaleStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.entity.vo.AfterSaleApplyVO; +import cn.lili.modules.order.order.entity.vo.AfterSaleSearchParams; +import cn.lili.modules.order.order.entity.vo.AfterSaleVO; +import cn.lili.modules.order.order.mapper.AfterSaleMapper; +import cn.lili.modules.order.order.service.AfterSaleService; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.order.trade.entity.enums.AfterSaleRefundWayEnum; +import cn.lili.modules.order.trade.entity.enums.AfterSaleStatusEnum; +import cn.lili.modules.order.trade.entity.enums.AfterSaleTypeEnum; +import cn.lili.modules.payment.kit.RefundSupport; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.util.StatisticsDateUtil; +import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO; +import cn.lili.modules.store.entity.enums.StoreStatusEnum; +import cn.lili.modules.store.service.StoreDetailService; +import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.entity.vo.Traces; +import cn.lili.modules.system.service.LogisticsService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.Date; + +/** + * 售后业务层实现 + * + * @author Chopper + * @date 2020/11/17 7:38 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AfterSaleServiceImpl extends ServiceImpl implements AfterSaleService { + + //订单 + private final OrderService orderService; + //订单货物 + private final OrderItemService orderItemService; + //物流公司 + private final LogisticsService logisticsService; + //店铺详情 + private final StoreDetailService storeDetailService; + //售后支持,这里用于退款操作 + private RefundSupport refundSupport; + //RocketMQ配置 + private final RocketmqCustomProperties rocketmqCustomProperties; + //RocketMQ + private final RocketMQTemplate rocketMQTemplate; + + @Override + public IPage getAfterSalePages(AfterSaleSearchParams saleSearchParams) { + return baseMapper.queryByParams(PageUtil.initPage(saleSearchParams), saleSearchParams.queryWrapper()); + } + + @Override + public AfterSaleVO getAfterSale(String sn) { + return this.baseMapper.getAfterSaleVO(sn); + } + + @Override + public AfterSaleApplyVO getAfterSaleDTO(String sn) { + + AfterSaleApplyVO afterSaleApplyVO = new AfterSaleApplyVO(); + + //获取订单货物判断是否可申请售后 + OrderItem orderItem = orderItemService.getBySn(sn); + + //未申请售后订单货物才能进行申请 + if (!orderItem.getAfterSaleStatus().equals(OrderItemAfterSaleStatusEnum.NOT_APPLIED.name())) { + throw new ServiceException(ResultCode.AFTER_SALES_BAN); + } + + //获取售后类型 + Order order = orderService.getBySn(orderItem.getOrderSn()); + + //订单未支付,不能申请申请售后 + if (order.getPaymentMethod() == null) { + throw new ServiceException(ResultCode.AFTER_SALES_NOT_PAY_ERROR); + } + //判断支付方式是否为线上支付 + if (order.getPaymentMethod().equals(PaymentMethodEnum.BANK_TRANSFER)) { + afterSaleApplyVO.setRefundWay(AfterSaleRefundWayEnum.OFFLINE.name()); + } else { + afterSaleApplyVO.setRefundWay(AfterSaleRefundWayEnum.ORIGINAL.name()); + } + + afterSaleApplyVO.setAccountType(order.getPaymentMethod()); + afterSaleApplyVO.setApplyRefundPrice(CurrencyUtil.sub(orderItem.getFlowPrice(), orderItem.getNum())); + afterSaleApplyVO.setNum(orderItem.getNum()); + afterSaleApplyVO.setGoodsId(orderItem.getGoodsId()); + afterSaleApplyVO.setGoodsName(orderItem.getGoodsName()); + afterSaleApplyVO.setImage(orderItem.getImage()); + afterSaleApplyVO.setGoodsPrice(orderItem.getGoodsPrice()); + afterSaleApplyVO.setSkuId(orderItem.getSkuId()); + return afterSaleApplyVO; + } + + @Override + @AfterSaleLogPoint(sn = "#rvt.sn", description = "'售后申请:售后编号['+#rvt.sn+']'") + @SystemLogPoint(description = "售后-售后申请", customerLog = "'售后申请:售后编号['+#rvt.sn+']'") + public AfterSale saveAfterSale(AfterSaleDTO afterSaleDTO) { + + //检查当前订单是否可申请售后 + this.checkAfterSaleType(afterSaleDTO); + + //添加售后 + return addAfterSale(afterSaleDTO); + } + + @AfterSaleLogPoint(sn = "#afterSaleSn", description = "'审核售后:售后编号['+#afterSaleSn+'],'+ #serviceStatus") + @SystemLogPoint(description = "售后-审核售后", customerLog = "'审核售后:售后编号['+#afterSaleSn+'],'+ #serviceStatus") + @Override + public AfterSale review(String afterSaleSn, String serviceStatus, String remark, Double actualRefundPrice) { + //根据售后单号获取售后单 + AfterSale afterSale = OperationalJudgment.judgment(this.getBySn(afterSaleSn)); + afterSale.setActualRefundPrice(actualRefundPrice); + + //判断为待审核的售后服务 + if (!afterSale.getServiceStatus().equals(AfterSaleStatusEnum.APPLY.name())) { + throw new ServiceException(ResultCode.AFTER_SALES_DOUBLE_ERROR); + } + + //判断审核状态 + //如果售后类型为:退款,审核状态为已通过并且退款方式为原路退回,售后单状态为已完成。 + //如果售后类型为:退款,审核状态已通过并且退款方式为线下退回,售后单状态为待退款。 + //如果售后类型不为退款,售后单状态为:已通过。 + AfterSaleStatusEnum afterSaleStatusEnum = null; + if (serviceStatus.equals(AfterSaleStatusEnum.PASS.name())) { + if (afterSale.getServiceType().equals(AfterSaleTypeEnum.RETURN_MONEY.name())) { + if (afterSale.getRefundWay().equals(AfterSaleRefundWayEnum.ORIGINAL.name())) { + //如果为退款操作 && 在线支付 则直接进行退款 + refundSupport.refund(afterSale); + afterSaleStatusEnum = AfterSaleStatusEnum.COMPLETE; + } else { + afterSaleStatusEnum = AfterSaleStatusEnum.WAIT_REFUND; + } + } else { + afterSaleStatusEnum = AfterSaleStatusEnum.PASS; + } + } else { + afterSaleStatusEnum = AfterSaleStatusEnum.REFUSE; + } + afterSale.setServiceStatus(afterSaleStatusEnum.name()); + afterSale.setAuditRemark(remark); + + //根据售后编号修改售后单 + updateAfterSale(afterSaleSn, afterSale); + + //发送售后消息 + this.sendAfterSaleMessage(afterSale); + + return afterSale; + } + + @AfterSaleLogPoint(sn = "#afterSaleSn", description = "'买家退货,物流填写:单号['+#afterSaleSn+'],物流单号为['+#logisticsNo+']'") + @SystemLogPoint(description = "售后-买家退货,物流填写", customerLog = "'买家退货,物流填写:单号['+#afterSaleSn+'],物流单号为['+#logisticsNo+']'") + @Override + public AfterSale buyerDelivery(String afterSaleSn, String logisticsNo, String logisticsId, Date mDeliverTime) { + + //根据售后单号获取售后单 + AfterSale afterSale = OperationalJudgment.judgment(this.getBySn(afterSaleSn)); + + //判断为已审核通过,待邮寄的售后服务 + if (!afterSale.getServiceStatus().equals(AfterSaleStatusEnum.PASS.name())) { + throw new ServiceException(ResultCode.ERROR); + } + + //查询会员回寄的物流公司信息 + Logistics logistics = logisticsService.getById(logisticsId); + + //判断物流公司是否为空 + if (logistics == null) { + throw new ServiceException(ResultCode.AFTER_SALES_LOGISTICS_ERROR); + } + + afterSale.setMLogisticsCode(logistics.getId()); + afterSale.setMLogisticsName(logistics.getName()); + afterSale.setMLogisticsNo(logisticsNo); + afterSale.setMDeliverTime(mDeliverTime); + //修改售后单状态 + afterSale.setServiceStatus(AfterSaleStatusEnum.BUYER_RETURN.name()); + + //根据售后编号修改售后单 + this.updateAfterSale(afterSaleSn, afterSale); + return afterSale; + } + + @Override + public Traces deliveryTraces(String afterSaleSn) { + + //根据售后单号获取售后单 + AfterSale afterSale = OperationalJudgment.judgment(this.getBySn(afterSaleSn)); + + return logisticsService.getLogistic(afterSale.getMLogisticsCode(), afterSale.getMLogisticsNo()); + } + + @Override + @AfterSaleLogPoint(sn = "#afterSaleSn", description = "'售后-商家收货:单号['+#afterSaleSn+'],物流单号为['+#logisticsNo+']" + + ",处理结果['+serviceStatus='PASS'?+'商家收货':'商家拒收'+']'") + @SystemLogPoint(description = "售后-商家收货", customerLog = "'售后-商家收货:单号['+#afterSaleSn+'],物流单号为['+#logisticsNo+']" + + ",处理结果['+serviceStatus='PASS'?+'商家收货':'商家拒收'+']'") + public AfterSale storeConfirm(String afterSaleSn, String serviceStatus, String remark) { + //根据售后单号获取售后单 + AfterSale afterSale = OperationalJudgment.judgment(this.getBySn(afterSaleSn)); + + //判断是否为已邮寄售后单 + if (!afterSale.getServiceStatus().equals(AfterSaleStatusEnum.BUYER_RETURN.name())) { + throw new ServiceException(ResultCode.ERROR); + } + AfterSaleStatusEnum afterSaleStatusEnum = null; + //判断审核状态 + //在线支付 则直接进行退款 + if (serviceStatus.equals("PASS") && + afterSale.getRefundWay().equals(AfterSaleRefundWayEnum.ORIGINAL.name())) { + refundSupport.refund(afterSale); + afterSaleStatusEnum = AfterSaleStatusEnum.COMPLETE; + } else if (serviceStatus.equals("PASS")) { + afterSaleStatusEnum = AfterSaleStatusEnum.WAIT_REFUND; + } else { + afterSaleStatusEnum = AfterSaleStatusEnum.SELLER_TERMINATION; + + } + afterSale.setServiceStatus(afterSaleStatusEnum.name()); + afterSale.setAuditRemark(remark); + + //根据售后编号修改售后单 + this.updateAfterSale(afterSaleSn, afterSale); + + //发送售后消息 + this.sendAfterSaleMessage(afterSale); + return afterSale; + } + + @Override + @AfterSaleLogPoint(sn = "#afterSaleSn", description = "'售后-平台退款:单号['+#afterSaleSn+'],备注为['+#remark+']'") + @SystemLogPoint(description = "售后-平台退款", customerLog = "'售后-平台退款:单号['+#afterSaleSn+'],备注为['+#remark+']'") + public AfterSale refund(String afterSaleSn, String remark) { + //根据售后单号获取售后单 + AfterSale afterSale = OperationalJudgment.judgment(this.getBySn(afterSaleSn)); + afterSale.setServiceStatus(AfterSaleStatusEnum.COMPLETE.name()); + //根据售后编号修改售后单 + this.updateAfterSale(afterSaleSn, afterSale); + //退款 + refundSupport.refund(afterSale); + //发送退款消息 + this.sendAfterSaleMessage(afterSale); + return afterSale; + } + + @Override + @AfterSaleLogPoint(sn = "#afterSaleSn", description = "'售后-买家确认解决:单号['+#afterSaleSn+']'") + @SystemLogPoint(description = "售后-买家确认解决", customerLog = "'售后-买家确认解决:单号['+#afterSaleSn+']'") + public AfterSale complete(String afterSaleSn) { + AfterSale afterSale = this.getBySn(afterSaleSn); + afterSale.setServiceStatus(AfterSaleStatusEnum.COMPLETE.name()); + this.updateAfterSale(afterSaleSn, afterSale); + return afterSale; + } + + @Override + @AfterSaleLogPoint(sn = "#afterSaleSn", description = "'售后-买家取消:单号['+#afterSaleSn+']'") + @SystemLogPoint(description = "售后-取消售后", customerLog = "'售后-买家取消:单号['+#afterSaleSn+']'") + public AfterSale cancel(String afterSaleSn) { + + //根据售后单号获取售后单 + AfterSale afterSale = OperationalJudgment.judgment(this.getBySn(afterSaleSn)); + + //判断售后单是否可以申请售后 + //如果售后状态为:待审核、已通过则可进行申请售后 + if (afterSale.getServiceStatus().equals(AfterSaleStatusEnum.APPLY.name()) + || afterSale.getServiceStatus().equals(AfterSaleStatusEnum.PASS.name())) { + + afterSale.setServiceStatus(AfterSaleStatusEnum.BUYER_CANCEL.name()); + + //根据售后编号修改售后单 + this.updateAfterSale(afterSaleSn, afterSale); + return afterSale; + } + throw new ServiceException(ResultCode.AFTER_SALES_CANCEL_ERROR); + } + + @Override + public Integer applyNum(String serviceType) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(AfterSale::getServiceStatus, StoreStatusEnum.APPLYING.name()); + queryWrapper.eq(StringUtils.isNotEmpty(serviceType), AfterSale::getServiceType, serviceType); + queryWrapper.eq(StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), + AfterSale::getStoreId, UserContext.getCurrentUser().getStoreId()); + return this.count(queryWrapper); + } + + @Override + public StoreAfterSaleAddressDTO getStoreAfterSaleAddressDTO(String sn) { + return storeDetailService.getStoreAfterSaleAddressDTO(this.getBySn(sn).getStoreId()); + } + + @Override + public IPage getStatistics(StatisticsQueryParam statisticsQueryParam, PageVO pageVO) { + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam); + queryWrapper.between(AfterSale::getCreateTime, dates[0], dates[1]); + queryWrapper.eq(StringUtils.isNotEmpty(statisticsQueryParam.getStoreId()), AfterSale::getStoreId, statisticsQueryParam.getStoreId()); + + return this.page(PageUtil.initPage(pageVO), queryWrapper); + } + + /** + * 创建售后 + * + * @param afterSaleDTO 售后 + * @return 售后 + */ + private AfterSale addAfterSale(AfterSaleDTO afterSaleDTO) { + //写入其他属性 + AuthUser tokenUser = UserContext.getCurrentUser(); + + AfterSale afterSale = new AfterSale(); + BeanUtil.copyProperties(afterSaleDTO, afterSale); + + //写入会员信息 + afterSale.setMemberId(tokenUser.getId()); + afterSale.setMemberName(tokenUser.getNickName()); + + //写入商家信息 + OrderItem orderItem = orderItemService.getBySn(afterSaleDTO.getOrderItemSn()); + Order order = orderService.getBySn(orderItem.getOrderSn()); + afterSale.setStoreId(order.getStoreId()); + afterSale.setStoreName(order.getStoreName()); + + //写入订单商品信息 + afterSale.setGoodsImage(orderItem.getImage()); + afterSale.setGoodsName(orderItem.getGoodsName()); + afterSale.setSpecs(orderItem.getSpecs()); + afterSale.setFlowPrice(orderItem.getFlowPrice()); + + //写入交易流水号 + afterSale.setTradeSn(order.getTradeSn()); + afterSale.setOrderSn(order.getSn()); + afterSale.setPayOrderNo(order.getPayOrderNo()); + afterSale.setOrderItemSn(orderItem.getSn()); + + //写入状态 + afterSale.setServiceStatus(AfterSaleStatusEnum.APPLY.name()); + + //TODO 退还积分 + + //创建售后单号 + afterSale.setSn(SnowFlake.createStr("A")); + + //是否包含图片 + if (afterSaleDTO.getImages() != null) { + afterSale.setAfterSaleImage(afterSaleDTO.getImages()); + } + //计算退回金额 + afterSale.setApplyRefundPrice(CurrencyUtil.mul(orderItem.getUnitPrice(), afterSale.getNum())); + //添加售后 + this.save(afterSale); + //发送售后消息 + this.sendAfterSaleMessage(afterSale); + //修改订单的售后状态 + orderItemService.updateAfterSaleStatus(orderItem.getSn(), OrderItemAfterSaleStatusEnum.ALREADY_APPLIED); + return afterSale; + } + + /** + * 检查当前订单状态是否为可申请当前售后类型的状态 + * + * @param afterSaleDTO 售后 + */ + private void checkAfterSaleType(AfterSaleDTO afterSaleDTO) { + + //判断数据是否为空 + if (null == afterSaleDTO || StringUtils.isEmpty(afterSaleDTO.getOrderItemSn())) { + throw new ServiceException(ResultCode.ORDER_NOT_EXIST); + } + + //获取订单货物判断是否可申请售后 + OrderItem orderItem = orderItemService.getBySn(afterSaleDTO.getOrderItemSn()); + + //未申请售后订单货物才能进行申请 + if (!orderItem.getAfterSaleStatus().equals(OrderItemAfterSaleStatusEnum.NOT_APPLIED.name())) { + throw new ServiceException(ResultCode.AFTER_SALES_BAN); + } + + //获取售后类型 + Order order = orderService.getBySn(orderItem.getOrderSn()); + AfterSaleTypeEnum afterSaleTypeEnum = AfterSaleTypeEnum.valueOf(afterSaleDTO.getServiceType()); + switch (afterSaleTypeEnum) { + case RETURN_MONEY: + //只处理已付款的售后 + if (!PayStatusEnum.PAID.name().equals(order.getPayStatus())) { + throw new ServiceException(ResultCode.AFTER_SALES_BAN); + } + this.checkAfterSaleReturnMoneyParam(afterSaleDTO); + break; + case RETURN_GOODS: + // 是否为有效状态 + boolean availableStatus = StrUtil.equalsAny(order.getOrderStatus(), OrderStatusEnum.DELIVERED.name(), OrderStatusEnum.COMPLETED.name()); + if (!PayStatusEnum.PAID.name().equals(order.getPayStatus()) && availableStatus) { + throw new ServiceException(ResultCode.AFTER_SALES_BAN); + } + break; + default: + break; + } + + } + + /** + * 检测售后-退款参数 + * + * @param afterSaleDTO + */ + private void checkAfterSaleReturnMoneyParam(AfterSaleDTO afterSaleDTO) { + //如果为线下支付银行信息不能为空 + if (AfterSaleRefundWayEnum.OFFLINE.name().equals(afterSaleDTO.getRefundWay())) { + boolean emptyBankParam = StringUtils.isEmpty(afterSaleDTO.getBankDepositName()) + || StringUtils.isEmpty(afterSaleDTO.getBankAccountName()) + || StringUtils.isEmpty(afterSaleDTO.getBankAccountNumber()); + if (emptyBankParam) { + throw new ServiceException("当账号类型为银行转账时,银行信息不能为空"); + } + + } + } + + /** + * 根据sn获取信息 + * + * @param sn 订单sn + * @return 售后信息 + */ + private AfterSale getBySn(String sn) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("sn", sn); + return this.getOne(queryWrapper); + } + + /** + * 根据售后编号修改售后单 + * + * @param afterSaleSn 售后单号 + * @param afterSale 售后单 + */ + private void updateAfterSale(String afterSaleSn, AfterSale afterSale) { + LambdaUpdateWrapper queryWrapper = Wrappers.lambdaUpdate(); + queryWrapper.eq(AfterSale::getSn, afterSaleSn); + this.update(afterSale, queryWrapper); + } + + @Autowired + public void setRefundSupport(RefundSupport refundSupport) { + this.refundSupport = refundSupport; + } + + /** + * 发送售后消息 + * + * @param afterSale 售后对象 + */ + private void sendAfterSaleMessage(AfterSale afterSale) { + //发送售后创建消息 + String destination = rocketmqCustomProperties.getAfterSaleTopic() + ":" + AfterSaleTagsEnum.AFTER_SALE_STATUS_CHANGE.name(); + //发送订单变更mq消息 + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(afterSale), RocketmqSendCallbackBuilder.commonCallback()); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderComplaintCommunicationServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderComplaintCommunicationServiceImpl.java new file mode 100644 index 00000000..74a109b3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderComplaintCommunicationServiceImpl.java @@ -0,0 +1,35 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.OrderComplaintCommunication; +import cn.lili.modules.order.order.entity.vo.OrderComplaintCommunicationSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderComplaintCommunicationVO; +import cn.lili.modules.order.order.mapper.OrderComplainCommunicationMapper; +import cn.lili.modules.order.order.service.OrderComplaintCommunicationService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; + +/** + * 交易投诉通信业务层实现 + * + * @author paulG + * @since 2020/12/5 + **/ +@Service +@Transactional +public class OrderComplaintCommunicationServiceImpl extends ServiceImpl implements OrderComplaintCommunicationService { + + @Override + public boolean addCommunication(OrderComplaintCommunicationVO communicationVO) { + return this.save(communicationVO); + } + + @Override + public IPage getCommunication(OrderComplaintCommunicationSearchParams searchParams, PageVO pageVO) { + return this.page(PageUtil.initPage(pageVO), searchParams.lambdaQueryWrapper()); + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderComplaintServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderComplaintServiceImpl.java new file mode 100644 index 00000000..3459288f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderComplaintServiceImpl.java @@ -0,0 +1,251 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.OperationalJudgment; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.order.order.entity.dos.OrderComplaint; +import cn.lili.modules.order.order.entity.dos.OrderComplaintCommunication; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.dto.OrderComplaintDTO; +import cn.lili.modules.order.order.entity.enums.ComplaintStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderComplaintStatusEnum; +import cn.lili.modules.order.order.entity.vo.*; +import cn.lili.modules.order.order.mapper.OrderComplaintMapper; +import cn.lili.modules.order.order.service.OrderComplaintCommunicationService; +import cn.lili.modules.order.order.service.OrderComplaintService; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.order.order.service.OrderService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.Date; +import java.util.List; + +/** + * 交易投诉业务层实现 + * + * @author paulG + * @since 2020/12/5 + **/ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderComplaintServiceImpl extends ServiceImpl implements OrderComplaintService { + + //订单 + private final OrderService orderService; + //订单货物 + private final OrderItemService orderItemService; + //商品规格 + private final GoodsSkuService goodsSkuService; + //交易投诉沟通 + private final OrderComplaintCommunicationService orderComplaintCommunicationService; + + /** + * 分页获取交易投诉信息 + * + * @param searchParams 查询参数 + * @param pageVO 分页参数 + * @return 交易投诉信息 + */ + @Override + public IPage getOrderComplainByPage(OrderComplaintSearchParams searchParams, PageVO pageVO) { + return this.page(PageUtil.initPage(pageVO), searchParams.lambdaQueryWrapper()); + } + + /** + * 获取交易投诉详情 + * + * @param id 交易投诉ID + * @return 交易投诉详情 + */ + @Override + public OrderComplaintVO getOrderComplainById(String id) { + OrderComplaint orderComplaint = this.checkOrderComplainExist(id); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(OrderComplaintCommunication::getComplainId, id); + List list = orderComplaintCommunicationService.list(queryWrapper); + OrderComplaintVO orderComplainVO = new OrderComplaintVO(orderComplaint); + orderComplainVO.setOrderComplaintCommunications(list); + orderComplainVO.setOrderComplaintImages(orderComplaint.getImages().split(",")); + if (orderComplaint.getAppealImages() != null) { + orderComplainVO.setAppealImagesList(orderComplaint.getAppealImages().split(",")); + } + return orderComplainVO; + } + + /** + * 获取交易投诉详情 + * + * @param storeId 店铺id + * @return 交易投诉详情 + */ + @Override + public OrderComplaint getOrderComplainByStoreId(String storeId) { + return this.getOne(new LambdaQueryWrapper().eq(OrderComplaint::getStoreId, storeId)); + } + + /** + * 添加交易投诉 + * + * @param orderComplaintDTO 交易投诉信息 + * @return 添加结果 + */ + @Override + public OrderComplaint addOrderComplain(OrderComplaintDTO orderComplaintDTO) { + + try { + //查询订单信息 + OrderDetailVO orderDetailVO = orderService.queryDetail(orderComplaintDTO.getOrderSn()); + List orderItems = orderDetailVO.getOrderItems(); + OrderItem orderItem = orderItems.stream().filter(i -> orderComplaintDTO.getSkuId().equals(i.getSkuId())).findFirst().orElse(null); + + if (orderItem == null) { + throw new ServiceException(ResultCode.COMPLAINT_ORDER_ITEM_EMPTY_ERROR); + } + + //新建交易投诉 + OrderComplaint orderComplaint = new OrderComplaint(); + BeanUtil.copyProperties(orderComplaintDTO, orderComplaint); + + //获取商品规格信息 + GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(orderItem.getSkuId()); + if (goodsSku == null) { + throw new ServiceException(ResultCode.COMPLAINT_SKU_EMPTY_ERROR); + } + orderComplaint.setComplainStatus(ComplaintStatusEnum.NEW.name()); + orderComplaint.setGoodsId(goodsSku.getGoodsId()); + orderComplaint.setGoodsName(goodsSku.getGoodsName()); + orderComplaint.setGoodsImage(goodsSku.getThumbnail()); + orderComplaint.setGoodsPrice(goodsSku.getPrice()); + orderComplaint.setNum(orderItem.getNum()); + + //获取订单信息 + orderComplaint.setOrderTime(orderDetailVO.getOrder().getCreateTime()); + orderComplaint.setOrderPrice(orderDetailVO.getOrder().getPriceDetailDTO().getBillPrice()); + orderComplaint.setNum(orderDetailVO.getOrder().getGoodsNum()); + orderComplaint.setFreightPrice(orderDetailVO.getOrder().getPriceDetailDTO().getFreightPrice()); + orderComplaint.setLogisticsNo(orderDetailVO.getOrder().getLogisticsNo()); + orderComplaint.setConsigneeMobile(orderDetailVO.getOrder().getConsigneeMobile()); + orderComplaint.setConsigneeAddressPath(orderDetailVO.getOrder().getConsigneeAddressPath()); + orderComplaint.setConsigneeName(orderDetailVO.getOrder().getConsigneeName()); + + //获取商家信息 + orderComplaint.setStoreId(orderDetailVO.getOrder().getStoreId()); + orderComplaint.setStoreName(orderDetailVO.getOrder().getStoreName()); + + orderComplaint.setMemberId(UserContext.getCurrentUser().getId()); + orderComplaint.setMemberName(UserContext.getCurrentUser().getUsername()); + //保存订单投诉 + this.save(orderComplaint); + + //更新订单投诉状态 + orderItemService.updateOrderItemsComplainStatus(orderComplaint.getOrderSn(), orderComplaint.getSkuId(), orderComplaint.getId(), OrderComplaintStatusEnum.APPLYING); + return orderComplaint; + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + log.error("订单投诉异常:", e); + throw new ServiceException(ResultCode.COMPLAINT_ERROR); + } + } + + /** + * 更新交易投诉 + * + * @param orderComplainVO 交易投诉信息 + * @return 更新结果 + */ + @Override + public boolean updateOrderComplain(OrderComplaintVO orderComplainVO) { + OperationalJudgment.judgment(this.checkOrderComplainExist(orderComplainVO.getId())); + return this.updateById(orderComplainVO); + } + + /** + * 修改交易投诉状态 + * + * @param operationParam 操作参数 + * @return 修改的交易投诉 + */ + @Override + public OrderComplaint updateOrderComplainByStatus(OrderComplaintOperationParams operationParam) { + OrderComplaint orderComplaint = OperationalJudgment.judgment(this.checkOrderComplainExist(operationParam.getComplainId())); + this.checkOperationParams(operationParam, orderComplaint); + orderComplaint.setComplainStatus(operationParam.getComplainStatus()); + this.updateById(orderComplaint); + return orderComplaint; + } + + @Override + public Integer newComplainNum() { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq("complain_status", ComplaintStatusEnum.NEW.name()); + queryWrapper.eq(StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), + "store_id", UserContext.getCurrentUser().getStoreId()); + return this.count(queryWrapper); + } + + @Override + public boolean cancel(String id) { + + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.eq(OrderComplaint::getId, id); + lambdaUpdateWrapper.set(OrderComplaint::getComplainStatus, ComplaintStatusEnum.CANCEL.name()); + return this.update(lambdaUpdateWrapper); + } + + @Override + public boolean appeal(StoreAppealVO storeAppealVO) { + //获取投诉信息 + OrderComplaint orderComplaint = OperationalJudgment.judgment(this.checkOrderComplainExist(storeAppealVO.getOrderComplaintId())); + orderComplaint.setAppealContent(storeAppealVO.getAppealContent()); + orderComplaint.setAppealImages(storeAppealVO.getAppealImages()); + orderComplaint.setAppealTime(new Date()); + orderComplaint.setComplainStatus(ComplaintStatusEnum.WAIT_ARBITRATION.name()); + this.updateById(orderComplaint); + return true; + } + + private OrderComplaint checkOrderComplainExist(String id) { + OrderComplaint orderComplaint = this.getById(id); + if (orderComplaint == null) { + throw new ServiceException("当前投诉记录不存在"); + } + return orderComplaint; + } + + private void checkOperationParams(OrderComplaintOperationParams operationParam, OrderComplaint orderComplaint) { + ComplaintStatusEnum complaintStatusEnum = ComplaintStatusEnum.valueOf(operationParam.getComplainStatus()); + if (complaintStatusEnum == ComplaintStatusEnum.COMPLETE) { + if (StrUtil.isEmpty(operationParam.getArbitrationResult())) { + throw new ServiceException("结束订单投诉时,仲裁结果不能为空"); + } + orderComplaint.setArbitrationResult(operationParam.getArbitrationResult()); + } else if (complaintStatusEnum == ComplaintStatusEnum.COMMUNICATION) { + if (StrUtil.isEmpty(operationParam.getAppealContent()) || operationParam.getImages() == null) { + throw new ServiceException("商家申诉时,申诉内容不能为空"); + } + orderComplaint.setContent(operationParam.getAppealContent()); + orderComplaint.setImages(operationParam.getImages().get(0)); + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderItemServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderItemServiceImpl.java new file mode 100644 index 00000000..0723b9c8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderItemServiceImpl.java @@ -0,0 +1,94 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.enums.CommentStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderComplaintStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderItemAfterSaleStatusEnum; +import cn.lili.modules.order.order.mapper.OrderItemMapper; +import cn.lili.modules.order.order.service.OrderItemService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.Date; +import java.util.List; + +/** + * 子订单业务层实现 + * + * @author Chopper + * @date 2020/11/17 7:38 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderItemServiceImpl extends ServiceImpl implements OrderItemService { + + private final OrderItemMapper orderItemMapper; + + @Override + public void updateCommentStatus(String orderItemSn, CommentStatusEnum commentStatusEnum) { + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.set(OrderItem::getCommentStatus, commentStatusEnum.name()); + lambdaUpdateWrapper.eq(OrderItem::getSn, orderItemSn); + this.update(lambdaUpdateWrapper); + } + + @Override + public void updateAfterSaleStatus(String orderItemSn, OrderItemAfterSaleStatusEnum orderItemAfterSaleStatusEnum) { + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.set(OrderItem::getAfterSaleStatus, orderItemAfterSaleStatusEnum.name()); + lambdaUpdateWrapper.eq(OrderItem::getSn, orderItemSn); + this.update(lambdaUpdateWrapper); + } + + /** + * 更新订单可投诉状态 + * + * @param orderSn 订单sn + * @param skuId 商品skuId + * @param complainId 订单交易投诉ID + * @param complainStatusEnum 修改状态 + */ + @Override + public void updateOrderItemsComplainStatus(String orderSn, String skuId, String complainId, OrderComplaintStatusEnum complainStatusEnum) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(OrderItem::getOrderSn, orderSn).eq(OrderItem::getSkuId, skuId); + OrderItem orderItem = getOne(queryWrapper); + if (orderItem == null) { + throw new ServiceException("当前订单项不存在!"); + } + orderItem.setComplainId(complainId); + orderItem.setComplainStatus(complainStatusEnum.name()); + updateById(orderItem); + } + + @Override + public OrderItem getBySn(String sn) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(OrderItem::getSn, sn); + return this.getOne(lambdaQueryWrapper); + } + + @Override + public List getByOrderSn(String orderSn) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(OrderItem::getOrderSn, orderSn); + return this.list(lambdaQueryWrapper); + } + + @Override + public List waitEvaluate(Date date) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.ge("o.complete_time", date); + queryWrapper.eq("oi.comment_status", CommentStatusEnum.UNFINISHED.name()); + return orderItemMapper.waitEvaluate(queryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderPriceServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderPriceServiceImpl.java new file mode 100644 index 00000000..369191cc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderPriceServiceImpl.java @@ -0,0 +1,176 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.aop.syslog.annotation.SystemLogPoint; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.OperationalJudgment; +import cn.lili.modules.goods.service.CategoryService; +import cn.lili.modules.order.order.aop.OrderLogPoint; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.mapper.TradeMapper; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.order.order.service.OrderPriceService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.payment.kit.plugin.bankTransfer.BankTransferPlugin; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * @author liushuai(liushuai711 @ gmail.com) + * @version v4.1 + * @Description: + * @since 2021/4/28 3:48 下午 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderPriceServiceImpl implements OrderPriceService { + + //线下收款 + private final BankTransferPlugin bankTransferPlugin; + + private final OrderItemService orderItemService; + + //交易数据层 + private final TradeMapper tradeMapper; + //订单 + private final OrderService orderService; + //商品分类 + private final CategoryService categoryService; + + @Override + @SystemLogPoint(description = "修改订单价格", customerLog = "'订单编号:'+#orderSn +',价格修改为:'+#orderPrice") + @OrderLogPoint(description = "'订单['+#orderSn+']修改价格,修改后价格为['+#orderPrice+']'", orderSn = "#orderSn") + public Order updatePrice(String orderSn, Double orderPrice) { + + //修改订单金额 + Order order = updateOrderPrice(orderSn, orderPrice); + + //修改订单货物金额 + updateOrderItemPrice(order); + + //修改交易金额 + tradeMapper.updateTradePrice(order.getTradeSn()); + return order; + } + + @Override + @OrderLogPoint(description = "'管理员操作订单['+#orderSn+']付款'", orderSn = "#orderSn") + public void adminPayOrder(String orderSn) { + Order order = OperationalJudgment.judgment(orderService.getBySn(orderSn)); + //如果订单已付款,则抛出异常 + if (order.getPayStatus().equals(PayStatusEnum.PAID.name())) { + throw new ServiceException(ResultCode.PAY_DOUBLE_ERROR); + } + + bankTransferPlugin.callBack(order); + + //发送订单状态变化消息 + OperationalJudgment.judgment(orderService.getBySn(orderSn)); + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setOrderSn(orderSn); + orderMessage.setPaymentMethod(PaymentMethodEnum.BANK_TRANSFER.name()); + orderMessage.setNewStatus(OrderStatusEnum.PAID); + orderService.sendUpdateStatusMessage(orderMessage); + } + + + /** + * 修改订单价格 + * 1.判定订单是否支付 + * 2.记录订单原始价格信息 + * 3.计算修改的订单金额 + * 4.修改订单价格 + * 5.保存订单信息 + * + * @param orderSn 订单编号 + * @param orderPrice 修改订单金额 + */ + private Order updateOrderPrice(String orderSn, Double orderPrice) { + Order order = OperationalJudgment.judgment(orderService.getBySn(orderSn)); + //判定是否支付 + if (order.getPayStatus().equals(PayStatusEnum.PAID.name())) { + throw new ServiceException(ResultCode.ORDER_UPDATE_PRICE_ERROR); + } + + //获取订单价格信息 + PriceDetailDTO orderPriceDetailDTO = order.getPriceDetailDTO(); + + //如果未修改过金额则记录订单原始价格 + if (orderPriceDetailDTO.getOriginalPrice() == null) { + orderPriceDetailDTO.setOriginalPrice(order.getFlowPrice()); + } + + //修改订单价格 + order.setFlowPrice(orderPrice); + //订单修改金额=使用订单原始金额-修改后金额 + orderPriceDetailDTO.setUpdatePrice(CurrencyUtil.sub(orderPriceDetailDTO.getOriginalPrice(), orderPrice)); + orderPriceDetailDTO.setFlowPrice(orderPrice); + //修改订单 + order.setPriceDetail(JSONUtil.toJsonStr(orderPriceDetailDTO)); + orderService.updateById(order); + return order; + } + + /** + * 修改订单货物金额 + * 1.计算订单货物金额在订单金额中的百分比 + * 2.订单货物金额=订单修改后金额*订单货物百分比 + * 3.订单货物修改价格=订单货物原始价格-订单货物修改后金额 + * 4.修改平台佣金 + * 5.订单实际金额=修改后订单金额-平台佣金-分销提佣 + * + * @param order 订单 + */ + private void updateOrderItemPrice(Order order) { + List orderItems = orderItemService.getByOrderSn(order.getSn()); + for (OrderItem orderItem : orderItems) { + + //获取订单货物价格信息 + PriceDetailDTO priceDetailDTO = orderItem.getPriceDetailDTO(); + + //如果未修改过金额则记录订单原始价格 + if (priceDetailDTO.getOriginalPrice() == null) { + priceDetailDTO.setOriginalPrice(orderItem.getFlowPrice()); + } + + //获取订单货物占订单金额的百分比 + Double priceFluctuationRatio = CurrencyUtil.div(priceDetailDTO.getOriginalPrice(), order.getPriceDetailDTO().getOriginalPrice()); + + //计算修改后的订单货物金额 + double flowPrice = CurrencyUtil.mul(order.getFlowPrice(), priceFluctuationRatio); + + // 记录修改金额 + priceDetailDTO.setUpdatePrice(CurrencyUtil.sub(priceDetailDTO.getOriginalPrice(), flowPrice)); + priceDetailDTO.setFlowPrice(flowPrice); + + //计算平台佣金=交易金额*分类佣金比例/100 + Double platFormCommission = CurrencyUtil.div(CurrencyUtil.mul(flowPrice, categoryService.getById(orderItem.getCategoryId()).getCommissionRate()), 100); + priceDetailDTO.setPlatFormCommission(platFormCommission); + + // 最终结算金额 = 流水金额-平台佣金-分销提佣 + double billPrice = CurrencyUtil.sub(CurrencyUtil.sub(priceDetailDTO.getFlowPrice(),priceDetailDTO.getPlatFormCommission()), + priceDetailDTO.getDistributionCommission()); + priceDetailDTO.setBillPrice(billPrice); + + //修改订单货物金额 + orderItem.setFlowPrice(flowPrice); + orderItem.setPriceDetail(JSONUtil.toJsonStr(priceDetailDTO)); + orderItemService.update(orderItem, new LambdaUpdateWrapper().eq(OrderItem::getId, orderItem.getId())); + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java new file mode 100644 index 00000000..297e3c8c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/OrderServiceImpl.java @@ -0,0 +1,712 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.aop.syslog.annotation.SystemLogPoint; +import cn.lili.common.delayqueue.DelayQueueTools; +import cn.lili.common.delayqueue.DelayQueueType; +import cn.lili.common.delayqueue.PintuanOrderMessage; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.GoodsTagsEnum; +import cn.lili.common.rocketmq.tags.MqOrderTagsEnum; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.trigger.interfaces.TimeTrigger; +import cn.lili.common.trigger.model.TimeExecuteConstant; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import cn.lili.common.utils.*; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.dto.GoodsCompleteMessage; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.member.entity.dto.MemberAddressDTO; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import cn.lili.modules.order.order.aop.OrderLogPoint; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.dos.Receipt; +import cn.lili.modules.order.order.entity.dto.OrderMessage; +import cn.lili.modules.order.order.entity.dto.OrderSearchParams; +import cn.lili.modules.order.order.entity.dto.PriceDetailDTO; +import cn.lili.modules.order.order.entity.enums.*; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import cn.lili.modules.order.order.entity.vo.OrderVO; +import cn.lili.modules.order.order.mapper.OrderItemMapper; +import cn.lili.modules.order.order.mapper.OrderMapper; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.order.order.service.ReceiptService; +import cn.lili.modules.order.order.service.StoreFlowService; +import cn.lili.modules.order.trade.entity.dos.OrderLog; +import cn.lili.modules.order.trade.service.OrderLogService; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.promotion.entity.dos.Pintuan; +import cn.lili.modules.promotion.service.PintuanService; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.util.StatisticsDateUtil; +import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.entity.vo.Traces; +import cn.lili.modules.system.service.LogisticsService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 子订单业务层实现 + * + * @author Chopper + * @date 2020/11/17 7:38 下午 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderServiceImpl extends ServiceImpl implements OrderService { + + private static final String ORDER_SN_COLUMN = "order_sn"; + + //订单数据层 + private final OrderMapper orderMapper; + + //延时任务 + private final TimeTrigger timeTrigger; + + //订单货物数据层 + private final OrderItemMapper orderItemMapper; + //发票 + @Autowired + private ReceiptService receiptService; + //订单货物 + @Autowired + private OrderItemService orderItemService; + //物流公司 + @Autowired + private LogisticsService logisticsService; + //订单日志 + @Autowired + private OrderLogService orderLogService; + //RocketMQ + @Autowired + private RocketMQTemplate rocketMQTemplate; + //RocketMQ配置 + @Autowired + private RocketmqCustomProperties rocketmqCustomProperties; + //订单流水 + @Autowired + private StoreFlowService storeFlowService; + //拼团 + @Autowired + private PintuanService pintuanService; + //规格商品 + @Autowired + private GoodsSkuService goodsSkuService; + + @Override + public void intoDB(TradeDTO tradeDTO) { + List orders = new ArrayList<>(tradeDTO.getCartList().size()); + List orderItems = new ArrayList<>(); + List orderLogs = new ArrayList<>(); + if (tradeDTO.getParentOrderSn() != null) { + Order parentOrder = this.getBySn(tradeDTO.getParentOrderSn()); + if (parentOrder.getMemberId().equals(UserContext.getCurrentUser().getId())) { + throw new ServiceException("不能参与自己发起的拼团活动!"); + } + } + List list = new ArrayList<>(); + tradeDTO.getCartList().forEach(item -> { + Order order = new Order(item, tradeDTO); + if (OrderTypeEnum.PINTUAN.name().equals(order.getOrderType())) { + Pintuan pintuan = pintuanService.getPintuanById(order.getPromotionId()); + Integer limitNum = pintuan.getLimitNum(); + if (limitNum != 0 && order.getGoodsNum() > limitNum) { + throw new ServiceException("购买数量超过拼团活动限制数量"); + } + } + //构建orderVO对象 + OrderVO orderVO = new OrderVO(); + BeanUtil.copyProperties(order, orderVO); + orders.add(order); + String message = "订单[" + item.getSn() + "]创建"; + orderLogs.add(new OrderLog(item.getSn(), UserContext.getCurrentUser().getId(), UserContext.getCurrentUser().getRole().getRole(), UserContext.getCurrentUser().getUsername(), message)); + item.getSkuList().forEach( + sku -> orderItems.add(new OrderItem(sku, item, tradeDTO)) + ); + orderVO.setOrderItems(orderItems); + list.add(orderVO); + }); + tradeDTO.setOrderVO(list); + //批量保存订单 + this.saveBatch(orders); + //批量保存 子订单 + orderItemService.saveBatch(orderItems); + // 批量记录订单操作日志 + orderLogService.saveBatch(orderLogs); + // 赠品根据店铺单独生成订单 + this.generatorGiftOrder(tradeDTO); + } + + @Override + public IPage queryByParams(OrderSearchParams orderSearchParams) { + return this.baseMapper.queryByParams(PageUtil.initPage(orderSearchParams), orderSearchParams.queryWrapper()); + } + + @Override + public OrderDetailVO queryDetail(String orderSn) { + Order order = this.getBySn(orderSn); + if (order == null) { + throw new ServiceException(ResultCode.ORDER_NOT_EXIST); + } + QueryWrapper orderItemWrapper = new QueryWrapper<>(); + orderItemWrapper.eq(ORDER_SN_COLUMN, orderSn); + //查询订单项信息 + List orderItems = orderItemMapper.selectList(orderItemWrapper); + //查询订单日志信息 + List orderLogs = orderLogService.getOrderLog(orderSn); + //查询发票信息 + Receipt receipt = receiptService.getByOrderSn(orderSn); + //查询订单和自订单,然后写入vo返回 + return new OrderDetailVO(order, orderItems, orderLogs, receipt); + } + + @Override + @OrderLogPoint(description = "'订单['+#orderSn+']取消,原因为:'+#reason", orderSn = "#orderSn") + public Order cancel(String orderSn, String reason) { + Order order = OperationalJudgment.judgment(this.getBySn(orderSn)); + if (order.getOrderType().equals(OrderTypeEnum.PINTUAN.name()) && !order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name())) { + throw new ServiceException("未成团订单不可取消"); + } + if (CharSequenceUtil.equalsAny(order.getOrderStatus(), + OrderStatusEnum.UNDELIVERED.name(), + OrderStatusEnum.UNPAID.name(), + OrderStatusEnum.PAID.name())) { + + order.setOrderStatus(OrderStatusEnum.CANCELLED.name()); + order.setCancelReason(reason); + //修改订单 + this.updateById(order); + orderStatusMessage(order); + return order; + } else { + throw new ServiceException("当前订单状态不可取消"); + } + } + + + @Override + @OrderLogPoint(description = "'订单['+#orderSn+']系统取消,原因为:'+#reason", orderSn = "#orderSn") + public void systemCancel(String orderSn, String reason) { + Order order = this.getBySn(orderSn); + order.setOrderStatus(OrderStatusEnum.CANCELLED.name()); + order.setCancelReason(reason); + this.updateById(order); + orderStatusMessage(order); + } + + /** + * 获取订单 + * + * @param orderSn 订单编号 + * @return 订单详情 + */ + @Override + public Order getBySn(String orderSn) { + QueryWrapper orderWrapper = new QueryWrapper<>(); + orderWrapper.eq("sn", orderSn); + return this.getOne(orderWrapper); + } + + @Override + public void payOrder(String orderSn, String paymentMethod, String receivableNo) { + + Order order = this.getBySn(orderSn); + //如果订单已支付,就不能再次进行支付 + if (order.getPayStatus().equals(PayStatusEnum.PAID.name())) { + throw new ServiceException(ResultCode.PAY_DOUBLE_ERROR); + } + + //修改订单状态 + order.setPaymentTime(new Date()); + order.setPaymentMethod(paymentMethod); + order.setPayStatus(PayStatusEnum.PAID.name()); + order.setOrderStatus(OrderStatusEnum.PAID.name()); + order.setReceivableNo(receivableNo); + this.updateById(order); + + //记录订单流水 + storeFlowService.payOrder(orderSn); + + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setOrderSn(order.getSn()); + orderMessage.setPaymentMethod(paymentMethod); + orderMessage.setNewStatus(OrderStatusEnum.PAID); + this.sendUpdateStatusMessage(orderMessage); + + String message = "订单付款,付款方式[" + PaymentMethodEnum.valueOf(paymentMethod).paymentName() + "]"; + OrderLog orderLog = new OrderLog(orderSn, "-1", UserEnums.SYSTEM.name(), "系统操作", message); + orderLogService.save(orderLog); + + + } + + @Override + @OrderLogPoint(description = "'库存确认'", orderSn = "#orderSn") + public void afterOrderConfirm(String orderSn) { + Order order = this.getBySn(orderSn); + LambdaUpdateWrapper orderLambdaUpdateWrapper = new LambdaUpdateWrapper<>(); + + //如果为虚拟订单-修改订单状态为待核验 + if (order.getOrderType().equals(OrderTypeEnum.FICTITIOUS.name())) { + //填写提货码 + String code = CommonUtil.getRandomNum(); + orderLambdaUpdateWrapper.eq(Order::getSn, orderSn); + orderLambdaUpdateWrapper.set(Order::getQrCode, code); + orderLambdaUpdateWrapper.set(Order::getOrderStatus, OrderStatusEnum.TAKE.name()); + orderLambdaUpdateWrapper.set(Order::getCanReturn, !PaymentMethodEnum.BANK_TRANSFER.name().equals(order.getPaymentMethod())); + + this.update(orderLambdaUpdateWrapper); + + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setNewStatus(OrderStatusEnum.TAKE); + orderMessage.setOrderSn(order.getSn()); + this.sendUpdateStatusMessage(orderMessage); + } + //如果为商品订单-修改订单状态为待发货 + else { + orderLambdaUpdateWrapper.eq(Order::getSn, orderSn); + //拼团订单 + if (order.getOrderType().equals(OrderTypeEnum.PINTUAN.name()) && order.getPromotionId() != null) { + //校验拼团是否成团 对拼团结果进行处理 + this.checkPintuanOrder(order.getPromotionId(), order.getParentOrderSn()); + } else { + //普通订单直接修改为代发货状态 + orderLambdaUpdateWrapper.set(Order::getOrderStatus, OrderStatusEnum.UNDELIVERED.name()); + orderLambdaUpdateWrapper.set(Order::getCanReturn, !PaymentMethodEnum.BANK_TRANSFER.name().equals(order.getPaymentMethod())); + this.update(orderLambdaUpdateWrapper); + + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setNewStatus(OrderStatusEnum.UNDELIVERED); + orderMessage.setOrderSn(order.getSn()); + this.sendUpdateStatusMessage(orderMessage); + } + } + + // 发送当前商品购买完成的信息(用于更新商品数据) + List orderItems = orderItemService.getByOrderSn(orderSn); + List goodsCompleteMessageList = new ArrayList<>(); + for (OrderItem orderItem : orderItems) { + GoodsCompleteMessage goodsCompleteMessage = new GoodsCompleteMessage(); + goodsCompleteMessage.setGoodsId(orderItem.getGoodsId()); + goodsCompleteMessage.setSkuId(orderItem.getSkuId()); + goodsCompleteMessage.setBuyNum(orderItem.getNum()); + goodsCompleteMessage.setMemberId(order.getMemberId()); + goodsCompleteMessageList.add(goodsCompleteMessage); + } + if (!goodsCompleteMessageList.isEmpty()) { + String destination = rocketmqCustomProperties.getGoodsTopic() + ":" + GoodsTagsEnum.BUY_GOODS_COMPLETE.name(); + //发送订单变更mq消息 + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(goodsCompleteMessageList), RocketmqSendCallbackBuilder.commonCallback()); + } + + + } + + + @Override + @SystemLogPoint(description = "修改订单", customerLog = "'订单[' + #orderSn + ']收货信息修改,修改为'+#memberAddressDTO.consigneeDetail+'") + public Order updateConsignee(String orderSn, MemberAddressDTO memberAddressDTO) { + Order order = OperationalJudgment.judgment(this.getBySn(orderSn)); + + //要记录之前的收货地址,所以需要以代码方式进行调用 不采用注解 + String message = "订单[" + orderSn + "]收货信息修改,由[" + order.getConsigneeDetail() + "]修改为[" + memberAddressDTO.getConsigneeDetail() + "]"; + + BeanUtil.copyProperties(memberAddressDTO, order);// 记录订单操作日志 + this.updateById(order); + + OrderLog orderLog = new OrderLog(orderSn, UserContext.getCurrentUser().getId(), UserContext.getCurrentUser().getRole().getRole(), UserContext.getCurrentUser().getUsername(), message); + orderLogService.save(orderLog); + + return order; + } + + @Override + @OrderLogPoint(description = "'订单['+#orderSn+']发货,发货单号['+#logisticsNo+']'", orderSn = "#orderSn") + public Order delivery(String orderSn, String logisticsNo, String logisticsId) { + Order order = OperationalJudgment.judgment(this.getBySn(orderSn)); + //如果订单未发货,并且订单状态值等于待发货 + if (order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()) && order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name())) { + //获取对应物流 + Logistics logistics = logisticsService.getById(logisticsId); + if (logistics == null) { + throw new ServiceException(ResultCode.ORDER_LOGISTICS_ERROR); + } + //写入物流信息 + order.setLogisticsCode(logistics.getId()); + order.setLogisticsName(logistics.getName()); + order.setLogisticsNo(logisticsNo); + order.setLogisticsTime(new Date()); + order.setDeliverStatus(DeliverStatusEnum.DELIVERED.name()); + this.updateById(order); + //修改订单状态为已发送 + this.updateStatus(orderSn, OrderStatusEnum.DELIVERED); + + + //修改订单货物可以进行售后、投诉 + orderItemService.update(new UpdateWrapper().eq(ORDER_SN_COLUMN, orderSn) + .set("after_sale_status", OrderItemAfterSaleStatusEnum.NOT_APPLIED) + .set("complain_status", OrderComplaintStatusEnum.NO_APPLY)); + //发送订单状态改变消息 + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setNewStatus(OrderStatusEnum.DELIVERED); + orderMessage.setOrderSn(order.getSn()); + this.sendUpdateStatusMessage(orderMessage); + + + } else { + throw new ServiceException(ResultCode.ORDER_DELIVER_ERROR); + } + return order; + } + + @Override + public Traces getTraces(String orderSn) { + //获取订单信息 + Order order = this.getBySn(orderSn); + //获取踪迹信息 + return logisticsService.getLogistic(order.getLogisticsCode(), order.getLogisticsNo()); + } + + @Override + @OrderLogPoint(description = "'订单['+#orderSn+']核销,核销码['+#qrCode+']'", orderSn = "#orderSn") + public Order take(String orderSn, String qrCode) { + //是否可以查询到订单 + Order order = OperationalJudgment.judgment(this.getBySn(orderSn)); + //判断是否为虚拟订单 + if (!order.getOrderType().equals(OrderTypeEnum.FICTITIOUS.name())) { + throw new ServiceException(ResultCode.ORDER_TAKE_ERROR); + } + //判断虚拟订单状态 + if (order.getOrderStatus().equals(OrderStatusEnum.TAKE.name())) { + //判断提货码是否正确\修改订单状态 + if (order.getOrderStatus().equals(OrderStatusEnum.TAKE.name()) && qrCode.equals(order.getQrCode())) { + order.setOrderStatus(OrderStatusEnum.COMPLETED.name()); + + this.updateById(order); + + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setNewStatus(OrderStatusEnum.COMPLETED); + orderMessage.setOrderSn(order.getSn()); + this.sendUpdateStatusMessage(orderMessage); + } + return order; + } + throw new ServiceException(ResultCode.ORDER_TAKE_ERROR); + } + + @Override + @OrderLogPoint(description = "'订单['+#orderSn+']完成'", orderSn = "#orderSn") + public void complete(String orderSn) { + //是否可以查询到订单 + Order order = OperationalJudgment.judgment(this.getBySn(orderSn)); + + //修改订单状态为完成 + this.updateStatus(orderSn, OrderStatusEnum.COMPLETED); + + //修改订单货物可以进行评价 + orderItemService.update(new UpdateWrapper().eq(ORDER_SN_COLUMN, orderSn) + .set("comment_status", CommentStatusEnum.UNFINISHED)); + //发送订单状态改变消息 + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setNewStatus(OrderStatusEnum.COMPLETED); + orderMessage.setOrderSn(order.getSn()); + this.sendUpdateStatusMessage(orderMessage); + } + + @Override + public List getByTradeSn(String tradeSn) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + return this.list(queryWrapper.eq(Order::getTradeSn, tradeSn)); + } + + @Override + public void sendUpdateStatusMessage(OrderMessage orderMessage) { + String destination = rocketmqCustomProperties.getOrderTopic() + ":" + MqOrderTagsEnum.STATUS_CHANGE.name(); + //发送订单变更mq消息 + rocketMQTemplate.asyncSend(destination, JSONUtil.toJsonStr(orderMessage), RocketmqSendCallbackBuilder.commonCallback()); + } + + @Override + public void deleteOrder(String sn) { + Order order = this.getBySn(sn); + if (order == null) { + log.error("订单号为" + sn + "的订单不存在!"); + throw new ServiceException(); + } + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Order::getSn, sn).set(Order::getDeleteFlag, true); + this.update(updateWrapper); + LambdaUpdateWrapper orderItemLambdaUpdateWrapper = new LambdaUpdateWrapper<>(); + orderItemLambdaUpdateWrapper.eq(OrderItem::getOrderSn, sn).set(OrderItem::getDeleteFlag, true); + this.orderItemService.update(orderItemLambdaUpdateWrapper); + } + + @Override + public IPage getStatistics(StatisticsQueryParam statisticsQueryParam, PageVO pageVO) { + + QueryWrapper queryWrapper = new QueryWrapper<>(); + Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam); + queryWrapper.between("o.create_time", dates[0], dates[1]); + queryWrapper.eq(StringUtils.isNotEmpty(statisticsQueryParam.getStoreId()), + "o.store_id", statisticsQueryParam.getStoreId()); + + queryWrapper.eq("o.delete_flag", false); + queryWrapper.groupBy("o.id"); + queryWrapper.orderByDesc("o.id"); + return orderMapper.queryByParams(PageUtil.initPage(pageVO), queryWrapper); + } + + @Override + public Boolean invoice(String sn) { + //根据订单号查询发票信息 + Receipt receipt = receiptService.getByOrderSn(sn); + //校验发票信息是否存在 + if (receipt != null) { + receipt.setReceiptStatus(1); + return receiptService.updateById(receipt); + } + throw new ServiceException(ResultCode.USER_RECEIPT_NOT_EXIST); + } + + /** + * 自动成团订单处理 + * + * @param pintuanId 拼团活动id + * @param parentOrderSn 拼团订单sn + */ + @Override + public void agglomeratePintuanOrder(String pintuanId, String parentOrderSn) { + //获取拼团配置 + Pintuan pintuan = pintuanService.getPintuanById(pintuanId); + List list = this.getPintuanOrder(pintuanId, parentOrderSn); + if (Boolean.TRUE.equals(pintuan.getFictitious()) && pintuan.getRequiredNum() > list.size()) { + // 如果开启虚拟成团且当前订单数量不足成团数量,则认为拼团成功 + this.pintuanOrderSuccess(list); + } else if (Boolean.FALSE.equals(pintuan.getFictitious()) && pintuan.getRequiredNum() > list.size()) { + // 如果未开启虚拟成团且当前订单数量不足成团数量,则认为拼团失败 + this.pintuanOrderFailed(list); + } + } + + /** + * 订单状态变更消息 + * + * @param order + */ + private void orderStatusMessage(Order order) { + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setOrderSn(order.getSn()); + orderMessage.setNewStatus(OrderStatusEnum.valueOf(order.getOrderStatus())); + this.sendUpdateStatusMessage(orderMessage); + } + + /** + * 此方法只提供内部调用,调用前应该做好权限处理 + * 修改订单状态 + * + * @param orderSn 订单编号 + * @param orderStatus 订单状态 + */ + private void updateStatus(String orderSn, OrderStatusEnum orderStatus) { + this.baseMapper.updateStatus(orderStatus.name(), orderSn); + } + + private void checkPintuanOrder(String pintuanId, String parentOrderSn) { + //拼团有效参数判定 + if (CharSequenceUtil.isEmpty(parentOrderSn)) { + return; + } + //获取拼团配置 + Pintuan pintuan = pintuanService.getPintuanById(pintuanId); + List list = this.getPintuanOrder(pintuanId, parentOrderSn); + int count = list.size(); + if (count == 1) { + // 如果为开团订单,则发布一个一小时的延时任务,时间到达后,如果未成团则自动结束(未开启虚拟成团的情况下) + PintuanOrderMessage pintuanOrderMessage = new PintuanOrderMessage(); + long startTime = DateUtil.offsetHour(new Date(), 1).getTime(); + pintuanOrderMessage.setOrderSn(parentOrderSn); + pintuanOrderMessage.setPintuanId(pintuanId); + TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.PROMOTION_EXECUTOR, + startTime, + pintuanOrderMessage, + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PINTUAN_ORDER, (pintuanId + parentOrderSn)), + rocketmqCustomProperties.getPromotionTopic()); + this.timeTrigger.addDelay(timeTriggerMsg, cn.lili.common.utils.DateUtil.getDelayTime(startTime)); + } + //拼团所需人数,小于等于 参团后的人数,则说明成团,所有订单成团 + if (pintuan.getRequiredNum() <= count) { + this.pintuanOrderSuccess(list); + } + } + + /** + * 根据拼团活动id和拼团订单sn获取所有当前与当前拼团订单sn相关的订单 + * + * @param pintuanId 拼团活动id + * @param parentOrderSn 拼团订单sn + * @return 所有当前与当前拼团订单sn相关的订单 + */ + private List getPintuanOrder(String pintuanId, String parentOrderSn) { + //寻找拼团的所有订单 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Order::getPromotionId, pintuanId) + .eq(Order::getOrderType, OrderTypeEnum.PINTUAN.name()) + .eq(Order::getPayStatus, PayStatusEnum.PAID.name()); + // 拼团sn=开团订单sn 或者 参团订单的开团订单sn + queryWrapper.and(i -> i.eq(Order::getSn, parentOrderSn) + .or(j -> j.eq(Order::getParentOrderSn, parentOrderSn))); + //参团后的订单数(人数) + return this.list(queryWrapper); + } + + /** + * 根据提供的拼团订单列表更新拼团状态为拼团成功 + * + * @param list 需要更新拼团状态为成功的拼团订单列表 + */ + private void pintuanOrderSuccess(List list) { + for (Order order : list) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Order::getId, order.getId()); + updateWrapper.set(Order::getOrderStatus, OrderStatusEnum.UNDELIVERED.name()); + updateWrapper.set(Order::getCanReturn, !PaymentMethodEnum.BANK_TRANSFER.name().equals(order.getPaymentMethod())); + this.update(updateWrapper); + } + } + + /** + * 根据提供的拼团订单列表更新拼团状态为拼团失败 + * + * @param list 需要更新拼团状态为失败的拼团订单列表 + */ + private void pintuanOrderFailed(List list) { + for (Order order : list) { +// LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); +// updateWrapper.eq(Order::getId, order.getId()); +// updateWrapper.set(Order::getOrderStatus, OrderStatusEnum.CANCELLED.name()); +// updateWrapper.set(Order::getCancelReason, "拼团人数不足,拼团失败!"); +// this.update(updateWrapper); + try { + this.cancel(order.getSn(), "拼团人数不足,拼团失败!"); + } catch (Exception e) { + log.error("拼团订单取消失败", e); + } + } + } + + /** + * 生成赠品订单 + * + * @param tradeDTO 生成订单所需参数 + */ + private void generatorGiftOrder(TradeDTO tradeDTO) { + List orders = new ArrayList<>(tradeDTO.getCartList().size()); + List orderItems = new ArrayList<>(); + List orderLogs = new ArrayList<>(); + for (CartVO cartVO : tradeDTO.getCartList()) { + if (cartVO.getGiftList() != null && !cartVO.getGiftList().isEmpty()) { + Order order = new Order(); + PriceDetailDTO priceDetailDTO = new PriceDetailDTO(); + BeanUtil.copyProperties(cartVO, order, "id"); + BeanUtil.copyProperties(priceDetailDTO, order, "id"); + order.setSn(SnowFlake.createStr("G")); + order.setTradeSn(tradeDTO.getSn()); + order.setOrderType(OrderTypeEnum.GIFT.name()); + order.setOrderStatus(OrderStatusEnum.UNPAID.name()); + order.setPayStatus(PayStatusEnum.UNPAID.name()); + order.setDeliverStatus(DeliverStatusEnum.UNDELIVERED.name()); + order.setMemberId(tradeDTO.getMemberId()); + order.setMemberName(tradeDTO.getMemberName()); + order.setNeedReceipt(false); + order.setPriceDetailDTO(priceDetailDTO); + order.setClientType(tradeDTO.getClientType()); + + if (tradeDTO.getMemberAddress() != null) { + order.setConsigneeAddressIdPath(tradeDTO.getMemberAddress().getConsigneeAddressIdPath()); + order.setConsigneeAddressPath(tradeDTO.getMemberAddress().getConsigneeAddressPath()); + order.setConsigneeDetail(tradeDTO.getMemberAddress().getDetail()); + order.setConsigneeMobile(tradeDTO.getMemberAddress().getMobile()); + order.setConsigneeName(tradeDTO.getMemberAddress().getName()); + } + orders.add(order); + String message = "赠品订单[" + order.getSn() + "]创建"; + orderLogs.add(new OrderLog(order.getSn(), UserContext.getCurrentUser().getId(), UserContext.getCurrentUser().getRole().getRole(), UserContext.getCurrentUser().getUsername(), message)); + for (String giftGoodsId : cartVO.getGiftList()) { + GoodsSku goodsSkuByIdFromCache = goodsSkuService.getGoodsSkuByIdFromCache(giftGoodsId); + OrderItem orderItem = new OrderItem(); + BeanUtil.copyProperties(goodsSkuByIdFromCache, orderItem, "id"); + BeanUtil.copyProperties(priceDetailDTO, orderItem, "id"); + orderItem.setAfterSaleStatus(OrderItemAfterSaleStatusEnum.NEW.name()); + orderItem.setCommentStatus(CommentStatusEnum.NEW.name()); + orderItem.setComplainStatus(OrderComplaintStatusEnum.NEW.name()); + orderItem.setNum(cartVO.getGoodsNum()); + orderItem.setOrderSn(order.getSn()); + orderItem.setTradeSn(tradeDTO.getSn()); + orderItem.setImage(goodsSkuByIdFromCache.getThumbnail()); + orderItem.setGoodsName(goodsSkuByIdFromCache.getGoodsName()); + orderItem.setSkuId(goodsSkuByIdFromCache.getId()); + orderItem.setCategoryId(goodsSkuByIdFromCache.getCategoryPath().substring( + goodsSkuByIdFromCache.getCategoryPath().lastIndexOf(",") + 1 + )); + orderItem.setGoodsPrice(goodsSkuByIdFromCache.getPrice()); + orderItem.setPriceDetailDTO(priceDetailDTO); + orderItems.add(orderItem); + } + } + } + if (!orders.isEmpty()) { + this.saveBatch(orders); + orderItemService.saveBatch(orderItems); + orderLogService.saveBatch(orderLogs); + for (Order order : orders) { + OrderMessage orderMessage = new OrderMessage(); + orderMessage.setOrderSn(order.getSn()); + orderMessage.setPaymentMethod(PaymentMethodEnum.BANK_TRANSFER.name()); + orderMessage.setNewStatus(OrderStatusEnum.PAID); + this.sendUpdateStatusMessage(orderMessage); + } + } + } + + @Autowired + public void setStoreFlowService(StoreFlowService storeFlowService) { + this.storeFlowService = storeFlowService; + } + + @Autowired + public void setPintuanService(PintuanService pintuanService) { + this.pintuanService = pintuanService; + } + + @Autowired + private void setGoodsSkuService(GoodsSkuService goodsSkuService) { + this.goodsSkuService = goodsSkuService; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/ReceiptServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/ReceiptServiceImpl.java new file mode 100644 index 00000000..4c076927 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/ReceiptServiceImpl.java @@ -0,0 +1,81 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.Receipt; +import cn.lili.modules.order.order.entity.dto.OrderReceiptDTO; +import cn.lili.modules.order.order.entity.dto.ReceiptSearchParams; +import cn.lili.modules.order.order.mapper.OrderItemMapper; +import cn.lili.modules.order.order.mapper.ReceiptMapper; +import cn.lili.modules.order.order.service.ReceiptService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 发票业务层实现 + * + * @author Bulbasaur + * @date 2020/11/17 7:38 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ReceiptServiceImpl extends ServiceImpl implements ReceiptService { + + //发票mapper + private final ReceiptMapper receiptMapper; + + + @Override + public IPage getReceiptData(ReceiptSearchParams searchParams, PageVO pageVO) { + return receiptMapper.getReceipt(PageUtil.initPage(pageVO), searchParams.wrapper()); + } + + @Override + public Receipt getByOrderSn(String orderSn) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(Receipt::getOrderSn, orderSn); + return this.getOne(lambdaQueryWrapper); + } + + @Override + public Receipt getDetail(String id) { + return this.getById(id); + } + + @Override + public Receipt saveReceipt(Receipt receipt) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Receipt::getReceiptTitle, receipt.getReceiptTitle()); + queryWrapper.eq(Receipt::getMemberId, receipt.getMemberId()); + if (receipt.getId() != null) { + queryWrapper.ne(Receipt::getId, receipt.getId()); + } + if (this.getOne(queryWrapper) == null) { + this.save(receipt); + return receipt; + } + throw new ServiceException(ResultCode.ERROR); + } + + @Override + public Receipt invoicing(String receiptId) { + //根据id查询发票信息 + Receipt receipt = this.getById(receiptId); + if (receipt != null) { + receipt.setReceiptStatus(1); + this.saveOrUpdate(receipt); + return receipt; + } + throw new ServiceException(ResultCode.ERROR); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/StoreFlowServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/StoreFlowServiceImpl.java new file mode 100644 index 00000000..8c7772ea --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/StoreFlowServiceImpl.java @@ -0,0 +1,182 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.lili.common.utils.*; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.service.CategoryService; +import cn.lili.modules.goods.service.GoodsService; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.order.order.entity.enums.FlowTypeEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.mapper.StoreFlowMapper; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.order.order.service.StoreFlowService; +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.service.PaymentService; +import cn.lili.modules.payment.service.RefundLogService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 商家订单流水业务层实现 + * + * @author Chopper + * @date 2020/11/17 7:38 下午 + */ +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreFlowServiceImpl extends ServiceImpl implements StoreFlowService { + + //商品 + private final GoodsService goodsService; + //商品分类 + private final CategoryService categoryService; + //订单 + private OrderService orderService; + //订单货物 + private OrderItemService orderItemService; + //退款日志 + private RefundLogService refundLogService; + //支付日志 + private PaymentService paymentService; + + @Override + public void payOrder(String orderSn) { + //根据订单编号获取子订单列表 + List orderItems = orderItemService.getByOrderSn(orderSn); + //根据订单编号获取订单数据 + Order order = orderService.getBySn(orderSn); + + List sns = new ArrayList<>(); + sns.add(order.getSn()); + sns.add(order.getTradeSn()); + + //如果查询到多条支付记录,打印日志 + if (order.getPayStatus().equals(PayStatusEnum.PAID.name())) { + log.error("订单[{}]检测到重复付款,请处理", orderSn); + } + //循环子订单记录流水 + for (OrderItem item : orderItems) { + StoreFlow storeFlow = new StoreFlow(); + BeanUtil.copyProperties(item, storeFlow); + + //入账 + storeFlow.setId(SnowFlake.getIdStr()); + storeFlow.setFlowType(FlowTypeEnum.PAY.name()); + storeFlow.setSn(SnowFlake.createStr("SF")); + storeFlow.setOrderSn(item.getOrderSn()); + storeFlow.setOrderItemSn(item.getSn()); + storeFlow.setStoreId(order.getStoreId()); + storeFlow.setStoreName(order.getStoreName()); + storeFlow.setMemberId(order.getMemberId()); + storeFlow.setMemberName(order.getMemberName()); + storeFlow.setGoodsName(item.getGoodsName()); + + //计算平台佣金 + storeFlow.setFinalPrice(item.getPriceDetailDTO().getFlowPrice()); + storeFlow.setCommissionPrice(item.getPriceDetailDTO().getPlatFormCommission()); + storeFlow.setDistributionRebate(item.getPriceDetailDTO().getDistributionCommission()); + storeFlow.setBillPrice(item.getPriceDetailDTO().getBillPrice()); + + //添加支付方式 + storeFlow.setPaymentName(order.getPaymentMethod()); + //添加第三方支付流水号 + storeFlow.setTransactionId(order.getReceivableNo()); + + //添加付款交易流水 + this.save(storeFlow); + } + } + + @Override + public void refundOrder(AfterSale afterSale) { + StoreFlow storeFlow = new StoreFlow(); + //退款 + storeFlow.setFlowType(FlowTypeEnum.REFUND.name()); + storeFlow.setSn(SnowFlake.createStr("SF")); + storeFlow.setRefundSn(afterSale.getSn()); + storeFlow.setOrderSn(afterSale.getOrderSn()); + storeFlow.setOrderItemSn(afterSale.getOrderItemSn()); + storeFlow.setStoreId(afterSale.getStoreId()); + storeFlow.setStoreName(afterSale.getStoreName()); + storeFlow.setMemberId(afterSale.getMemberId()); + storeFlow.setMemberName(afterSale.getMemberName()); + storeFlow.setGoodsId(afterSale.getGoodsId()); + storeFlow.setGoodsName(afterSale.getGoodsName()); + storeFlow.setSkuId(afterSale.getSkuId()); + storeFlow.setImage(afterSale.getGoodsImage()); + storeFlow.setSpecs(afterSale.getSpecs()); + + + //获取付款信息 + StoreFlow payStoreFlow = this.getOne(new LambdaUpdateWrapper().eq(StoreFlow::getOrderItemSn, afterSale.getOrderItemSn())); + storeFlow.setNum(afterSale.getNum()); + storeFlow.setCategoryId(payStoreFlow.getCategoryId()); + //佣金 + storeFlow.setCommissionPrice(CurrencyUtil.mul(CurrencyUtil.div(payStoreFlow.getCommissionPrice(), payStoreFlow.getNum()), afterSale.getNum())); + //分销佣金 + storeFlow.setDistributionRebate(CurrencyUtil.mul(CurrencyUtil.div(payStoreFlow.getDistributionRebate(), payStoreFlow.getNum()), afterSale.getNum())); + //流水金额 + storeFlow.setFinalPrice(afterSale.getActualRefundPrice()); + //最终结算金额 + storeFlow.setBillPrice(CurrencyUtil.add(CurrencyUtil.add(storeFlow.getFinalPrice(), storeFlow.getDistributionRebate()), storeFlow.getCommissionPrice())); + //获取第三方支付流水号 + RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper().eq(RefundLog::getAfterSaleNo, afterSale.getSn())); + storeFlow.setTransactionId(refundLog.getReceivableNo()); + storeFlow.setPaymentName(refundLog.getPaymentName()); + this.save(storeFlow); + } + + @Override + public IPage getStoreFlow(String storeId, String type, boolean distribution, PageVO pageVO, Date startTime, Date endTime) { + + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(StoreFlow::getStoreId, storeId); + if (distribution) { + lambdaQueryWrapper.isNotNull(StoreFlow::getDistributionRebate); + } + lambdaQueryWrapper.between(StoreFlow::getCreateTime, startTime, endTime); + if (StringUtils.isNotEmpty(type)) { + lambdaQueryWrapper.eq(StoreFlow::getFlowType, type); + } + + return this.page(PageUtil.initPage(pageVO), lambdaQueryWrapper); + } + + @Autowired + public void setOrderItemService(OrderItemService orderItemService) { + this.orderItemService = orderItemService; + } + + @Autowired + public void setOrderService(OrderService orderService) { + this.orderService = orderService; + } + + @Autowired + public void setRefundLogService(RefundLogService refundLogService) { + this.refundLogService = refundLogService; + } + + @Autowired + public void setPaymentService(PaymentService paymentService) { + this.paymentService = paymentService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/TradeServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/TradeServiceImpl.java new file mode 100644 index 00000000..4dfcd7f3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/serviceimpl/TradeServiceImpl.java @@ -0,0 +1,153 @@ +package cn.lili.modules.order.order.serviceimpl; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.MqOrderTagsEnum; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.order.cart.entity.dto.MemberCouponDTO; +import cn.lili.modules.order.cart.entity.dto.TradeDTO; +import cn.lili.modules.order.cart.entity.vo.CartVO; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.Trade; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.mapper.TradeMapper; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.order.order.service.TradeService; +import cn.lili.modules.promotion.service.CouponService; +import cn.lili.modules.promotion.service.MemberCouponService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 交易业务层实现 + * + * @author Chopper + * @date 2020/11/17 7:39 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class TradeServiceImpl extends ServiceImpl implements TradeService { + + //缓存 + private final Cache cache; + //订单 + private final OrderService orderService; + //会员 + private final MemberService memberService; + //优惠券 + private final CouponService couponService; + //会员优惠券 + private final MemberCouponService memberCouponService; + //RocketMQ + private final RocketMQTemplate rocketMQTemplate; + //RocketMQ 配置 + private final RocketmqCustomProperties rocketmqCustomProperties; + + + @Override + @Transactional(rollbackFor = Exception.class) + public Trade createTrade(TradeDTO tradeDTO) { + Trade trade = new Trade(tradeDTO); + String key = CachePrefix.TRADE.getPrefix() + trade.getSn(); + //积分预处理 + pointPretreatment(tradeDTO); + //优惠券预处理 + couponPretreatment(tradeDTO); + this.save(trade); + orderService.intoDB(tradeDTO); + //写入缓存,给消费者调用 + cache.put(key, tradeDTO); + //构建订单创建消息 + String destination = rocketmqCustomProperties.getOrderTopic() + ":" + MqOrderTagsEnum.ORDER_CREATE.name(); + //发送订单创建消息 + rocketMQTemplate.asyncSend(destination, key, RocketmqSendCallbackBuilder.commonCallback()); + return trade; + } + + @Override + public Trade getBySn(String sn) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Trade::getSn, sn); + return this.getOne(queryWrapper); + } + + + @Override + public void payTrade(String tradeSn, String paymentName, String receivableNo) { + LambdaQueryWrapper orderQueryWrapper = new LambdaQueryWrapper<>(); + orderQueryWrapper.eq(Order::getTradeSn, tradeSn); + List orders = orderService.list(orderQueryWrapper); + for (Order order : orders) { + orderService.payOrder(order.getSn(), paymentName, receivableNo); + } + Trade trade = this.getBySn(tradeSn); + trade.setPayStatus(PayStatusEnum.PAID.name()); + this.saveOrUpdate(trade); + } + + /** + * 积分预处理 + * 下单同时,使用积分 + * + * @param tradeDTO + */ + private void pointPretreatment(TradeDTO tradeDTO) { + StringBuilder orderSns = new StringBuilder(); + for (CartVO item : tradeDTO.getCartList()) { + orderSns.append(item.getSn()).append(","); + } + if (tradeDTO.getPriceDetailDTO() != null && tradeDTO.getPriceDetailDTO().getPayPoint() != null && tradeDTO.getPriceDetailDTO().getPayPoint() > 0) { + Member userInfo = memberService.getUserInfo(); + if (userInfo.getPoint() < tradeDTO.getPriceDetailDTO().getPayPoint()) { + throw new ServiceException(ResultCode.PAY_POINT_ENOUGH); + } + boolean result = memberService.updateMemberPoint(tradeDTO.getPriceDetailDTO(). + getPayPoint().longValue(), 0, tradeDTO.getMemberId(), + "订单【" + orderSns + "】创建,积分扣减"); + + if (!result) { + throw new ServiceException(ResultCode.PAY_POINT_ENOUGH); + } + } + } + + + /** + * 优惠券预处理 + * 下单同时,扣除优惠券 + * + * @param tradeDTO + */ + private void couponPretreatment(TradeDTO tradeDTO) { + List memberCouponDTOList = new ArrayList<>(); + if (null != tradeDTO.getPlatformCoupon()) { + memberCouponDTOList.add(tradeDTO.getPlatformCoupon()); + } + Collection storeCoupons = tradeDTO.getStoreCoupons().values(); + if (!storeCoupons.isEmpty()) { + memberCouponDTOList.addAll(storeCoupons); + } + List ids = memberCouponDTOList.stream().map(e -> e.getMemberCoupon().getId()).collect(Collectors.toList()); + memberCouponService.used(ids); + memberCouponDTOList.forEach(e -> couponService.usedCoupon(e.getMemberCoupon().getCouponId(), 1)); + + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/order/support/OrderStep.java b/framework/src/main/java/cn/lili/modules/order/order/support/OrderStep.java new file mode 100644 index 00000000..2b83289e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/order/support/OrderStep.java @@ -0,0 +1,82 @@ +package cn.lili.modules.order.order.support; + + +import cn.lili.modules.order.order.entity.enums.OrderOperateEnum; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; + +import java.util.ArrayList; +import java.util.List; + +/** + * 订单流程 + * + * @author Chopper + * @date 2020/11/17 7:40 下午 + */ +public class OrderStep implements Cloneable { + + + /** + * 允许的操作 + */ + private List allowableOperate; + + public OrderStep(OrderStatusEnum orderStatus, OrderOperateEnum... operates) { + /** + * 订单状态 + */ + this.allowableOperate = new ArrayList(); + for (OrderOperateEnum orderOperate : operates) { + allowableOperate.add(orderOperate); + } + } + + @Override + public Object clone() { + OrderStep orderStep = null; + try { + orderStep = (OrderStep) super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + ArrayList list = (ArrayList) allowableOperate; + orderStep.allowableOperate = (List) list.clone(); + return orderStep; + } + + public List getOperate() { + return this.allowableOperate; + } + + public static void main(String[] args) throws CloneNotSupportedException { + OrderStep step1 = new OrderStep(OrderStatusEnum.UNPAID, OrderOperateEnum.CONFIRM, OrderOperateEnum.CANCEL); + OrderStep step2 = (OrderStep) step1.clone(); + + step2.getOperate().remove(OrderOperateEnum.CONFIRM); + step2.getOperate().add(OrderOperateEnum.PAY); + + System.out.println(step1); + System.out.println(step2); + + step1.getOperate().forEach(orderOperateEnum -> { + System.out.println(orderOperateEnum); + }); + + System.out.println("--------"); + step2.getOperate().forEach(orderOperateEnum -> { + System.out.println(orderOperateEnum); + }); + + } + + + public boolean checkAllowable(OrderOperateEnum operate) { + for (OrderOperateEnum orderOperate : allowableOperate) { + if (operate.compareTo(orderOperate) == 0) { + return true; + } + } + return false; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/AfterSaleLog.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/AfterSaleLog.java new file mode 100644 index 00000000..a70e8abd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/AfterSaleLog.java @@ -0,0 +1,82 @@ +package cn.lili.modules.order.trade.entity.dos; + +import cn.lili.common.security.enums.UserEnums; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 售后日志 + * + * @author Bulbasaur + * @date 2020-03-25 2:30 下午 + */ +@Data +@Entity +@Table(name = "li_after_sale_log") +@TableName("li_after_sale_log") +@ApiModel(value = "售后日志") +@NoArgsConstructor +public class AfterSaleLog { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "售后服务单号") + private String sn; + + @ApiModelProperty(value = "操作者id(可以是卖家)") + private String operatorId; + + /** + * @see UserEnums + */ + @ApiModelProperty(value = "操作者类型") + private String operatorType; + + + @ApiModelProperty(value = "操作者名称") + private String operatorName; + + @ApiModelProperty(value = "日志信息") + private String message; + + public AfterSaleLog(String sn, String operatorId, String operatorType, String operatorName, String message) { + this.sn = sn; + this.operatorId = operatorId; + this.operatorType = operatorType; + this.operatorName = operatorName; + this.message = message; + } +} diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/OrderLog.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/OrderLog.java new file mode 100644 index 00000000..39c8a263 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/OrderLog.java @@ -0,0 +1,91 @@ +package cn.lili.modules.order.trade.entity.dos; + +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.StringUtils; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 订单日志 + * + * @author Chopper + * @date 2020-03-25 2:30 下午 + */ +@Data +@Entity +@Table(name = "li_order_log") +@TableName("li_order_log") +@ApiModel(value = "订单日志") +@NoArgsConstructor +public class OrderLog { + + private static final long serialVersionUID = -1599270944927160096L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "订单编号") + private String orderSn; + + @ApiModelProperty(value = "操作者id(可以是卖家)") + private String operatorId; + /** + * @see UserEnums + */ + @ApiModelProperty(value = "操作者类型") + private String operatorType; + + + @ApiModelProperty(value = "操作者名称") + private String operatorName; + + @ApiModelProperty(value = "日志信息") + private String message; + + public OrderLog(String orderSn, String operatorId, String operatorType, String operatorName, String message) { + this.orderSn = orderSn; + this.operatorId = operatorId; + this.operatorType = operatorType; + this.operatorName = operatorName; + this.message = message; + } + + public String getCreateBy() { + if (StringUtils.isEmpty(createBy)) { + return "系统"; + } + return createBy; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/Recharge.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/Recharge.java new file mode 100644 index 00000000..8e039c55 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/Recharge.java @@ -0,0 +1,110 @@ +package cn.lili.modules.order.trade.entity.dos; + +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; +import java.util.Date; + +/** + * 预存款充值记录 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_recharge") +@TableName("li_recharge") +@ApiModel(value = "预存款充值记录") +@AllArgsConstructor +@NoArgsConstructor +public class Recharge { + + private static final long serialVersionUID = -1529240544327161096L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "充值订单编号") + private String rechargeSn; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @NotEmpty(message = "充值金额不能为空") + @ApiModelProperty(value = "充值金额") + private Double rechargeMoney; + + @NotEmpty(message = "充值方式,如:支付宝,微信不能为空") + @ApiModelProperty(value = "充值方式,如:支付宝,微信") + private String rechargeWay; + + @ApiModelProperty(value = "支付状态") + private String payStatus; + + @ApiModelProperty(value = "支付插件id") + private String paymentPluginId; + + @ApiModelProperty(value = "第三方流水") + private String receivableNo; + + @ApiModelProperty(value = "支付时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date payTime; + + + /** + * 构建充值账单信息 + * + * @param rechargeSn 充值订单号 + * @param memberId 会员id + * @param memberName 会员名称 + * @param money 充值金额 + */ + public Recharge(String rechargeSn, String memberId, String memberName, Double money) { + this.rechargeSn = rechargeSn; + this.memberId = memberId; + this.memberName = memberName; + this.rechargeMoney = money; + this.payStatus = PayStatusEnum.UNPAID.name(); + + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/WalletLog.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/WalletLog.java new file mode 100644 index 00000000..8508af4e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/dos/WalletLog.java @@ -0,0 +1,105 @@ +package cn.lili.modules.order.trade.entity.dos; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 预存款日志实体 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_wallet_log") +@TableName("li_wallet_log") +@ApiModel(value = "钱包变动日志") +@NoArgsConstructor +public class WalletLog { + + private static final long serialVersionUID = -1599270544927161096L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + /** + * 会员id + */ + @ApiModelProperty(value = "会员id") + private String memberId; + + /** + * 会员名称 + */ + @ApiModelProperty(value = "会员名称") + private String memberName; + /** + * 金额 + */ + @ApiModelProperty(value = "金额") + private Double money; + + /** + * 变动业务类型 + * + * @see cn.lili.modules.order.trade.entity.enums.DepositServiceTypeEnum + */ + @ApiModelProperty(value = "业务类型") + private String serviceType; + + /** + * 日志明细 + */ + @ApiModelProperty(value = "日志明细") + private String detail; + + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + /** + * 构建新的预存款日志对象 + * + * @param memberId 会员id + * @param memberName 会员名称 + * @param money 金额 + * @param detail 备注 + */ + public WalletLog(String memberId, String memberName, Double money, String detail, String serviceType) { + this.setMemberId(memberId); + this.setMemberName(memberName); + this.setMoney(money); + this.setDetail(detail); + this.setServiceType(serviceType); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/AfterSaleRefundWayEnum.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/AfterSaleRefundWayEnum.java new file mode 100644 index 00000000..58a7ab2e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/AfterSaleRefundWayEnum.java @@ -0,0 +1,25 @@ +package cn.lili.modules.order.trade.entity.enums; + +/** + * 退款方式 + * + * @author paulG + * @since 2020/12/4 + **/ +public enum AfterSaleRefundWayEnum { + + + ORIGINAL("原路退回"), + OFFLINE("线下支付"); + + private final String description; + + AfterSaleRefundWayEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/AfterSaleStatusEnum.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/AfterSaleStatusEnum.java new file mode 100644 index 00000000..f0ff1d64 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/AfterSaleStatusEnum.java @@ -0,0 +1,35 @@ +package cn.lili.modules.order.trade.entity.enums; + +/** + * 售后状态 + * + * @author Chopper + * @date 2020-08-20 15:39 + */ +public enum AfterSaleStatusEnum { + + /** + * 售后服务类型枚举 + */ + APPLY("申请中"), + PASS("已通过"), + REFUSE("已拒绝"), + BUYER_RETURN("买家退货,待卖家收货"), + SELLER_CONFIRM("卖家确认收货"), + SELLER_TERMINATION("卖家终止售后"), + BUYER_CANCEL("买家取消售后"), + WAIT_REFUND("等待平台退款"), + COMPLETE("完成"); + + private final String description; + + AfterSaleStatusEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/AfterSaleTypeEnum.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/AfterSaleTypeEnum.java new file mode 100644 index 00000000..6b0d26a4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/AfterSaleTypeEnum.java @@ -0,0 +1,26 @@ +package cn.lili.modules.order.trade.entity.enums; + +/** + * 售后类型 + * + * @author Chopper + * @date 2020/8/20 15:39 + */ + +public enum AfterSaleTypeEnum { + /** + * 售后服务类型枚举 + */ + RETURN_MONEY("退款"), RETURN_GOODS("退货"); + + private final String description; + + AfterSaleTypeEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/DepositServiceTypeEnum.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/DepositServiceTypeEnum.java new file mode 100644 index 00000000..51c8eaa7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/enums/DepositServiceTypeEnum.java @@ -0,0 +1,30 @@ +package cn.lili.modules.order.trade.entity.enums; + +/** + * 预存款变动日志业务类型 + * + * @author Chopper + * @date 2020/8/20 15:39 + */ + +public enum DepositServiceTypeEnum { + /** + * 预存款变动日志业务类型枚举 + */ + WALLET_WITHDRAWAL("余额提现"), + WALLET_PAY("余额支付"), + WALLET_REFUND("余额退款"), + WALLET_RECHARGE("余额充值"), + WALLET_COMMISSION("佣金提成"); + + private final String description; + + DepositServiceTypeEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/vo/DepositQueryVO.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/vo/DepositQueryVO.java new file mode 100644 index 00000000..dfd438a8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/vo/DepositQueryVO.java @@ -0,0 +1,44 @@ +package cn.lili.modules.order.trade.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 预存款充值记录查询条件 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +@ApiModel(value = "预存款变动记录查询条件") +@AllArgsConstructor +@NoArgsConstructor +public class DepositQueryVO implements Serializable { + + + private static final long serialVersionUID = -6413611244037073693L; + + /** + * 会员ID + */ + @ApiModelProperty(value = "会员Id") + private String memberId; + /** + * 会员名称 + */ + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "起始日期") + private String startDate; + + @ApiModelProperty(value = "结束日期") + private String endDate; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/entity/vo/RechargeQueryVO.java b/framework/src/main/java/cn/lili/modules/order/trade/entity/vo/RechargeQueryVO.java new file mode 100644 index 00000000..7bd5d894 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/entity/vo/RechargeQueryVO.java @@ -0,0 +1,55 @@ +package cn.lili.modules.order.trade.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 预存款充值记录查询条件 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Data +@ApiModel(value = "预存款充值记录查询条件") +@AllArgsConstructor +@NoArgsConstructor +public class RechargeQueryVO implements Serializable { + + + private static final long serialVersionUID = 318396158590640917L; + + /** + * 充值订单编号 + */ + @ApiModelProperty(value = "充值订单编号") + private String rechargeSn; + + /** + * 会员ID + */ + @ApiModelProperty(value = "会员Id") + private String memberId; + /** + * 会员名称 + */ + @ApiModelProperty(value = "会员名称") + private String memberName; + /** + * 充值时间 + */ + @ApiModelProperty(value = "充值开始时间") + private String startDate; + + /** + * 充值时间 + */ + @ApiModelProperty(value = "充值结束时间") + private String endDate; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/mapper/AfterSaleLogMapper.java b/framework/src/main/java/cn/lili/modules/order/trade/mapper/AfterSaleLogMapper.java new file mode 100644 index 00000000..8315d63d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/mapper/AfterSaleLogMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.order.trade.mapper; + +import cn.lili.modules.order.trade.entity.dos.AfterSaleLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 售后日志数据处理层 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +public interface AfterSaleLogMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/mapper/OrderLogMapper.java b/framework/src/main/java/cn/lili/modules/order/trade/mapper/OrderLogMapper.java new file mode 100644 index 00000000..91cfb106 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/mapper/OrderLogMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.order.trade.mapper; + +import cn.lili.modules.order.trade.entity.dos.OrderLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 订单日志数据处理层 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +public interface OrderLogMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/mapper/RechargeMapper.java b/framework/src/main/java/cn/lili/modules/order/trade/mapper/RechargeMapper.java new file mode 100644 index 00000000..b3af0ac9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/mapper/RechargeMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.order.trade.mapper; + +import cn.lili.modules.order.trade.entity.dos.Recharge; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 预存款充值记录数据处理层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface RechargeMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/mapper/WalletLogMapper.java b/framework/src/main/java/cn/lili/modules/order/trade/mapper/WalletLogMapper.java new file mode 100644 index 00000000..428698d8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/mapper/WalletLogMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.order.trade.mapper; + +import cn.lili.modules.order.trade.entity.dos.WalletLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 预存款日志数据处理层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface WalletLogMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/service/OrderLogService.java b/framework/src/main/java/cn/lili/modules/order/trade/service/OrderLogService.java new file mode 100644 index 00000000..e2be3e7d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/service/OrderLogService.java @@ -0,0 +1,22 @@ +package cn.lili.modules.order.trade.service; + +import cn.lili.modules.order.trade.entity.dos.OrderLog; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 订单日志业务层 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +public interface OrderLogService extends IService { + + /** + * 根据订单编号获取订单日志列表 + * @param orderSn 订单编号 + * @return 订单日志列表 + */ + List getOrderLog(String orderSn); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/service/RechargeService.java b/framework/src/main/java/cn/lili/modules/order/trade/service/RechargeService.java new file mode 100644 index 00000000..c8919ded --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/service/RechargeService.java @@ -0,0 +1,51 @@ +package cn.lili.modules.order.trade.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.trade.entity.dos.Recharge; +import cn.lili.modules.order.trade.entity.vo.RechargeQueryVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 预存款充值记录业务层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface RechargeService extends IService { + + /** + * 创建充值订单 + * + * @param price 价格 + * @return 预存款充值记录 + */ + Recharge recharge(Double price); + + /** + * 查询充值订单列表 + * + * @param page 分页数据 + * @param rechargeQueryVO 查询条件 + * @return + */ + IPage rechargePage(PageVO page, RechargeQueryVO rechargeQueryVO); + + + /** + * 支付成功 + * + * @param sn 充值订单编号 + * @param receivableNo 流水no + */ + void paySuccess(String sn, String receivableNo); + + /** + * 根据充值订单号查询充值信息 + * + * @param sn 充值订单号 + * @return + */ + Recharge getRecharge(String sn); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/service/WalletLogService.java b/framework/src/main/java/cn/lili/modules/order/trade/service/WalletLogService.java new file mode 100644 index 00000000..a83baece --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/service/WalletLogService.java @@ -0,0 +1,27 @@ +package cn.lili.modules.order.trade.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.trade.entity.dos.WalletLog; +import cn.lili.modules.order.trade.entity.vo.DepositQueryVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 预存款日志业务层 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +public interface WalletLogService extends IService { + + + /** + * 预存款充值日志记录 + * + * @param page 分页数据 + * @param depositQueryVO 查询条件 + * @return 日志记录分页列表 + */ + IPage depositLogPage(PageVO page, DepositQueryVO depositQueryVO); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/serviceimpl/OrderLogServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/trade/serviceimpl/OrderLogServiceImpl.java new file mode 100644 index 00000000..a8689098 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/serviceimpl/OrderLogServiceImpl.java @@ -0,0 +1,33 @@ +package cn.lili.modules.order.trade.serviceimpl; + +import cn.lili.modules.order.trade.entity.dos.OrderLog; +import cn.lili.modules.order.trade.mapper.OrderLogMapper; +import cn.lili.modules.order.trade.service.OrderLogService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 订单日志业务层实现 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderLogServiceImpl extends ServiceImpl implements OrderLogService { + + @Override + public List getOrderLog(String orderSn) { + LambdaQueryWrapper lambdaQueryWrapper= Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(OrderLog::getOrderSn,orderSn); + return this.list(lambdaQueryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/serviceimpl/RechargeServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/trade/serviceimpl/RechargeServiceImpl.java new file mode 100644 index 00000000..62fb18f0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/serviceimpl/RechargeServiceImpl.java @@ -0,0 +1,109 @@ +package cn.lili.modules.order.trade.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.SnowFlake; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.service.MemberWalletService; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.trade.entity.dos.Recharge; +import cn.lili.modules.order.trade.entity.enums.DepositServiceTypeEnum; +import cn.lili.modules.order.trade.entity.vo.RechargeQueryVO; +import cn.lili.modules.order.trade.mapper.RechargeMapper; +import cn.lili.modules.order.trade.service.RechargeService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +/** + * 预存款日志业务层实现 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RechargeServiceImpl extends ServiceImpl implements RechargeService { + + //预存款 + private final RechargeMapper rechargeMapper; + //会员预存款 + private MemberWalletService memberWalletService; + + @Override + public Recharge recharge(Double price) { + //获取当前登录的会员 + AuthUser authUser = UserContext.getCurrentUser(); + //构建sn + String sn = "Y" + SnowFlake.getId(); + //整合充值订单数据 + Recharge recharge = new Recharge(sn, authUser.getId(), authUser.getUsername(), price); + //添加预存款充值账单 + this.rechargeMapper.insert(recharge); + //返回预存款 + return recharge; + } + + @Override + public IPage rechargePage(PageVO page, RechargeQueryVO rechargeQueryVO) { + //构建查询条件 + QueryWrapper queryWrapper = new QueryWrapper<>(); + //会员名称 + queryWrapper.like(!StringUtils.isEmpty(rechargeQueryVO.getMemberName()), "member_name", rechargeQueryVO.getMemberName()); + //充值订单号 + queryWrapper.eq(!StringUtils.isEmpty(rechargeQueryVO.getRechargeSn()), "recharge_sn", rechargeQueryVO.getRechargeSn()); + //会员id + queryWrapper.eq(!StringUtils.isEmpty(rechargeQueryVO.getMemberId()), "member_id", rechargeQueryVO.getMemberId()); + //已付款的充值订单 + queryWrapper.eq("pay_status", PayStatusEnum.PAID.name()); + //支付时间 开始时间和结束时间 + if (!StringUtils.isEmpty(rechargeQueryVO.getStartDate()) && !StringUtils.isEmpty(rechargeQueryVO.getEndDate())) { + Date start = cn.hutool.core.date.DateUtil.parse(rechargeQueryVO.getStartDate()); + Date end = cn.hutool.core.date.DateUtil.parse(rechargeQueryVO.getEndDate()); + queryWrapper.between("pay_time", start, end); + } + //查询返回数据 + return this.rechargeMapper.selectPage(PageUtil.initPage(page), queryWrapper); + } + + @Override + public void paySuccess(String sn, String receivableNo) { + //根据sn获取支付账单 + Recharge recharge = this.rechargeMapper.selectOne(new QueryWrapper().eq("recharge_sn", sn)); + //如果支付账单不为空则进行一下逻辑 + if (recharge != null) { + //将此账单支付状态更改为已支付 + recharge.setPayStatus(PayStatusEnum.PAID.name()); + recharge.setReceivableNo(receivableNo); + //执行保存操作 + this.rechargeMapper.updateById(recharge); + //增加预存款余额 + memberWalletService.increase(recharge.getRechargeMoney(), recharge.getMemberId(), "会员余额充值,充值单号为:" + recharge.getRechargeSn(), DepositServiceTypeEnum.WALLET_RECHARGE.name()); + } + } + + @Override + public Recharge getRecharge(String sn) { + Recharge recharge = this.rechargeMapper.selectOne(new QueryWrapper().eq("recharge_sn", sn)); + if (recharge != null) { + return recharge; + } + throw new ServiceException(ResultCode.ORDER_NOT_EXIST); + } + + @Autowired + public void setMemberWalletService(MemberWalletService memberWalletService) { + this.memberWalletService = memberWalletService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/order/trade/serviceimpl/WalletLogServiceImpl.java b/framework/src/main/java/cn/lili/modules/order/trade/serviceimpl/WalletLogServiceImpl.java new file mode 100644 index 00000000..991116ea --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/order/trade/serviceimpl/WalletLogServiceImpl.java @@ -0,0 +1,50 @@ +package cn.lili.modules.order.trade.serviceimpl; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.trade.entity.dos.WalletLog; +import cn.lili.modules.order.trade.entity.vo.DepositQueryVO; +import cn.lili.modules.order.trade.mapper.WalletLogMapper; +import cn.lili.modules.order.trade.service.WalletLogService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +/** + * 预存款日志业务层实现 + * + * @author pikachu + * @date 2020-02-25 14:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WalletLogServiceImpl extends ServiceImpl implements WalletLogService { + + private final WalletLogMapper walletLogMapper; + + @Override + public IPage depositLogPage(PageVO page, DepositQueryVO depositQueryVO) { + //构建查询条件 + QueryWrapper depositLogQueryWrapper = new QueryWrapper<>(); + //会员名称 + depositLogQueryWrapper.like(!StringUtils.isEmpty(depositQueryVO.getMemberName()), "member_name", depositQueryVO.getMemberName()); + //会员id + depositLogQueryWrapper.eq(!StringUtils.isEmpty(depositQueryVO.getMemberId()), "member_id", depositQueryVO.getMemberId()); + //开始时间和技术时间 + if (!StringUtils.isEmpty(depositQueryVO.getStartDate()) && !StringUtils.isEmpty(depositQueryVO.getEndDate())) { + Date start = cn.hutool.core.date.DateUtil.parse(depositQueryVO.getStartDate()); + Date end = cn.hutool.core.date.DateUtil.parse(depositQueryVO.getEndDate()); + depositLogQueryWrapper.between("create_time", start, end); + } + //查询返回数据 + return this.walletLogMapper.selectPage(PageUtil.initPage(page), depositLogQueryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/entity/dos/Article.java b/framework/src/main/java/cn/lili/modules/page/entity/dos/Article.java new file mode 100644 index 00000000..95bd10f0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/dos/Article.java @@ -0,0 +1,58 @@ +package cn.lili.modules.page.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.modules.page.entity.enums.ArticleEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 文章DO + * + * @author Bulbasaur + * @date 2020/12/10 17:42 + */ +@Data +@Entity +@Table(name = "li_article") +@TableName("li_article") +@ApiModel(value = "文章") +public class Article extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "文章标题") + @NotEmpty(message = "文章标题不能为空") + @Length(max = 30, message = "文章标题不能超过30个字符") + private String title; + + @ApiModelProperty(value = "分类id") + @NotNull(message = "文章分类不能为空") + private String categoryId; + + @ApiModelProperty(value = "文章排序") + private Integer sort; + + @ApiModelProperty(value = "文章内容") + @NotEmpty(message = "文章内容不能为空") + private String content; + + /** + * @see SwitchEnum + */ + @ApiModelProperty(value = "状态", allowableValues = "OPEN,CLOSE") + private String openStatus; + /** + * @see ArticleEnum + */ + @ApiModelProperty(value = "类型") + private String type; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/entity/dos/ArticleCategory.java b/framework/src/main/java/cn/lili/modules/page/entity/dos/ArticleCategory.java new file mode 100644 index 00000000..1ef9e680 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/dos/ArticleCategory.java @@ -0,0 +1,54 @@ +package cn.lili.modules.page.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.page.entity.enums.ArticleCategoryEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; + +/** + * 文章分类 + * + * @author pikachu + * @author Bulbasaur + * @date 2020/12/10 17:42 + */ +@Data +@Entity +@Table(name = "li_article_category") +@TableName("li_article_category") +@ApiModel(value = "文章分类") +@NoArgsConstructor +@AllArgsConstructor +public class ArticleCategory extends BaseEntity { + + private static final long serialVersionUID = 1L; + + + @ApiModelProperty(value = "分类名称") + @NotEmpty(message = "分类名称不能为空") + private String articleCategoryName; + + @ApiModelProperty(value = "父分类ID") + private String parentId; + + @ApiModelProperty(value = "排序,正序123") + private Integer sort; + + @ApiModelProperty(value = "层级") + private Integer level; + + /** + * @see ArticleCategoryEnum + */ + @ApiModelProperty(value = "类型") + private String type; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/entity/dos/Feedback.java b/framework/src/main/java/cn/lili/modules/page/entity/dos/Feedback.java new file mode 100644 index 00000000..432df5f2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/dos/Feedback.java @@ -0,0 +1,76 @@ +package cn.lili.modules.page.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.common.utils.SnowFlake; +import cn.lili.modules.page.entity.enums.FeedbackTypeEnum; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; +import java.io.Serializable; +import java.util.Date; + +/** + * 意见反馈 + * + * @author Bulbasaur + * @date 2020/12/10 17:42 + */ +@Data +@Entity +@Table(name = "li_feedback") +@TableName("li_feedback") +@ApiModel(value = "意见反馈") +public class Feedback implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "会员名称", hidden = true) + private String userName; + + @ApiModelProperty(value = "反馈内容") + @NotEmpty(message = "反馈内容不能为空") + @Length(max = 500, message = "反馈内容不能超过500个字符") + private String context; + + @ApiModelProperty(value = "手机号") + private String mobile; + + @ApiModelProperty(value = "图片,多个图片使用:(,)分割") + private String images; + + /** + * 类型 + * + * @see FeedbackTypeEnum + */ + @ApiModelProperty(value = "类型", allowableValues = "FUNCTION,OPTIMIZE,OTHER") + private String type; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/entity/dos/PageData.java b/framework/src/main/java/cn/lili/modules/page/entity/dos/PageData.java new file mode 100644 index 00000000..9261b7f5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/dos/PageData.java @@ -0,0 +1,69 @@ +package cn.lili.modules.page.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.modules.page.entity.enums.PageEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 页面数据DO + * + * @author Bulbasaur + * @date 2020/12/10 17:42 + */ +@Data +@Entity +@Table(name = "li_page_data") +@TableName("li_page_data") +@ApiModel(value = "页面数据DO") +@NoArgsConstructor +public class PageData extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "页面名称") + + private String name; + + @ApiModelProperty(value = "页面数据") + private String pageData; + + /** + * @see SwitchEnum + */ + @ApiModelProperty(value = "页面开关状态", allowableValues = "OPEN,CLOSE") + private String pageShow; + + /** + * @see PageEnum + */ + @ApiModelProperty(value = "页面类型", allowableValues = "INDEX,STORE,SPECIAL") + private String pageType; + + /** + * @see cn.lili.modules.base.entity.enums.ClientTypeEnum + */ + @ApiModelProperty(value = "客户端类型", allowableValues = "PC,H5,WECHAT_MP,APP") + private String pageClientType; + + @ApiModelProperty(value = "值") + private String num; + + //店铺首页 + public PageData(String name, String pageClientType, String pageData) { + this.pageClientType = pageClientType; + this.pageData = pageData; + this.num = UserContext.getCurrentUser().getStoreId(); + this.pageShow = SwitchEnum.CLOSE.name(); + this.pageType = PageEnum.STORE.name(); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/entity/dos/Special.java b/framework/src/main/java/cn/lili/modules/page/entity/dos/Special.java new file mode 100644 index 00000000..62a8531e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/dos/Special.java @@ -0,0 +1,36 @@ +package cn.lili.modules.page.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 专题活动 + * + * @author Bulbasaur + * @date 2020/12/10 17:42 + */ +@Data +@Entity +@Table(name = "li_special") +@TableName("li_special") +@ApiModel(value = "专题活动") +public class Special extends BaseEntity { + + @ApiModelProperty(value = "专题活动名称") + private String specialName; + + /** + * @see cn.lili.modules.base.entity.enums.ClientTypeEnum + */ + @ApiModelProperty(value = "楼层对应连接端类型", allowableValues = "PC,H5,WECHAT_MP,APP") + private String clientType; + + @ApiModelProperty(value = "页面ID") + private String pageDataId; +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/dto/ArticleSearchParams.java b/framework/src/main/java/cn/lili/modules/page/entity/dto/ArticleSearchParams.java new file mode 100644 index 00000000..b62f1bb4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/dto/ArticleSearchParams.java @@ -0,0 +1,34 @@ +package cn.lili.modules.page.entity.dto; + +import cn.lili.common.vo.PageVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 商品查询条件 + * + * @author pikachu + * @date 2020-02-24 19:27:20 + */ +@Data +public class ArticleSearchParams extends PageVO { + + @ApiModelProperty(value = "分类ID") + private String categoryId; + + @ApiModelProperty(value = "标题") + private String title; + + @ApiModelProperty(value = "分类类型") + private String type; + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq(StringUtils.isNotBlank(categoryId), "category_id", categoryId); + queryWrapper.like(StringUtils.isNotBlank(title), "title", title); + //queryWrapper.orderByAsc("a.sort"); + return queryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/dto/PageDataDTO.java b/framework/src/main/java/cn/lili/modules/page/entity/dto/PageDataDTO.java new file mode 100644 index 00000000..98af786b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/dto/PageDataDTO.java @@ -0,0 +1,36 @@ +package cn.lili.modules.page.entity.dto; + +import cn.lili.modules.page.entity.enums.PageEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 楼层装修数据DTO + * + * @author Bulbasaur + * @date 2020/12/10 17:44 + */ +@Data +@NoArgsConstructor +public class PageDataDTO { + + @ApiModelProperty(value = "值") + private String num; + + /** + * @see PageEnum + */ + @ApiModelProperty(value = "页面类型", allowableValues = "INDEX,STORE,SPECIAL") + private String pageType; + + /** + * @see cn.lili.modules.base.entity.enums.ClientTypeEnum + */ + @ApiModelProperty(value = "客户端类型", allowableValues = "PC,H5,WECHAT_MP,APP") + private String pageClientType; + + public PageDataDTO(String pageType) { + this.pageType = pageType; + } +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/enums/ArticleCategoryEnum.java b/framework/src/main/java/cn/lili/modules/page/entity/enums/ArticleCategoryEnum.java new file mode 100644 index 00000000..b914f792 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/enums/ArticleCategoryEnum.java @@ -0,0 +1,32 @@ +package cn.lili.modules.page.entity.enums; + +/** + * 文章分类枚举 + * + * @author Bulbasaur + * @date 2020/12/7 10:50 + */ +public enum ArticleCategoryEnum { + + /** + * 店铺公告 + */ + STORE_ARTICLE, + /** + * 平台公告 + */ + ANNOUNCEMENT, + /** + * 平台信息 + */ + PLATFORM_INFORMATION, + /** + * 其他文章分类 + */ + OTHER; + + public String value() { + return this.name(); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/enums/ArticleEnum.java b/framework/src/main/java/cn/lili/modules/page/entity/enums/ArticleEnum.java new file mode 100644 index 00000000..6a92d460 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/enums/ArticleEnum.java @@ -0,0 +1,36 @@ +package cn.lili.modules.page.entity.enums; + +/** + * 文章分类枚举 + * + * @author Bulbasaur + * @date 2020/12/7 10:50 + */ +public enum ArticleEnum { + + /** + * 关于我们 + */ + ABOUT, + /** + * 隐私政策 + */ + PRIVACY_POLICY, + /** + * 用户协议 + */ + USER_AGREEMENT, + /** + * 证照信息 + */ + LICENSE_INFORMATION, + /** + * 其他文章 + */ + OTHER; + + public String value() { + return this.name(); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/enums/FeedbackTypeEnum.java b/framework/src/main/java/cn/lili/modules/page/entity/enums/FeedbackTypeEnum.java new file mode 100644 index 00000000..cca95dfb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/enums/FeedbackTypeEnum.java @@ -0,0 +1,30 @@ +package cn.lili.modules.page.entity.enums; + +/** + * 功能反馈枚举 + * + * @author Bulbasaur + * @date 2020/12/7 10:50 + */ +public enum FeedbackTypeEnum { + + /** + * 功能建议 + */ + FUNCTION, + + /** + * 优化反馈 + */ + OPTIMIZE , + + /** + * 其他意见 + */ + OTHER; + + public String value() { + return this.name(); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/enums/PageEnum.java b/framework/src/main/java/cn/lili/modules/page/entity/enums/PageEnum.java new file mode 100644 index 00000000..0c5b2948 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/enums/PageEnum.java @@ -0,0 +1,30 @@ +package cn.lili.modules.page.entity.enums; + +/** + * 楼层装修枚举 + * + * @author Bulbasaur + * @date 2020/12/7 10:50 + */ +public enum PageEnum { + + /** + * 首页 + */ + INDEX, + + /** + * 店铺 + */ + STORE, + + /** + * 专题页面 + */ + SPECIAL; + + public String value() { + return this.name(); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/vos/ArticleCategoryVO.java b/framework/src/main/java/cn/lili/modules/page/entity/vos/ArticleCategoryVO.java new file mode 100644 index 00000000..9ca34f72 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/vos/ArticleCategoryVO.java @@ -0,0 +1,44 @@ +package cn.lili.modules.page.entity.vos; + +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.page.entity.dos.ArticleCategory; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * 文章分类VO + * + * @author Chopper + * @date 2021-03-26 11:32 + */ +@Data +public class ArticleCategoryVO extends ArticleCategory { + + @ApiModelProperty(value = "子菜单") + private List children = new ArrayList<>(); + + public ArticleCategoryVO() { + + } + + public ArticleCategoryVO(ArticleCategory articleCategory) { + BeanUtil.copyProperties(articleCategory, this); + } + + public List getChildren() { + if (children != null) { + children.sort(new Comparator() { + @Override + public int compare(ArticleCategoryVO o1, ArticleCategoryVO o2) { + return o1.getSort().compareTo(o2.getSort()); + } + }); + return children; + } + return null; + } +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/vos/ArticleVO.java b/framework/src/main/java/cn/lili/modules/page/entity/vos/ArticleVO.java new file mode 100644 index 00000000..74ff538f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/vos/ArticleVO.java @@ -0,0 +1,29 @@ +package cn.lili.modules.page.entity.vos; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 文章VO + * + * @author Chopper + * @date 2021-03-26 11:32 + */ +@Data +public class ArticleVO { + + @ApiModelProperty(value = "文章ID") + private String id; + + @ApiModelProperty(value = "文章标题") + private String title; + + @ApiModelProperty(value = "分类名称") + private String articleCategoryName; + + @ApiModelProperty(value = "文章排序") + private Integer sort; + + @ApiModelProperty(value = "状态, allowableValues = OPEN,CLOSE") + private String openStatus; +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/vos/PageDataListVO.java b/framework/src/main/java/cn/lili/modules/page/entity/vos/PageDataListVO.java new file mode 100644 index 00000000..1eb61f9c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/vos/PageDataListVO.java @@ -0,0 +1,25 @@ +package cn.lili.modules.page.entity.vos; + +import cn.lili.common.enums.SwitchEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 楼层装修数据VO + * + * @author Bulbasaur + * @date 2020/12/10 17:42 + */ +@Data +public class PageDataListVO { + + @ApiModelProperty(value = "页面ID") + private String id; + @ApiModelProperty(value = "页面名称") + private String name; + /** + * @see SwitchEnum + */ + @ApiModelProperty(value = "页面开关状态", allowableValues = "OPEN,CLOSE") + private String pageShow; +} diff --git a/framework/src/main/java/cn/lili/modules/page/entity/vos/PageDataVO.java b/framework/src/main/java/cn/lili/modules/page/entity/vos/PageDataVO.java new file mode 100644 index 00000000..8df6902c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/entity/vos/PageDataVO.java @@ -0,0 +1,17 @@ +package cn.lili.modules.page.entity.vos; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 楼层装修数据VO + * + * @author Bulbasaur + * @date 2020/12/10 17:42 + */ +@Data +public class PageDataVO { + + @ApiModelProperty(value = "页面数据") + private String pageData; +} diff --git a/framework/src/main/java/cn/lili/modules/page/mapper/ArticleCategoryMapper.java b/framework/src/main/java/cn/lili/modules/page/mapper/ArticleCategoryMapper.java new file mode 100644 index 00000000..933db36b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/mapper/ArticleCategoryMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.page.mapper; + +import cn.lili.modules.page.entity.dos.ArticleCategory; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + + +/** + * 文章分类数据处理层 + * + * @author pikachu + * @date 2020-05-5 15:10:16 + */ +public interface ArticleCategoryMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/mapper/ArticleMapper.java b/framework/src/main/java/cn/lili/modules/page/mapper/ArticleMapper.java new file mode 100644 index 00000000..366c0c80 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/mapper/ArticleMapper.java @@ -0,0 +1,24 @@ +package cn.lili.modules.page.mapper; + +import cn.lili.modules.page.entity.dos.Article; +import cn.lili.modules.page.entity.vos.ArticleVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + + +/** + * 文章数据处理层 + * + * @author pikachu + * @date 2020-05-06 15:18:56 + */ +public interface ArticleMapper extends BaseMapper
{ + + @Select("select a.id,a.title,a.sort,ac.article_category_name,a.open_status from " + + "li_article as a inner join li_article_category ac on a.category_id=ac.id ${ew.customSqlSegment}") + IPage getArticleList(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/mapper/FeedbackMapper.java b/framework/src/main/java/cn/lili/modules/page/mapper/FeedbackMapper.java new file mode 100644 index 00000000..4db2f07d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/mapper/FeedbackMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.page.mapper; + +import cn.lili.modules.page.entity.dos.Feedback; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + + +/** + * 意见反馈处理层 + * + * @author pikachu + * @date 2020-05-06 15:18:56 + */ +public interface FeedbackMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/mapper/PageDataMapper.java b/framework/src/main/java/cn/lili/modules/page/mapper/PageDataMapper.java new file mode 100644 index 00000000..548918a6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/mapper/PageDataMapper.java @@ -0,0 +1,30 @@ +package cn.lili.modules.page.mapper; + +import cn.lili.modules.page.entity.dos.PageData; +import cn.lili.modules.page.entity.vos.PageDataListVO; +import cn.lili.modules.page.entity.vos.PageDataVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 楼层装修设置数据处理层 + * + * @author paulGao + * @date 2020/12/7 11:26 + */ +public interface PageDataMapper extends BaseMapper { + + @Select("SELECT page_data FROM li_page_data ${ew.customSqlSegment}") + PageDataVO getPageData(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Select("SELECT COUNT(id) FROM li_page_data ${ew.customSqlSegment}") + Integer getPageDataNum(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Select("SELECT id,name,page_show FROM li_page_data ${ew.customSqlSegment}") + IPage getPageDataList(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/mapper/SpecialMapper.java b/framework/src/main/java/cn/lili/modules/page/mapper/SpecialMapper.java new file mode 100644 index 00000000..ddbfc783 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/mapper/SpecialMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.page.mapper; + + +import cn.lili.modules.page.entity.dos.Special; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 专题活动数据处理层 + * + * @author Bulbasaur + * @date 2020/12/7 11:26 + */ +public interface SpecialMapper extends BaseMapper { +} diff --git a/framework/src/main/java/cn/lili/modules/page/service/ArticleCategoryService.java b/framework/src/main/java/cn/lili/modules/page/service/ArticleCategoryService.java new file mode 100644 index 00000000..c31ffb32 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/service/ArticleCategoryService.java @@ -0,0 +1,47 @@ +package cn.lili.modules.page.service; + +import cn.lili.modules.page.entity.dos.ArticleCategory; +import cn.lili.modules.page.entity.vos.ArticleCategoryVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + + +/** + * 文章分类业务层 + * + * @author Bulbasaur + * @date 2020/11/24 17:07 + */ +public interface ArticleCategoryService extends IService { + + /** + * 添加文章分类 + * + * @param articleCategory 文章分类 + */ + ArticleCategory saveArticleCategory(ArticleCategory articleCategory); + + /** + * 修改文章分类 + * + * @param articleCategory 文章分类 + */ + ArticleCategory updateArticleCategory(ArticleCategory articleCategory); + + /** + * 查询所有的分类,父子关系 + * + * @return 文章分类 + */ + List allChildren(); + + /** + * 删除文章分类 + * + * @param id 文章分类id + */ + boolean deleteById(String id); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/service/ArticleService.java b/framework/src/main/java/cn/lili/modules/page/service/ArticleService.java new file mode 100644 index 00000000..b397e94c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/service/ArticleService.java @@ -0,0 +1,72 @@ +package cn.lili.modules.page.service; + +import cn.lili.modules.page.entity.dos.Article; +import cn.lili.modules.page.entity.dto.ArticleSearchParams; +import cn.lili.modules.page.entity.vos.ArticleVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; + +import java.util.List; + + +/** + * 文章业务层 + * + * @author pikachu + * @date 2020/11/18 11:40 上午 + */ +@CacheConfig(cacheNames = "{article}") +public interface ArticleService extends IService
{ + + /** + * 获取文章分页 + * + * @param articleSearchParams 文章搜索条件 + * @return 文章分页 + */ + IPage articlePage(ArticleSearchParams articleSearchParams); + + /** + * 获取文章分页 + * + * @param categoryId 文章分类ID + * @return 文章分页 + */ + List
list(String categoryId); + + /** + * 修改文章内容 + * + * @param article 文章 + * @return 修改后的文章 + */ + @CacheEvict(key = "#article.id") + Article updateArticle(Article article); + + /** + * 删除文章 + * + * @param id + */ + @CacheEvict(key = "#id") + void customRemove(String id); + + /** + * 读取文章 + * + * @param id + */ + @Cacheable(key = "#id") + Article customGet(String id); + + /** + * 读取文章 + * + * @param type + */ + @Cacheable(key = "#type") + Article customGetByType(String type); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/service/FeedbackService.java b/framework/src/main/java/cn/lili/modules/page/service/FeedbackService.java new file mode 100644 index 00000000..c2b2a51c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/service/FeedbackService.java @@ -0,0 +1,16 @@ +package cn.lili.modules.page.service; + +import cn.lili.modules.page.entity.dos.Feedback; +import com.baomidou.mybatisplus.extension.service.IService; + + +/** + * 意见反馈业务层 + * + * @author pikachu + * @date 2020/11/18 11:40 上午 + */ +public interface FeedbackService extends IService { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/service/PageDataService.java b/framework/src/main/java/cn/lili/modules/page/service/PageDataService.java new file mode 100644 index 00000000..1ddea4c7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/service/PageDataService.java @@ -0,0 +1,77 @@ +package cn.lili.modules.page.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.page.entity.dos.PageData; +import cn.lili.modules.page.entity.dto.PageDataDTO; +import cn.lili.modules.page.entity.vos.PageDataListVO; +import cn.lili.modules.page.entity.vos.PageDataVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 页面业务层 + * + * @author Bulbasaur + * @date 2020/12/10 17:23 + */ +public interface PageDataService extends IService { + + /** + * 添加店铺页面 + * 用于初次开店,生成店铺首页 + * + * @param storeId 店铺ID + * @return 页面 + */ + void addStorePageData(String storeId); + + /** + * 添加页面 + * + * @param pageData 页面 + * @return 页面 + */ + PageData addPageData(PageData pageData); + + /** + * 修改页面 + * + * @param pageData 页面 + * @return 页面 + */ + PageData updatePageData(PageData pageData); + + /** + * 发布页面 + * + * @param id 页面ID + * @return 页面 + */ + PageData releasePageData(String id); + + /** + * 删除页面 + * + * @param id 页面ID + * @return 操作状态 + */ + boolean removePageData(String id); + + /** + * 获取页面 + * 用户前台页面展示 + * + * @param pageDataDTO 页面数据DTO + * @return + */ + PageDataVO getPageData(PageDataDTO pageDataDTO); + + /** + * 页面分页 + * + * @param pageVO 分页 + * @param pageDataDTO 查询数据 + * @return + */ + IPage getPageDataList(PageVO pageVO, PageDataDTO pageDataDTO); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/service/SpecialService.java b/framework/src/main/java/cn/lili/modules/page/service/SpecialService.java new file mode 100644 index 00000000..76c8f424 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/service/SpecialService.java @@ -0,0 +1,28 @@ +package cn.lili.modules.page.service; + +import cn.lili.modules.page.entity.dos.Special; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 专题活动业务层 + * + * @author Bulbasaur + * @date 2020/12/7 11:27 + */ +public interface SpecialService extends IService { + + /** + * 添加专题活动 + * @param special 专题活动 + * @return 专题活动 + */ + Special addSpecial(Special special); + + /** + * 删除专题活动 + * @param id 活动ID + * @return 操作状态 + */ + boolean removeSpecial(String id); + +} diff --git a/framework/src/main/java/cn/lili/modules/page/serviceimpl/ArticleCategoryServiceImpl.java b/framework/src/main/java/cn/lili/modules/page/serviceimpl/ArticleCategoryServiceImpl.java new file mode 100644 index 00000000..7a94edaa --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/serviceimpl/ArticleCategoryServiceImpl.java @@ -0,0 +1,198 @@ +package cn.lili.modules.page.serviceimpl; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.page.entity.dos.Article; +import cn.lili.modules.page.entity.dos.ArticleCategory; +import cn.lili.modules.page.entity.enums.ArticleCategoryEnum; +import cn.lili.modules.page.entity.enums.ArticleEnum; +import cn.lili.modules.page.entity.vos.ArticleCategoryVO; +import cn.lili.modules.page.mapper.ArticleCategoryMapper; +import cn.lili.modules.page.service.ArticleCategoryService; +import cn.lili.modules.page.service.ArticleService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * 文章分类业务层实现 + * + * @author pikachu + * @date 2020-05-5 15:10:16 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ArticleCategoryServiceImpl extends ServiceImpl implements ArticleCategoryService { + + //缓存 + private final Cache cache; + //文章 + private ArticleService articleService; + + @Override + public ArticleCategory saveArticleCategory(ArticleCategory articleCategory) { + //不能添加重复的分类名称 + List list = this.list( + new LambdaQueryWrapper().eq(ArticleCategory::getArticleCategoryName, articleCategory.getArticleCategoryName())); + if (StringUtils.isNotEmpty(list)) { + throw new ServiceException(ResultCode.SUCCESS.ARTICLE_CATEGORY_NAME_EXIST); + } + // 非顶级分类 + if (articleCategory.getParentId() != null && !articleCategory.getParentId().equals("0")) { + ArticleCategory parent = this.getById(articleCategory.getParentId()); + if (parent == null) { + throw new ServiceException(ResultCode.ARTICLE_CATEGORY_PARENT_NOT_EXIST); + } + if (articleCategory.getLevel() >= 2) { + throw new ServiceException(ResultCode.ARTICLE_CATEGORY_BEYOND_TWO); + } + } + articleCategory.setType(ArticleCategoryEnum.OTHER.name()); + if (this.save(articleCategory)) { + //清除文章分类缓存 + this.clearCache(); + return articleCategory; + } + throw new ServiceException(ResultCode.ERROR); + } + + + @Override + public ArticleCategory updateArticleCategory(ArticleCategory articleCategory) { + // 非顶级分类校验是否存在 + if (!articleCategory.getParentId().equals("0")) { + ArticleCategory parent = this.getById(articleCategory.getParentId()); + if (parent == null) { + throw new ServiceException(ResultCode.ARTICLE_CATEGORY_PARENT_NOT_EXIST); + } + // 替换catPath 根据path规则来匹配级别 + if (articleCategory.getLevel() >= 2) { + throw new ServiceException(ResultCode.ARTICLE_CATEGORY_BEYOND_TWO); + } + } + //验证分类名称是否重复 + ArticleCategory category = this.getOne( + new LambdaQueryWrapper().eq(ArticleCategory::getArticleCategoryName, articleCategory.getArticleCategoryName())); + if (category != null && !category.getId().equals(articleCategory.getId())) { + throw new ServiceException(ResultCode.ARTICLE_CATEGORY_NAME_EXIST); + } + if (this.updateById(articleCategory)) { + //清除文章分类 + this.clearCache(); + return category; + } + throw new ServiceException(ResultCode.ERROR); + } + + @Override + public boolean deleteById(String id) { + + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(ArticleCategory::getParentId, id); + + //查看文章分类下是否有分类 + if (this.count(lambdaQueryWrapper) > 0) { + throw new ServiceException(ResultCode.ARTICLE_CATEGORY_DELETE_ERROR); + } + + //查看文章分类下是否有文章 + LambdaQueryWrapper
articleLambdaQueryWrapper = new LambdaQueryWrapper<>(); + articleLambdaQueryWrapper.eq(Article::getCategoryId, id); + if (articleService.count(articleLambdaQueryWrapper) > 0) { + throw new ServiceException(ResultCode.ARTICLE_CATEGORY_HAS_ARTICLE); + } + //判断是否为默认的分类 + if(!this.getById(id).getType().equals(ArticleEnum.OTHER.name())){ + throw new ServiceException(ResultCode.ARTICLE_CATEGORY_NO_DELETION); + } + + //清除文章分类缓存 + this.clearCache(); + //删除文章分类 + return this.removeById(id); + } + + @Override + public List allChildren() { + // 从缓存取所有的分类 + Object all = cache.get(CachePrefix.ARTICLE_CATEGORY.getPrefix()); + List articleCategories; + if (all == null) { + // 调用初始化分类缓存方法 + articleCategories = initCategory(); + } else { + articleCategories = (List) all; + } + return articleCategories; + } + + /** + * 初始化所有文章分类 + * + * @return 文章分类集合 + */ + private List initCategory() { + List articleCategories = this.list(); + List tree = new ArrayList<>(); + articleCategories.forEach(item -> { + if (item.getLevel() == 0) { + ArticleCategoryVO articleCategoryVO = new ArticleCategoryVO(item); + initChild(articleCategoryVO, articleCategories); + tree.add(articleCategoryVO); + } + }); + //对一级菜单排序 + tree.sort(new Comparator() { + @Override + public int compare(ArticleCategoryVO o1, ArticleCategoryVO o2) { + return o1.getSort().compareTo(o2.getSort()); + } + }); + cache.put(CachePrefix.ARTICLE_CATEGORY.getPrefix(), tree); + + return tree; + } + + /** + * 递归初始化子树 + * + * @param tree 树结构 + * @param articleCategories 数据库对象集合 + */ + private void initChild(ArticleCategoryVO tree, List articleCategories) { + if (articleCategories == null) { + return; + } + articleCategories.stream() + .filter(item -> (item.getParentId().equals(tree.getId()))) + .forEach(child -> { + ArticleCategoryVO childTree = new ArticleCategoryVO(child); + initChild(childTree, articleCategories); + tree.getChildren().add(childTree); + }); + } + + /** + * 清除缓存中的文章分类 + */ + private void clearCache() { + cache.remove(CachePrefix.ARTICLE_CATEGORY.getPrefix()); + } + + + @Autowired + public void setArticleService(ArticleService articleService) { + this.articleService = articleService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/serviceimpl/ArticleServiceImpl.java b/framework/src/main/java/cn/lili/modules/page/serviceimpl/ArticleServiceImpl.java new file mode 100644 index 00000000..7fb7b283 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/serviceimpl/ArticleServiceImpl.java @@ -0,0 +1,86 @@ +package cn.lili.modules.page.serviceimpl; + + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.modules.page.entity.dos.Article; +import cn.lili.modules.page.entity.dto.ArticleSearchParams; +import cn.lili.modules.page.entity.enums.ArticleEnum; +import cn.lili.modules.page.entity.vos.ArticleVO; +import cn.lili.modules.page.mapper.ArticleMapper; +import cn.lili.modules.page.service.ArticleService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 文章业务层实现 + * + * @author Chopper + * @date 2020/11/18 11:40 上午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ArticleServiceImpl extends ServiceImpl implements ArticleService { + + private final ArticleMapper articleMapper; + + @Override + public IPage articlePage(ArticleSearchParams articleSearchParams) { + articleSearchParams.setSort("a.sort"); + return this.baseMapper.getArticleList(PageUtil.initPage(articleSearchParams), articleSearchParams.queryWrapper()); + } + + @Override + public List
list(String categoryId) { + + QueryWrapper
queryWrapper = Wrappers.query(); + queryWrapper.eq(StringUtils.isNotBlank(categoryId), "category_id", categoryId); + return this.list(queryWrapper); + } + + + @Override + public Article updateArticle(Article article) { + Article oldArticle = this.getById(article.getId()); + BeanUtil.copyProperties(article, oldArticle); + this.updateById(oldArticle); + return oldArticle; + } + + @Override + public void customRemove(String id) { + //判断是否为默认文章 + if(this.getById(id).getType().equals(ArticleEnum.OTHER.name())){ + this.removeById(id); + }else{ + throw new ServiceException(ResultCode.ARTICLE_NO_DELETION); + } + } + + @Override + public Article customGet(String id) { + return this.getById(id); + } + + @Override + public Article customGetByType(String type) { + if(!StrUtil.equals(type, ArticleEnum.OTHER.name())){ + return this.getOne(new LambdaUpdateWrapper
().eq(Article::getType,type)); + } + return null; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/serviceimpl/FeedbackServiceImpl.java b/framework/src/main/java/cn/lili/modules/page/serviceimpl/FeedbackServiceImpl.java new file mode 100644 index 00000000..1c7ee821 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/serviceimpl/FeedbackServiceImpl.java @@ -0,0 +1,24 @@ +package cn.lili.modules.page.serviceimpl; + + +import cn.lili.modules.page.entity.dos.Feedback; +import cn.lili.modules.page.mapper.FeedbackMapper; +import cn.lili.modules.page.service.FeedbackService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 意见反馈业务层实现 + * + * @author Chopper + * @date 2020/11/18 11:40 上午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FeedbackServiceImpl extends ServiceImpl implements FeedbackService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/serviceimpl/PageDataServiceImpl.java b/framework/src/main/java/cn/lili/modules/page/serviceimpl/PageDataServiceImpl.java new file mode 100644 index 00000000..795f5934 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/serviceimpl/PageDataServiceImpl.java @@ -0,0 +1,153 @@ +package cn.lili.modules.page.serviceimpl; + +import cn.lili.common.enums.SwitchEnum; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.page.entity.dos.PageData; +import cn.lili.modules.page.entity.dto.PageDataDTO; +import cn.lili.modules.page.entity.enums.PageEnum; +import cn.lili.modules.page.entity.vos.PageDataListVO; +import cn.lili.modules.page.entity.vos.PageDataVO; +import cn.lili.modules.page.mapper.PageDataMapper; +import cn.lili.modules.page.service.PageDataService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 楼层装修管理业务层实现 + * + * @author Bulbasaur + * @date 2020/12/11 9:15 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PageDataServiceImpl extends ServiceImpl implements PageDataService { + + private final PageDataMapper pageDataMapper; + + @Override + public void addStorePageData(String storeId) { + //设置店铺的PC页面 + PageData pageData = new PageData(); + pageData.setNum(storeId); + pageData.setPageClientType(ClientTypeEnum.PC.value()); + pageData.setPageShow(SwitchEnum.OPEN.name()); + pageData.setPageType(PageEnum.STORE.value()); + this.save(pageData); + + //设置店铺的Mobile页面 + PageData mobilePageData = new PageData(); + mobilePageData.setNum(storeId); + mobilePageData.setPageClientType(ClientTypeEnum.H5.value()); + mobilePageData.setPageShow(SwitchEnum.OPEN.name()); + mobilePageData.setPageType(PageEnum.STORE.value()); + this.save(mobilePageData); + } + + @Override + public PageData addPageData(PageData pageData) { + this.save(pageData); + return pageData; + } + + @Override + public PageData updatePageData(PageData pageData) { + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.set(PageData::getPageData, pageData.getPageData()); + lambdaUpdateWrapper.eq(PageData::getId, pageData.getId()); + this.updateById(pageData); + return pageData; + } + + @Override + public PageData releasePageData(String id) { + PageData pageData = this.getById(id); + + + //如果已经发布,不能重复发布 + if (pageData.getPageShow().equals(SwitchEnum.OPEN.name())) { + throw new ServiceException(ResultCode.PAGE_RELEASE_ERROR); + } + + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.set(PageData::getPageShow, SwitchEnum.CLOSE.name()); + lambdaUpdateWrapper.eq(PageData::getPageType, pageData.getPageType()); + lambdaUpdateWrapper.eq(PageData::getPageClientType, pageData.getPageClientType()); + //如果是店铺需要设置店铺ID + if (pageData.getPageType().equals(PageEnum.STORE.value())) { + lambdaUpdateWrapper.eq(PageData::getNum, pageData.getNum()); + } + //设置禁用所有店铺首页 + this.update(lambdaUpdateWrapper); + + //将当前页面启用 + LambdaUpdateWrapper wrapper = Wrappers.lambdaUpdate(); + wrapper.set(PageData::getPageShow, SwitchEnum.OPEN.name()); + wrapper.eq(PageData::getId, pageData.getId()); + this.update(wrapper); + return pageData; + } + + @Override + public boolean removePageData(String id) { + PageData pageData = this.getById(id); + //专题则直接进行删除 + if (pageData.getPageType().equals(PageEnum.SPECIAL)) { + return this.removeById(id); + } + //店铺、平台首页需要判断是否开启,开启则无法删除 + if (pageData.getPageShow().equals(SwitchEnum.OPEN.name())) { + throw new ServiceException(ResultCode.PAGE_OPEN_DELETE_ERROR); + } + //判断是否有其他页面,如果没有其他的页面则无法进行删除 + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq(pageData.getPageType() != null, "page_type", pageData.getPageType()); + queryWrapper.eq(pageData.getPageClientType() != null, "page_client_type", pageData.getPageClientType()); + //如果为店铺页面需要设置店铺ID + if (pageData.getPageType().equals(PageEnum.STORE)) { + queryWrapper.eq(pageData.getNum() != null, "num", pageData.getNum()); + } + //判断是否为唯一的页面 + if (pageDataMapper.getPageDataNum(queryWrapper) == 1) { + throw new ServiceException(ResultCode.PAGE_DELETE_ERROR); + } + return this.removeById(id); + } + + @Override + public PageDataVO getPageData(PageDataDTO pageDataDTO) { + + //如果获取的是专题、店铺页面数据需要传入ID + if (!pageDataDTO.getPageType().equals(PageEnum.INDEX.name()) && pageDataDTO.getNum() == null) { + throw new ServiceException(ResultCode.PAGE_NOT_EXIST); + } + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq(pageDataDTO.getPageType() != null, "page_type", pageDataDTO.getPageType()); + queryWrapper.eq(pageDataDTO.getNum() != null, "num", pageDataDTO.getNum()); + queryWrapper.eq("page_show", SwitchEnum.OPEN.name()); + + queryWrapper.eq("page_client_type", pageDataDTO.getPageClientType()); + + return pageDataMapper.getPageData(queryWrapper); + } + + @Override + public IPage getPageDataList(PageVO pageVO, PageDataDTO pageDataDTO) { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq(pageDataDTO.getPageType() != null, "page_type", pageDataDTO.getPageType()); + queryWrapper.eq(pageDataDTO.getNum() != null, "num", pageDataDTO.getNum()); + queryWrapper.eq(pageDataDTO.getPageClientType() != null, "page_client_type", pageDataDTO.getPageClientType()); + + return pageDataMapper.getPageDataList(PageUtil.initPage(pageVO), queryWrapper); + + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/page/serviceimpl/SpecialServiceImpl.java b/framework/src/main/java/cn/lili/modules/page/serviceimpl/SpecialServiceImpl.java new file mode 100644 index 00000000..b401e3be --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/page/serviceimpl/SpecialServiceImpl.java @@ -0,0 +1,50 @@ +package cn.lili.modules.page.serviceimpl; + +import cn.lili.modules.page.entity.dos.PageData; +import cn.lili.modules.page.entity.dos.Special; +import cn.lili.modules.page.mapper.SpecialMapper; +import cn.lili.modules.page.service.PageDataService; +import cn.lili.modules.page.service.SpecialService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 专题活动业务层实现 + * + * @author Bulbasaur + * @date 2020/12/7 11:27 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SpecialServiceImpl extends ServiceImpl implements SpecialService { + + //专题 + private final SpecialMapper specialMapper; + //页面数据 + private final PageDataService pageDataService; + + @Override + public Special addSpecial(Special special) { + //新建页面 + PageData pageData=new PageData(); + pageDataService.save(pageData); + + //设置专题页面 + special.setPageDataId(pageData.getId()); + this.save(special); + return special; + } + + @Override + public boolean removeSpecial(String id) { + + //删除页面内容 + Special special=this.getById(id); + pageDataService.removeById(special.getPageDataId()); + + //删除专题 + return this.removeById(id); + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/entity/RefundLog.java b/framework/src/main/java/cn/lili/modules/payment/entity/RefundLog.java new file mode 100644 index 00000000..9023b991 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/entity/RefundLog.java @@ -0,0 +1,90 @@ +package cn.lili.modules.payment.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 退款日志 + * + * @author Chopper + * @date 2021/1/28 09:21 + */ +@Data +@Entity +@Table(name = "li_refund_log") +@TableName("li_refund_log") +@Builder +@NoArgsConstructor +@AllArgsConstructor +@ApiModel(value = "退款日志") +public class RefundLog { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "退单编号") + private String afterSaleNo; + + @ApiModelProperty(value = "订单编号") + private String orderSn; + + @ApiModelProperty(value = "金额") + private Double totalAmount; + + @ApiModelProperty(value = "改笔交易支付金额") + private Double payPrice; + + @ApiModelProperty(value = "是否已退款") + private Boolean isRefund ; + + @ApiModelProperty(value = "退款方式") + private String paymentName; + + + @ApiModelProperty(value = "支付第三方付款流水") + private String paymentReceivableNo; + + @ApiModelProperty(value = "退款请求流水") + private String outOrderNo; + + + @ApiModelProperty(value = "第三方退款流水号") + private String receivableNo; + + @ApiModelProperty(value = "退款理由") + private String refundReason; + + @ApiModelProperty(value = "退款失败原因") + private String errorMessage; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/CashierSupport.java b/framework/src/main/java/cn/lili/modules/payment/kit/CashierSupport.java new file mode 100644 index 00000000..a289ec9b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/CashierSupport.java @@ -0,0 +1,169 @@ +package cn.lili.modules.payment.kit; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.SpringContextUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.member.service.MemberWalletService; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.enums.PaymentClientEnum; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.payment.kit.params.CashierExecute; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.OrderSetting; +import cn.lili.modules.system.entity.dto.payment.PaymentSupportSetting; +import cn.lili.modules.system.entity.dto.payment.dto.PaymentSupportItem; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 收银台工具 + * + * @author Chopper + * @date 2020-12-19 09:25 + */ +@Component +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CashierSupport { + //收银台 + private final List cashierExecuteList; + //预存款 + private final MemberWalletService memberWalletService; + //配置 + private final SettingService settingService; + + /** + * 支付 + * + * @param paymentMethodEnum 支付渠道枚举 + * @param paymentClientEnum 支付方式枚举 + * @return 支付消息 + */ + public ResultMessage payment(PaymentMethodEnum paymentMethodEnum, PaymentClientEnum paymentClientEnum, + HttpServletRequest request, HttpServletResponse response, + PayParam payParam) { + if (paymentClientEnum == null || paymentMethodEnum == null) { + throw new ServiceException("未知的支付方式"); + } + //获取支付插件 + Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin()); + log.info("支付请求:客户端:{},支付类型:{},请求:{}", paymentClientEnum.name(), paymentMethodEnum.name(), payParam.toString()); + + //支付方式调用 + switch (paymentClientEnum) { + case H5: + return payment.h5pay(request, response, payParam); + case APP: + return payment.appPay(request, payParam); + case JSAPI: + return payment.JSApiPay(request, payParam); + case NATIVE: + return payment.nativePay(request, payParam); + case MP: + return payment.mpPay(request, payParam); + default: + return null; + } + } + + /** + * 支付 支持的支付方式 + * + * @param client 客户端类型 + * @return 支持的支付方式 + */ + public List support(String client) { + + ClientTypeEnum clientTypeEnum; + try { + clientTypeEnum = ClientTypeEnum.valueOf(client); + } catch (IllegalArgumentException e) { + throw new ServiceException(ResultCode.PAY_CLIENT_TYPE_ERROR); + } + //支付方式 循环获取 + Setting setting = settingService.get(SettingEnum.PAYMENT_SUPPORT.name()); + PaymentSupportSetting paymentSupportSetting = JSONUtil.toBean(setting.getSettingValue(), PaymentSupportSetting.class); + for (PaymentSupportItem paymentSupportItem : paymentSupportSetting.getPaymentSupportItems()) { + if (paymentSupportItem.getClient().equals(clientTypeEnum.name())) { + return paymentSupportItem.getSupports(); + } + } + throw new ServiceException(ResultCode.PAY_NOT_SUPPORT); + } + + /** + * 支付回调 + * + * @param paymentMethodEnum 支付渠道枚举 + * @return 回调消息 + */ + public void callback(PaymentMethodEnum paymentMethodEnum, + HttpServletRequest request) { + + log.info("支付回调:支付类型:{}", paymentMethodEnum.name()); + + //获取支付插件 + Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin()); + payment.callBack(request); + } + + /** + * 支付通知 + * + * @param paymentMethodEnum 支付渠道 + */ + public void notify(PaymentMethodEnum paymentMethodEnum, + HttpServletRequest request) { + + log.info("支付异步通知:支付类型:{}", paymentMethodEnum.name()); + + //获取支付插件 + Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin()); + payment.notify(request); + } + + /** + * 获取收银台参数 + * + * @param payParam 支付请求参数 + * @return 收银台参数 + */ + public CashierParam cashierParam(PayParam payParam) { + for (CashierExecute paramInterface : cashierExecuteList) { + CashierParam cashierParam = paramInterface.getPaymentParams(payParam); + if (cashierParam != null) { + cashierParam.setSupport(support(payParam.getClientType())); + cashierParam.setWalletValue(memberWalletService.getMemberWallet(UserContext.getCurrentUser().getId()).getMemberWallet()); + OrderSetting orderSetting = JSONUtil.toBean(settingService.get(SettingEnum.ORDER_SETTING.name()).getSettingValue(), OrderSetting.class); + Integer minute = orderSetting.getAutoCancel(); + cashierParam.setAutoCancel(cashierParam.getCreateTime().getTime() + minute * 1000 * 60); + return cashierParam; + } + } + + log.error("错误的支付请求:{}", payParam.toString()); + throw new ServiceException(ResultCode.PAY_CASHIER_ERROR); + } + + public Boolean paymentResult(PayParam payParam) { + for (CashierExecute cashierExecute : cashierExecuteList) { + if (cashierExecute.cashierEnum().name().equals(payParam.getOrderType())) { + return cashierExecute.paymentResult(payParam); + } + } + return false; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/Payment.java b/framework/src/main/java/cn/lili/modules/payment/kit/Payment.java new file mode 100644 index 00000000..1308cb24 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/Payment.java @@ -0,0 +1,166 @@ +package cn.lili.modules.payment.kit; + +import cn.hutool.core.net.URLEncoder; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.nio.charset.StandardCharsets; + +/** + * 支付接口 + * + * @author Chopper + * @date 2020-12-21 09:32 + */ +public interface Payment { + /** + * 普通移动网页调用支付app + * + * @param request HttpServletRequest + * @param response HttpServletResponse + * @param payParam api参数 + * @return 移动支付所需参数 + */ + default ResultMessage h5pay(HttpServletRequest request, HttpServletResponse response, PayParam payParam) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + /** + * 公众号内部调用支付 + * + * @param request HttpServletRequest + * @param payParam api参数 + * @return 公众号内部支付参数 + */ + default ResultMessage JSApiPay(HttpServletRequest request, PayParam payParam) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + /** + * app支付 + * + * @param request HttpServletRequest + * @param payParam 支付参数 + * @return app支付所需参数 + */ + default ResultMessage appPay(HttpServletRequest request, PayParam payParam) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + /** + * 展示二维码扫描支付 + * + * @param request HttpServletRequest + * @param payParam 支付参数 + * @return 二维码内容 + */ + default ResultMessage nativePay(HttpServletRequest request, PayParam payParam) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + /** + * 小程序支付 + * + * @param request HttpServletRequest + * @param payParam 支付参数 + * @return 二维码内容 + */ + default ResultMessage mpPay(HttpServletRequest request, PayParam payParam) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + + /** + * 退款 + * + * @param refundLog 退款请求参数 + */ + default void refund(RefundLog refundLog) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + + /** + * 取消支付订单 + * + * @param refundLog 支付参数 + */ + default void cancel(RefundLog refundLog) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + + /** + * 回调 + * + * @param request HttpServletRequest + */ + default void callBack(HttpServletRequest request) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + /** + * 异步通知 + * + * @param request HttpServletRequest + */ + default void notify(HttpServletRequest request) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + /** + * 退款异步通知 + * + * @param request HttpServletRequest + */ + default void refundNotify(HttpServletRequest request) { + throw new ServiceException(ResultCode.PAY_ERROR); + } + + /** + * 支付回调地址 + * + * @param api api地址 + * @param paymentMethodEnum 支付类型 + * @return 回调地址 + */ + default String callbackUrl(String api, PaymentMethodEnum paymentMethodEnum) { + return api + "/buyer/cashier/callback/" + paymentMethodEnum.name(); + } + + /** + * 支付异步通知地址 + * + * @param api api地址 + * @param paymentMethodEnum 支付类型 + * @return 异步通知地址 + */ + default String notifyUrl(String api, PaymentMethodEnum paymentMethodEnum) { + return api + "/buyer/cashier/notify/" + paymentMethodEnum.name(); + } + + /** + * 退款支付异步通知地址 + * + * @param api api地址 + * @param paymentMethodEnum 支付类型 + * @return 异步通知地址 + */ + default String refundNotifyUrl(String api, PaymentMethodEnum paymentMethodEnum) { + return api + "/buyer/cashier/refund/notify/" + paymentMethodEnum.name(); + } + + public static void main(String[] args) { + String str = "orderType=TRADE&sn=O202104271386961176205721601"; + System.out.println(str.length()); + URLEncoder urlEncoder = new URLEncoder(); + System.out.println(urlEncoder.encode(str, StandardCharsets.UTF_8)); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/RefundSupport.java b/framework/src/main/java/cn/lili/modules/payment/kit/RefundSupport.java new file mode 100644 index 00000000..ce1e7d07 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/RefundSupport.java @@ -0,0 +1,109 @@ +package cn.lili.modules.payment.kit; + +import cn.lili.common.utils.SnowFlake; +import cn.lili.common.utils.SpringContextUtil; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.service.AfterSaleService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.order.order.service.StoreFlowService; +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.util.Date; + +/** + * 退款支持 + * + * @author Chopper + * @date 2020-12-19 09:25 + */ +@Component +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RefundSupport { + //店铺流水 + private final StoreFlowService storeFlowService; + //售后 + @Autowired + private AfterSaleService afterSaleService; + + private final OrderService orderService; + + /** + * 售后退款 + * + * @param afterSale + */ + public void refund(AfterSale afterSale) { + Order order = orderService.getBySn(afterSale.getOrderSn()); + RefundLog refundLog = RefundLog.builder() + .isRefund(false) + .totalAmount(afterSale.getActualRefundPrice()) + .payPrice(afterSale.getActualRefundPrice()) + .memberId(afterSale.getMemberId()) + .paymentName(order.getPaymentMethod()) + .afterSaleNo(afterSale.getSn()) + .paymentReceivableNo(order.getReceivableNo()) + .outOrderNo("AF" + SnowFlake.getIdStr()) + .orderSn(afterSale.getOrderSn()) + .refundReason(afterSale.getReason()) + .build(); + PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.paymentNameOf(order.getPaymentMethod()); + Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin()); + payment.refund(refundLog); + + //记录售后单中的退款金额 + afterSaleService.update(new LambdaUpdateWrapper() + .eq(AfterSale::getId, afterSale.getId()) + .set(AfterSale::getRefundTime, new Date())); + //记录退款流水 + storeFlowService.refundOrder(afterSale); + } + + /** + * 订单取消 + * + * @param afterSale + */ + public void cancel(AfterSale afterSale) { + + Order order = orderService.getBySn(afterSale.getOrderSn()); + RefundLog refundLog = RefundLog.builder() + .isRefund(false) + .totalAmount(afterSale.getActualRefundPrice()) + .payPrice(afterSale.getActualRefundPrice()) + .memberId(afterSale.getMemberId()) + .paymentName(order.getPaymentMethod()) + .afterSaleNo(afterSale.getSn()) + .paymentReceivableNo(order.getReceivableNo()) + .outOrderNo("AF" + SnowFlake.getIdStr()) + .orderSn(afterSale.getOrderSn()) + .refundReason(afterSale.getReason()) + .build(); + PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.paymentNameOf(order.getPaymentMethod()); + Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin()); + payment.refund(refundLog); + } + + + /** + * 退款通知 + * + * @param paymentMethodEnum 支付渠道 + */ + public void notify(PaymentMethodEnum paymentMethodEnum, + HttpServletRequest request) { + + //获取支付插件 + Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin()); + payment.refundNotify(request); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/PaymentHttpResponse.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/PaymentHttpResponse.java new file mode 100644 index 00000000..239303ef --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/PaymentHttpResponse.java @@ -0,0 +1,70 @@ + +package cn.lili.modules.payment.kit.core; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.CaseInsensitiveMap; +import cn.hutool.core.util.StrUtil; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * 支付接口响应 + * + * @author Chopper + * @date 2020/12/18 15:13 + */ +public class PaymentHttpResponse implements Serializable { + private static final long serialVersionUID = 6089103955998013402L; + private String body; + private int status; + private Map> headers; + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> headers) { + this.headers = headers; + } + + public String getHeader(String name) { + List values = this.headerList(name); + return CollectionUtil.isEmpty(values) ? null : values.get(0); + } + + private List headerList(String name) { + if (StrUtil.isBlank(name)) { + return null; + } else { + CaseInsensitiveMap> headersIgnoreCase = new CaseInsensitiveMap<>(getHeaders()); + return headersIgnoreCase.get(name.trim()); + } + } + + @Override + public String toString() { + return "IJPayHttpResponse{" + + "body='" + body + '\'' + + ", status=" + status + + ", headers=" + headers + + '}'; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/XmlHelper.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/XmlHelper.java new file mode 100644 index 00000000..d6b15471 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/XmlHelper.java @@ -0,0 +1,249 @@ +package cn.lili.modules.payment.kit.core; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; + +/** + * xpath解析xml + * + *
+ *     文档地址:
+ *     http://www.w3school.com.cn/xpath/index.asp
+ * 
+ * @author L.cm + */ +public class XmlHelper { + private final XPath path; + private final Document doc; + + private XmlHelper(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory dbf = getDocumentBuilderFactory(); + // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all + // XML entity attacks are prevented + // Xerces 2 only - + // http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl + dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + + // If you can't completely disable DTDs, then at least do the following: + // Xerces 1 - + // http://xerces.apache.org/xerces-j/features.html#external-general-entities + // Xerces 2 - + // http://xerces.apache.org/xerces2-j/features.html#external-general-entities + // JDK7+ - http://xml.org/sax/features/external-general-entities + dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); + + // Xerces 1 - + // http://xerces.apache.org/xerces-j/features.html#external-parameter-entities + // Xerces 2 - + // http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities + // JDK7+ - http://xml.org/sax/features/external-parameter-entities + dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + + // Disable external DTDs as well + dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + + // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and + // Entity Attacks" + dbf.setXIncludeAware(false); + dbf.setExpandEntityReferences(false); + DocumentBuilder db = dbf.newDocumentBuilder(); + doc = db.parse(inputSource); + path = getXpathFactory().newXPath(); + } + + private static XmlHelper create(InputSource inputSource) { + try { + return new XmlHelper(inputSource); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new RuntimeException(e); + } + } + + public static XmlHelper of(InputStream is) { + InputSource inputSource = new InputSource(is); + return create(inputSource); + } + + public static XmlHelper of(File file) { + InputSource inputSource = new InputSource(file.toURI().toASCIIString()); + return create(inputSource); + } + + public static XmlHelper of(String xmlStr) { + StringReader sr = new StringReader(xmlStr.trim()); + InputSource inputSource = new InputSource(sr); + XmlHelper xmlHelper = create(inputSource); + sr.close(); + return xmlHelper; + } + + private Object evalXpath(String expression, Object item, QName returnType) { + item = null == item ? doc : item; + try { + return path.evaluate(expression, item, returnType); + } catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + } + + /** + * 获取String + * + * @param expression 路径 + * @return String + */ + public String getString(String expression) { + return (String) evalXpath(expression, null, XPathConstants.STRING); + } + + /** + * 获取Boolean + * + * @param expression 路径 + * @return String + */ + public Boolean getBoolean(String expression) { + return (Boolean) evalXpath(expression, null, XPathConstants.BOOLEAN); + } + + /** + * 获取Number + * + * @param expression 路径 + * @return {Number} + */ + public Number getNumber(String expression) { + return (Number) evalXpath(expression, null, XPathConstants.NUMBER); + } + + /** + * 获取某个节点 + * + * @param expression 路径 + * @return {Node} + */ + public Node getNode(String expression) { + return (Node) evalXpath(expression, null, XPathConstants.NODE); + } + + /** + * 获取子节点 + * + * @param expression 路径 + * @return NodeList + */ + public NodeList getNodeList(String expression) { + return (NodeList) evalXpath(expression, null, XPathConstants.NODESET); + } + + /** + * 获取String + * + * @param node 节点 + * @param expression 相对于node的路径 + * @return String + */ + public String getString(Object node, String expression) { + return (String) evalXpath(expression, node, XPathConstants.STRING); + } + + /** + * 获取 + * + * @param node 节点 + * @param expression 相对于node的路径 + * @return String + */ + public Boolean getBoolean(Object node, String expression) { + return (Boolean) evalXpath(expression, node, XPathConstants.BOOLEAN); + } + + /** + * 获取 + * + * @param node 节点 + * @param expression 相对于node的路径 + * @return {Number} + */ + public Number getNumber(Object node, String expression) { + return (Number) evalXpath(expression, node, XPathConstants.NUMBER); + } + + /** + * 获取某个节点 + * + * @param node 节点 + * @param expression 路径 + * @return {Node} + */ + public Node getNode(Object node, String expression) { + return (Node) evalXpath(expression, node, XPathConstants.NODE); + } + + /** + * 获取子节点 + * + * @param node 节点 + * @param expression 相对于node的路径 + * @return NodeList + */ + public NodeList getNodeList(Object node, String expression) { + return (NodeList) evalXpath(expression, node, XPathConstants.NODESET); + } + + /** + * 针对没有嵌套节点的简单处理 + * + * @return map集合 + */ + public Map toMap() { + Element root = doc.getDocumentElement(); + + // 将节点封装成map形式 + NodeList list = root.getChildNodes(); + Map params = new HashMap(list.getLength()); + for (int i = 0; i < list.getLength(); i++) { + Node node = list.item(i); + params.put(node.getNodeName(), node.getTextContent()); + } + // 含有空白符会生成一个#text参数 + params.remove("#text"); + return params; + } + + private static DocumentBuilderFactory getDocumentBuilderFactory() { + return XmlHelper.XmlHelperHolder.documentBuilderFactory; + } + + private static XPathFactory getXpathFactory() { + return XmlHelper.XmlHelperHolder.xPathFactory; + } + + /** + * 内部类单例 + */ + private static class XmlHelperHolder { + private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + private static XPathFactory xPathFactory = XPathFactory.newInstance(); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/enums/RequestMethodEnums.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/enums/RequestMethodEnums.java new file mode 100644 index 00000000..f98591a1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/enums/RequestMethodEnums.java @@ -0,0 +1,57 @@ +package cn.lili.modules.payment.kit.core.enums; + +/** + * HTTP 请求的方法 + * + * @author Chopper + * @date 2021-01-25 15:10 + */ +public enum RequestMethodEnums { + /** + * 上传实质是 post 请求 + */ + UPLOAD("POST"), + /** + * post 请求 + */ + POST("POST"), + /** + * get 请求 + */ + GET("GET"), + /** + * put 请求 + */ + PUT("PUT"), + /** + * delete 请求 + */ + DELETE("DELETE"), + /** + * options 请求 + */ + OPTIONS("OPTIONS"), + /** + * head 请求 + */ + HEAD("HEAD"), + /** + * trace 请求 + */ + TRACE("TRACE"), + /** + * connect 请求 + */ + CONNECT("CONNECT"); + + private final String method; + + RequestMethodEnums(String method) { + this.method = method; + } + + @Override + public String toString() { + return this.method; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/enums/SignType.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/enums/SignType.java new file mode 100644 index 00000000..d3994217 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/enums/SignType.java @@ -0,0 +1,37 @@ +package cn.lili.modules.payment.kit.core.enums; + +/** + * 签名方式 + * + * @author Chopper + * @date 2021-01-25 15:10 + */ +public enum SignType { + /** + * HMAC-SHA256 加密 + */ + HMACSHA256("HMAC-SHA256"), + /** + * MD5 加密 + */ + MD5("MD5"), + /** + * RSA + */ + RSA("RSA"); + + SignType(String type) { + this.type = type; + } + + private final String type; + + public String getType() { + return type; + } + + @Override + public String toString() { + return type; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/http/AbstractHttpDelegate.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/http/AbstractHttpDelegate.java new file mode 100644 index 00000000..4a4906ff --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/http/AbstractHttpDelegate.java @@ -0,0 +1,498 @@ +package cn.lili.modules.payment.kit.core.http; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import cn.hutool.http.ssl.SSLSocketFactoryBuilder; +import cn.lili.modules.payment.kit.core.PaymentHttpResponse; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.util.Map; + +/** + * Http 代理类 + * + * @author Chopper + * @date 2021-01-25 15:10 + */ +public abstract class AbstractHttpDelegate { + + /** + * get 请求 + * + * @param url 请求url + * @return {@link String} 请求返回的结果 + */ + public String get(String url) { + return HttpUtil.get(url); + } + + /** + * get 请求 + * + * @param url 请求url + * @param paramMap 请求参数 + * @return {@link String} 请求返回的结果 + */ + public String get(String url, Map paramMap) { + return HttpUtil.get(url, paramMap); + } + + /** + * get 请求 + * + * @param url 请求url + * @param paramMap 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public PaymentHttpResponse get(String url, Map paramMap, Map headers) { + PaymentHttpResponse response = new PaymentHttpResponse(); + HttpResponse httpResponse = getToResponse(url, paramMap, headers); + response.setBody(httpResponse.body()); + response.setStatus(httpResponse.getStatus()); + response.setHeaders(httpResponse.headers()); + return response; + } + + /** + * post 请求 + * + * @param url 请求url + * @param data 请求参数 + * @return {@link String} 请求返回的结果 + */ + public String post(String url, String data) { + return HttpUtil.post(url, data); + } + + /** + * post 请求 + * + * @param url 请求url + * @param paramMap 请求参数 + * @return {@link String} 请求返回的结果 + */ + public String post(String url, Map paramMap) { + return HttpUtil.post(url, paramMap); + } + + /** + * post 请求 + * + * @param url 请求url + * @param paramMap 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public PaymentHttpResponse post(String url, Map paramMap, Map headers) { + PaymentHttpResponse response = new PaymentHttpResponse(); + HttpResponse httpResponse = postToResponse(url, headers, paramMap); + response.setBody(httpResponse.body()); + response.setStatus(httpResponse.getStatus()); + response.setHeaders(httpResponse.headers()); + return response; + } + + /** + * post 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public PaymentHttpResponse post(String url, String data, Map headers) { + PaymentHttpResponse response = new PaymentHttpResponse(); + HttpResponse httpResponse = postToResponse(url, headers, data); + response.setBody(httpResponse.body()); + response.setStatus(httpResponse.getStatus()); + response.setHeaders(httpResponse.headers()); + return response; + } + + /** + * patch 请求 + * + * @param url 请求url + * @param paramMap 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public PaymentHttpResponse patch(String url, Map paramMap, Map headers) { + PaymentHttpResponse response = new PaymentHttpResponse(); + HttpResponse httpResponse = patchToResponse(url, headers, paramMap); + response.setBody(httpResponse.body()); + response.setStatus(httpResponse.getStatus()); + response.setHeaders(httpResponse.headers()); + return response; + } + + /** + * patch 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public PaymentHttpResponse patch(String url, String data, Map headers) { + PaymentHttpResponse response = new PaymentHttpResponse(); + HttpResponse httpResponse = patchToResponse(url, headers, data); + response.setBody(httpResponse.body()); + response.setStatus(httpResponse.getStatus()); + response.setHeaders(httpResponse.headers()); + return response; + } + + /** + * delete 请求 + * + * @param url 请求url + * @param paramMap 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public PaymentHttpResponse delete(String url, Map paramMap, Map headers) { + PaymentHttpResponse response = new PaymentHttpResponse(); + HttpResponse httpResponse = deleteToResponse(url, headers, paramMap); + response.setBody(httpResponse.body()); + response.setStatus(httpResponse.getStatus()); + response.setHeaders(httpResponse.headers()); + return response; + } + + /** + * delete 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public PaymentHttpResponse delete(String url, String data, Map headers) { + PaymentHttpResponse response = new PaymentHttpResponse(); + HttpResponse httpResponse = deleteToResponse(url, headers, data); + response.setBody(httpResponse.body()); + response.setStatus(httpResponse.getStatus()); + response.setHeaders(httpResponse.headers()); + return response; + } + + /** + * put 请求 + * + * @param url 请求url + * @param paramMap 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public PaymentHttpResponse put(String url, Map paramMap, Map headers) { + PaymentHttpResponse response = new PaymentHttpResponse(); + HttpResponse httpResponse = putToResponse(url, headers, paramMap); + response.setBody(httpResponse.body()); + response.setStatus(httpResponse.getStatus()); + response.setHeaders(httpResponse.headers()); + return response; + } + + /** + * put 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public PaymentHttpResponse put(String url, String data, Map headers) { + PaymentHttpResponse response = new PaymentHttpResponse(); + HttpResponse httpResponse = putToResponse(url, headers, data); + response.setBody(httpResponse.body()); + response.setStatus(httpResponse.getStatus()); + response.setHeaders(httpResponse.headers()); + return response; + } + + /** + * 上传文件 + * + * @param url 请求url + * @param data 请求参数 + * @param certPath 证书路径 + * @param certPass 证书密码 + * @param filePath 上传文件路径 + * @param protocol 协议 + * @return {@link String} 请求返回的结果 + */ + public String upload(String url, String data, String certPath, String certPass, String filePath, String protocol) { + try { + File file = FileUtil.newFile(filePath); + return HttpRequest.post(url) + .setSSLSocketFactory(SSLSocketFactoryBuilder + .create() + .setProtocol(protocol) + .setKeyManagers(getKeyManager(certPass, certPath, null)) + .setSecureRandom(new SecureRandom()) + .build() + ) + .header("Content-Type", "multipart/form-data;boundary=\"boundary\"") + .form("file", file) + .form("meta", data) + .execute() + .body(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 上传文件 + * + * @param url 请求url + * @param data 请求参数 + * @param certPath 证书路径 + * @param certPass 证书密码 + * @param filePath 上传文件路径 + * @return {@link String} 请求返回的结果 + */ + public String upload(String url, String data, String certPath, String certPass, String filePath) { + return upload(url, data, certPath, certPass, filePath, SSLSocketFactoryBuilder.TLSv1); + } + + /** + * post 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param certPath 证书路径 + * @param certPass 证书密码 + * @param protocol 协议 + * @return {@link String} 请求返回的结果 + */ + public String post(String url, String data, String certPath, String certPass, String protocol) { + try { + return HttpRequest.post(url) + .setSSLSocketFactory(SSLSocketFactoryBuilder + .create() + .setProtocol(protocol) + .setKeyManagers(getKeyManager(certPass, certPath, null)) + .setSecureRandom(new SecureRandom()) + .build() + ) + .body(data) + .execute() + .body(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * post 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param certPath 证书路径 + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public String post(String url, String data, String certPath, String certPass) { + return post(url, data, certPath, certPass, SSLSocketFactoryBuilder.TLSv1); + } + + /** + * post 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param certFile 证书文件输入流 + * @param certPass 证书密码 + * @param protocol 协议 + * @return {@link String} 请求返回的结果 + */ + public String post(String url, String data, InputStream certFile, String certPass, String protocol) { + try { + return HttpRequest.post(url) + .setSSLSocketFactory(SSLSocketFactoryBuilder + .create() + .setProtocol(protocol) + .setKeyManagers(getKeyManager(certPass, null, certFile)) + .setSecureRandom(new SecureRandom()) + .build() + ) + .body(data) + .execute() + .body(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * post 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param certFile 证书文件输入流 + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public String post(String url, String data, InputStream certFile, String certPass) { + return post(url, data, certFile, certPass, SSLSocketFactoryBuilder.TLSv1); + } + + /** + * get 请求 + * + * @param url 请求url + * @param paramMap 请求参数 + * @param headers 请求头 + * @return {@link HttpResponse} 请求返回的结果 + */ + private HttpResponse getToResponse(String url, Map paramMap, Map headers) { + return HttpRequest.get(url) + .addHeaders(headers) + .form(paramMap) + .execute(); + } + + /** + * post 请求 + * + * @param url 请求url + * @param headers 请求头 + * @param data 请求参数 + * @return {@link HttpResponse} 请求返回的结果 + */ + private HttpResponse postToResponse(String url, Map headers, String data) { + return HttpRequest.post(url) + .addHeaders(headers) + .body(data) + .execute(); + } + + /** + * post 请求 + * + * @param url 请求url + * @param headers 请求头 + * @param paramMap 请求参数 + * @return {@link HttpResponse} 请求返回的结果 + */ + private HttpResponse postToResponse(String url, Map headers, Map paramMap) { + return HttpRequest.post(url) + .addHeaders(headers) + .form(paramMap) + .execute(); + } + + /** + * patch 请求 + * + * @param url 请求url + * @param headers 请求头 + * @param paramMap 请求参数 + * @return {@link HttpResponse} 请求返回的结果 + */ + private HttpResponse patchToResponse(String url, Map headers, Map paramMap) { + return HttpRequest.patch(url) + .addHeaders(headers) + .form(paramMap) + .execute(); + } + + /** + * patch 请求 + * + * @param url 请求url + * @param headers 请求头 + * @param data 请求参数 + * @return {@link HttpResponse} 请求返回的结果 + */ + private HttpResponse patchToResponse(String url, Map headers, String data) { + return HttpRequest.patch(url) + .addHeaders(headers) + .body(data) + .execute(); + } + + /** + * delete 请求 + * + * @param url 请求url + * @param headers 请求头 + * @param data 请求参数 + * @return {@link HttpResponse} 请求返回的结果 + */ + private HttpResponse deleteToResponse(String url, Map headers, String data) { + return HttpRequest.delete(url) + .addHeaders(headers) + .body(data) + .execute(); + } + + /** + * delete 请求 + * + * @param url 请求url + * @param headers 请求头 + * @param paramMap 请求参数 + * @return {@link HttpResponse} 请求返回的结果 + */ + private HttpResponse deleteToResponse(String url, Map headers, Map paramMap) { + return HttpRequest.delete(url) + .addHeaders(headers) + .form(paramMap) + .execute(); + } + + /** + * put 请求 + * + * @param url 请求url + * @param headers 请求头 + * @param data 请求参数 + * @return {@link HttpResponse} 请求返回的结果 + */ + private HttpResponse putToResponse(String url, Map headers, String data) { + return HttpRequest.put(url) + .addHeaders(headers) + .body(data) + .execute(); + } + + /** + * put 请求 + * + * @param url 请求url + * @param headers 请求头 + * @param paramMap 请求参数 + * @return {@link HttpResponse} 请求返回的结果 + */ + private HttpResponse putToResponse(String url, Map headers, Map paramMap) { + return HttpRequest.put(url) + .addHeaders(headers) + .form(paramMap) + .execute(); + } + + + private KeyManager[] getKeyManager(String certPass, String certPath, InputStream certFile) throws Exception { + KeyStore clientStore = KeyStore.getInstance("PKCS12"); + if (certFile != null) { + clientStore.load(certFile, certPass.toCharArray()); + } else { + clientStore.load(new FileInputStream(certPath), certPass.toCharArray()); + } + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(clientStore, certPass.toCharArray()); + return kmf.getKeyManagers(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/AesUtil.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/AesUtil.java new file mode 100644 index 00000000..57a94476 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/AesUtil.java @@ -0,0 +1,62 @@ +package cn.lili.modules.payment.kit.core.kit; + +import cn.hutool.core.codec.Base64; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +/** + *

工具类 AesUtil

+ * + * @author 微信 + */ +public class AesUtil { + + static final int KEY_LENGTH_BYTE = 32; + static final int TAG_LENGTH_BIT = 128; + private final byte[] aesKey; + + /** + * @param key APIv3 密钥 + */ + public AesUtil(byte[] key) { + if (key.length != KEY_LENGTH_BYTE) { + throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节"); + } + this.aesKey = key; + } + + /** + * 证书和回调报文解密 + * + * @param associatedData associated_data + * @param nonce nonce + * @param cipherText ciphertext + * @return {String} 平台证书明文 + * @throws GeneralSecurityException 异常 + */ + public String decryptToString(byte[] associatedData, byte[] nonce, String cipherText) throws GeneralSecurityException { + try { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + SecretKeySpec key = new SecretKeySpec(aesKey, "AES"); + GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce); + + cipher.init(Cipher.DECRYPT_MODE, key, spec); + cipher.updateAAD(associatedData); + + return new String(cipher.doFinal(Base64.decode(cipherText)), StandardCharsets.UTF_8); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new IllegalStateException(e); + } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { + throw new IllegalArgumentException(e); + } + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/HttpKit.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/HttpKit.java new file mode 100644 index 00000000..767d4ad1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/HttpKit.java @@ -0,0 +1,80 @@ +package cn.lili.modules.payment.kit.core.kit; + +import cn.lili.modules.payment.kit.core.http.AbstractHttpDelegate; + +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + *

Http 工具类

+ * + * @author + */ +public class HttpKit { + + private static AbstractHttpDelegate delegate = new DefaultHttpKit(); + + public static AbstractHttpDelegate getDelegate() { + return delegate; + } + + public static void setDelegate(AbstractHttpDelegate delegate) { + HttpKit.delegate = delegate; + } + + public static String readData(HttpServletRequest request) { + BufferedReader br = null; + try { + StringBuilder result = new StringBuilder(); + br = request.getReader(); + for (String line; (line = br.readLine()) != null; ) { + if (result.length() > 0) { + result.append("\n"); + } + result.append(line); + } + return result.toString(); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * 将同步通知的参数转化为Map + * + * @param request {@link HttpServletRequest} + * @return 转化后的 Map + */ + public static Map toMap(HttpServletRequest request) { + Map params = new HashMap<>(); + Map requestParams = request.getParameterMap(); + for (String name : requestParams.keySet()) { + String[] values = requestParams.get(name); + String valueStr = ""; + for (int i = 0; i < values.length; i++) { + valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; + } + params.put(name, valueStr); + } + return params; + } +} + +/** + * 使用 huTool 实现的 Http 工具类 + * + * @author + */ +class DefaultHttpKit extends AbstractHttpDelegate { +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/IpKit.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/IpKit.java new file mode 100644 index 00000000..cecaed0c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/IpKit.java @@ -0,0 +1,41 @@ +package cn.lili.modules.payment.kit.core.kit; +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// +import javax.servlet.http.HttpServletRequest; + +/** + * @author + */ +public class IpKit { + public IpKit() { + } + + public static String getRealIp(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + // 对于通过多个代理的情况,第一个 IP 为客户端真实 IP,多个IP按照','分割 + if (ip != null && ip.length() > 15) { + if (ip.indexOf(",") > 0) { + ip = ip.substring(0, ip.indexOf(",")); + } + } +// return ip; + return "27.189.225.9"; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/PayKit.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/PayKit.java new file mode 100644 index 00000000..9f94e796 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/PayKit.java @@ -0,0 +1,480 @@ +package cn.lili.modules.payment.kit.core.kit; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.digest.HmacAlgorithm; +import cn.lili.modules.payment.kit.core.XmlHelper; +import cn.lili.modules.payment.kit.core.enums.RequestMethodEnums; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.*; +import java.util.*; + +/** + * 支付工具 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/12/18 15:24 + */ +public class PayKit { + + /** + * 生成16进制的 sha256 字符串 + * + * @param data 数据 + * @param key 密钥 + * @return sha256 字符串 + */ + public static String hmacSha256(String data, String key) { + return SecureUtil.hmac(HmacAlgorithm.HmacSHA256, key).digestHex(data, CharsetUtil.UTF_8); + } + + /** + * SHA1加密文件,生成16进制SHA1字符串
+ * + * @param dataFile 被加密文件 + * @return SHA1 字符串 + */ + public static String sha1(File dataFile) { + return SecureUtil.sha1(dataFile); + } + + /** + * SHA1加密,生成16进制SHA1字符串
+ * + * @param data 数据 + * @return SHA1 字符串 + */ + public static String sha1(InputStream data) { + return SecureUtil.sha1(data); + } + + /** + * SHA1加密,生成16进制SHA1字符串
+ * + * @param data 数据 + * @return SHA1 字符串 + */ + public static String sha1(String data) { + return SecureUtil.sha1(data); + } + + /** + * 生成16进制 MD5 字符串 + * + * @param data 数据 + * @return MD5 字符串 + */ + public static String md5(String data) { + return SecureUtil.md5(data); + } + + /** + * AES 解密 + * + * @param base64Data 需要解密的数据 + * @param key 密钥 + * @return 解密后的数据 + */ + public static String decryptData(String base64Data, String key) { + return SecureUtil.aes(md5(key).toLowerCase().getBytes()).decryptStr(base64Data); + } + + /** + * AES 加密 + * + * @param data 需要加密的数据 + * @param key 密钥 + * @return 加密后的数据 + */ + public static String encryptData(String data, String key) { + return SecureUtil.aes(md5(key).toLowerCase().getBytes()).encryptBase64(data.getBytes()); + } + + /** + * 把所有元素排序 + * + * @param params 需要排序并参与字符拼接的参数组 + * @return 拼接后字符串 + */ + public static String createLinkString(Map params) { + return createLinkString(params, false); + } + + /** + * @param params 需要排序并参与字符拼接的参数组 + * @param encode 是否进行URLEncoder + * @return 拼接后字符串 + */ + public static String createLinkString(Map params, boolean encode) { + return createLinkString(params, "&", encode); + } + + /** + * @param params 需要排序并参与字符拼接的参数组 + * @param connStr 连接符号 + * @param encode 是否进行URLEncoder + * @return 拼接后字符串 + */ + public static String createLinkString(Map params, String connStr, boolean encode) { + return createLinkString(params, connStr, encode, false); + } + + public static String createLinkString(Map params, String connStr, boolean encode, boolean quotes) { + List keys = new ArrayList<>(params.keySet()); + Collections.sort(keys); + StringBuilder content = new StringBuilder(); + for (int i = 0; i < keys.size(); i++) { + String key = keys.get(i); + String value = params.get(key); + // 拼接时,不包括最后一个&字符 + if (i == keys.size() - 1) { + if (quotes) { + content.append(key).append("=").append('"').append(encode ? urlEncode(value) : value).append('"'); + } else { + content.append(key).append("=").append(encode ? urlEncode(value) : value); + } + } else { + if (quotes) { + content.append(key).append("=").append('"').append(encode ? urlEncode(value) : value).append('"').append(connStr); + } else { + content.append(key).append("=").append(encode ? urlEncode(value) : value).append(connStr); + } + } + } + return content.toString(); + } + + + /** + * URL 编码 + * + * @param src 需要编码的字符串 + * @return 编码后的字符串 + */ + public static String urlEncode(String src) { + try { + return URLEncoder.encode(src, CharsetUtil.UTF_8).replace("+", "%20"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 遍历 Map 并构建 xml 数据 + * + * @param params 需要遍历的 Map + * @param prefix xml 前缀 + * @param suffix xml 后缀 + * @return xml 字符串 + */ + public static StringBuffer forEachMap(Map params, String prefix, String suffix) { + StringBuffer xml = new StringBuffer(); + if (StrUtil.isNotEmpty(prefix)) { + xml.append(prefix); + } + for (Map.Entry entry : params.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + // 略过空值 + if (StrUtil.isEmpty(value)) { + continue; + } + xml.append("<").append(key).append(">"); + xml.append(entry.getValue()); + xml.append(""); + } + if (StrUtil.isNotEmpty(suffix)) { + xml.append(suffix); + } + return xml; + } + + /** + * 微信下单 map to xml + * + * @param params Map 参数 + * @return xml 字符串 + */ + public static String toXml(Map params) { + StringBuffer xml = forEachMap(params, "", ""); + return xml.toString(); + } + + /** + * 针对支付的 xml,没有嵌套节点的简单处理 + * + * @param xmlStr xml 字符串 + * @return 转化后的 Map + */ + public static Map xmlToMap(String xmlStr) { + XmlHelper xmlHelper = XmlHelper.of(xmlStr); + return xmlHelper.toMap(); + } + + /** + * 构造签名串 + * + * @param method {@link RequestMethodEnums} GET,POST,PUT等 + * @param url 请求接口 /v3/certificates + * @param timestamp 获取发起请求时的系统当前时间戳 + * @param nonceStr 随机字符串 + * @param body 请求报文主体 + * @return 待签名字符串 + */ + public static String buildSignMessage(RequestMethodEnums method, String url, long timestamp, String nonceStr, String body) { + ArrayList arrayList = new ArrayList<>(); + arrayList.add(method.toString()); + arrayList.add(url); + arrayList.add(String.valueOf(timestamp)); + arrayList.add(nonceStr); + arrayList.add(body); + return buildSignMessage(arrayList); + } + + /** + * 构造签名串 + * + * @param timestamp 应答时间戳 + * @param nonceStr 应答随机串 + * @param body 应答报文主体 + * @return 应答待签名字符串 + */ + public static String buildSignMessage(String timestamp, String nonceStr, String body) { + ArrayList arrayList = new ArrayList<>(); + arrayList.add(timestamp); + arrayList.add(nonceStr); + arrayList.add(body); + return buildSignMessage(arrayList); + } + + /** + * 构造签名串 + * + * @param signMessage 待签名的参数 + * @return 构造后带待签名串 + */ + public static String buildSignMessage(ArrayList signMessage) { + if (signMessage == null || signMessage.size() <= 0) { + return null; + } + StringBuilder sbf = new StringBuilder(); + for (String str : signMessage) { + sbf.append(str).append("\n"); + } + return sbf.toString(); + } + + /** + * v3 接口创建签名 + * + * @param signMessage 待签名的参数 + * @param keyPath key.pem 证书路径 + * @return 生成 v3 签名 + * @throws Exception 异常信息 + */ + public static String createSign(ArrayList signMessage, String keyPath) throws Exception { + return createSign(buildSignMessage(signMessage), keyPath); + } + + /** + * v3 接口创建签名 + * + * @param signMessage 待签名的参数 + * @param privateKey 商户私钥 + * @return 生成 v3 签名 + * @throws Exception 异常信息 + */ + public static String createSign(ArrayList signMessage, PrivateKey privateKey) throws Exception { + return createSign(buildSignMessage(signMessage), privateKey); + } + + + /** + * v3 接口创建签名 + * + * @param signMessage 待签名的参数 + * @param keyPath key.pem 证书路径 + * @return 生成 v3 签名 + * @throws Exception 异常信息 + */ + public static String createSign(String signMessage, String keyPath) throws Exception { + if (StrUtil.isEmpty(signMessage)) { + return null; + } + // 获取商户私钥 + PrivateKey privateKey = PayKit.getPrivateKey(keyPath); + // 生成签名 + return RsaKit.encryptByPrivateKey(signMessage, privateKey); + } + + /** + * v3 接口创建签名 + * + * @param signMessage 待签名的参数 + * @param privateKey 商户私钥 + * @return 生成 v3 签名 + * @throws Exception 异常信息 + */ + public static String createSign(String signMessage, PrivateKey privateKey) throws Exception { + if (StrUtil.isEmpty(signMessage)) { + return null; + } + // 生成签名 + return RsaKit.encryptByPrivateKey(signMessage, privateKey); + } + + /** + * 获取授权认证信息 + * + * @param mchId 商户号 + * @param serialNo 商户API证书序列号 + * @param nonceStr 请求随机串 + * @param timestamp 时间戳 + * @param signature 签名值 + * @param authType 认证类型,目前为WECHATPAY2-SHA256-RSA2048 + * @return 请求头 Authorization + */ + public static String getAuthorization(String mchId, String serialNo, String nonceStr, String timestamp, String signature, String authType) { + Map params = new HashMap<>(5); + params.put("mchid", mchId); + params.put("serial_no", serialNo); + params.put("nonce_str", nonceStr); + params.put("timestamp", timestamp); + params.put("signature", signature); + return authType.concat(" ").concat(createLinkString(params, ",", false, true)); + } + + /** + * 获取商户私钥 + * + * @param keyPath 商户私钥证书路径 + * @return {@link String} 商户私钥 + * @throws Exception 异常信息 + */ + public static String getPrivateKeyStr(String keyPath) throws Exception { + return RsaKit.getPrivateKeyStr(getPrivateKey(keyPath)); + } + + /** + * 获取商户私钥 + * + * @param keyPath 商户私钥证书路径 + * @return {@link PrivateKey} 商户私钥 + * @throws Exception 异常信息 + */ + public static PrivateKey getPrivateKey(String keyPath) throws Exception { + String originalKey = FileUtil.readUtf8String(keyPath); + String privateKey = originalKey + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + + return RsaKit.loadPrivateKey(privateKey); + } + + /** + * 获取证书 + * + * @param inputStream 证书文件 + * @return {@link X509Certificate} 获取证书 + */ + public static X509Certificate getCertificate(InputStream inputStream) { + try { + CertificateFactory cf = CertificateFactory.getInstance("X509"); + X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream); + cert.checkValidity(); + return cert; + } catch (CertificateExpiredException e) { + throw new RuntimeException("证书已过期", e); + } catch (CertificateNotYetValidException e) { + throw new RuntimeException("证书尚未生效", e); + } catch (CertificateException e) { + throw new RuntimeException("无效的证书", e); + } + } + + /** + * 获取证书 + * + * @param publicKey 证书 + * @return {@link X509Certificate} 获取证书 + */ + public static X509Certificate getCertificate(String publicKey) { + try { + X509Certificate cert = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes())); + cert.checkValidity(); + return cert; + } catch (CertificateExpiredException e) { + throw new RuntimeException("证书已过期", e); + } catch (CertificateNotYetValidException e) { + throw new RuntimeException("证书尚未生效", e); + } + } + + /** + * 公钥加密 + * + * @param data 待加密数据 + * @param certificate 平台公钥证书 + * @return 加密后的数据 + * @throws Exception 异常信息 + */ + public static String rsaEncryptOAEP(String data, X509Certificate certificate) throws Exception { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey()); + + byte[] dataByte = data.getBytes(StandardCharsets.UTF_8); + byte[] cipherData = cipher.doFinal(dataByte); + return Base64.encode(cipherData); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("无效的证书", e); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new IllegalBlockSizeException("加密原串的长度不能超过214字节"); + } + } + + /** + * 私钥解密 + * + * @param cipherText 加密字符 + * @param privateKey 私钥 + * @return 解密后的数据 + * @throws Exception 异常信息 + */ + public static String rsaDecryptOAEP(String cipherText, PrivateKey privateKey) throws Exception { + try { + Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] data = Base64.decode(cipherText); + return new String(cipher.doFinal(data), StandardCharsets.UTF_8); + } catch (NoSuchPaddingException | NoSuchAlgorithmException e) { + throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("无效的私钥", e); + } catch (BadPaddingException | IllegalBlockSizeException e) { + throw new BadPaddingException("解密失败"); + } + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/QrCodeKit.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/QrCodeKit.java new file mode 100644 index 00000000..7291fa52 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/QrCodeKit.java @@ -0,0 +1,141 @@ +package cn.lili.modules.payment.kit.core.kit; + +import com.google.zxing.*; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; +import com.google.zxing.client.j2se.MatrixToImageConfig; +import com.google.zxing.client.j2se.MatrixToImageWriter; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.common.HybridBinarizer; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +/** + *

google 开源图形码工具类

+ * + * @author + */ +public class QrCodeKit { + /** + * 图形码生成工具 + * + * @param contents 内容 + * @param barcodeFormat BarcodeFormat对象 + * @param format 图片格式,可选[png,jpg,bmp] + * @param width 宽 + * @param height 高 + * @param margin 边框间距px + * @param saveImgFilePath 存储图片的完整位置,包含文件名 + * @return {boolean} + */ + public static boolean encode(String contents, BarcodeFormat barcodeFormat, Integer margin, + ErrorCorrectionLevel errorLevel, String format, int width, int height, String saveImgFilePath) { + boolean bool = false; + BufferedImage bufImg; + Map hints = new HashMap(3); + // 指定纠错等级 + hints.put(EncodeHintType.ERROR_CORRECTION, errorLevel); + hints.put(EncodeHintType.MARGIN, margin); + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); + try { + BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, barcodeFormat, width, height, hints); + MatrixToImageConfig config = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF); + bufImg = MatrixToImageWriter.toBufferedImage(bitMatrix, config); + bool = writeToFile(bufImg, format, saveImgFilePath); + } catch (Exception e) { + e.printStackTrace(); + } + return bool; + } + + /** + * @param outputStream 可以来自response,也可以来自文件 + * @param contents 内容 + * @param barcodeFormat BarcodeFormat对象 + * @param margin 图片格式,可选[png,jpg,bmp] + * @param errorLevel 纠错级别 一般为:ErrorCorrectionLevel.H + * @param format 图片格式,可选[png,jpg,bmp] + * @param width 宽 + * @param height 高 + */ + public static void encodeOutPutSteam(OutputStream outputStream, String contents, BarcodeFormat barcodeFormat, Integer margin, ErrorCorrectionLevel errorLevel, String format, int width, int height) throws IOException { + Map hints = new HashMap(3); + hints.put(EncodeHintType.ERROR_CORRECTION, errorLevel); + hints.put(EncodeHintType.MARGIN, margin); + hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); + + try { + BitMatrix bitMatrix = (new MultiFormatWriter()).encode(contents, barcodeFormat, width, height, hints); + MatrixToImageWriter.writeToStream(bitMatrix, format, outputStream); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (outputStream != null) { + outputStream.close(); + } + } + + } + + /** + * @param srcImgFilePath 要解码的图片地址 + * @return {Result} + */ + public static Result decode(String srcImgFilePath) { + Result result = null; + BufferedImage image; + try { + File srcFile = new File(srcImgFilePath); + image = ImageIO.read(srcFile); + if (null != image) { + LuminanceSource source = new BufferedImageLuminanceSource(image); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + + Hashtable hints = new Hashtable(); + hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); + result = new MultiFormatReader().decode(bitmap, hints); + } else { + throw new IllegalArgumentException("Could not decode image."); + } + } catch (Exception e) { + e.printStackTrace(); + } + return result; + } + + /** + * 将BufferedImage对象写入文件 + * + * @param bufImg BufferedImage对象 + * @param format 图片格式,可选[png,jpg,bmp] + * @param saveImgFilePath 存储图片的完整位置,包含文件名 + * @return {boolean} + */ + public static boolean writeToFile(BufferedImage bufImg, String format, String saveImgFilePath) { + boolean bool = false; + try { + bool = ImageIO.write(bufImg, format, new File(saveImgFilePath)); + } catch (Exception e) { + e.printStackTrace(); + } + return bool; + } + + public static void main(String[] args) { + String saveImgFilePath = "/Users//Documents/dev/IJPay/qrCode.png"; + boolean encode = encode("https://gitee.com/205/IJPay", BarcodeFormat.QR_CODE, 3, + ErrorCorrectionLevel.H, "png", 200, 200, saveImgFilePath); + if (encode) { + Result result = decode(saveImgFilePath); + String text = result.getText(); + System.out.println(text); + } + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/RsaKit.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/RsaKit.java new file mode 100644 index 00000000..f6d5f06a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/RsaKit.java @@ -0,0 +1,379 @@ +package cn.lili.modules.payment.kit.core.kit; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.StrUtil; + +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.*; +import java.util.HashMap; +import java.util.Map; + +/** + *

RSA 非对称加密工具类

+ * + * @author + */ +public class RsaKit { + + /** + * RSA最大加密明文大小 + */ + private static final int MAX_ENCRYPT_BLOCK = 117; + + /** + * RSA最大解密密文大小 + */ + private static final int MAX_DECRYPT_BLOCK = 128; + + /** + * 加密算法RSA + */ + private static final String KEY_ALGORITHM = "RSA"; + + /** + * 生成公钥和私钥 + * + * @throws Exception 异常信息 + */ + public static Map getKeys() throws Exception { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); + keyPairGen.initialize(1024); + KeyPair keyPair = keyPairGen.generateKeyPair(); + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); + + String publicKeyStr = getPublicKeyStr(publicKey); + String privateKeyStr = getPrivateKeyStr(privateKey); + + Map map = new HashMap(2); + map.put("publicKey", publicKeyStr); + map.put("privateKey", privateKeyStr); + + System.out.println("公钥\r\n" + publicKeyStr); + System.out.println("私钥\r\n" + privateKeyStr); + return map; + } + + /** + * 使用模和指数生成RSA公钥 + * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA + * /None/NoPadding】 + * + * @param modulus 模 + * @param exponent 公钥指数 + * @return {@link RSAPublicKey} + */ + public static RSAPublicKey getPublicKey(String modulus, String exponent) { + try { + BigInteger b1 = new BigInteger(modulus); + BigInteger b2 = new BigInteger(exponent); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2); + return (RSAPublicKey) keyFactory.generatePublic(keySpec); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 使用模和指数生成RSA私钥 + * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA + * /None/NoPadding】 + * + * @param modulus 模 + * @param exponent 指数 + * @return {@link RSAPrivateKey} + */ + public static RSAPrivateKey getPrivateKey(String modulus, String exponent) { + try { + BigInteger b1 = new BigInteger(modulus); + BigInteger b2 = new BigInteger(exponent); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2); + return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 公钥加密 + * + * @param data 需要加密的数据 + * @param publicKey 公钥 + * @return 加密后的数据 + * @throws Exception 异常信息 + */ + public static String encryptByPublicKey(String data, String publicKey) throws Exception { + return encryptByPublicKey(data, publicKey, "RSA/ECB/PKCS1Padding"); + } + + /** + * 公钥加密 + * + * @param data 需要加密的数据 + * @param publicKey 公钥 + * @return 加密后的数据 + * @throws Exception 异常信息 + */ + public static String encryptByPublicKeyByWx(String data, String publicKey) throws Exception { + return encryptByPublicKey(data, publicKey, "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"); + } + + /** + * 公钥加密 + * + * @param data 需要加密的数据 + * @param publicKey 公钥 + * @param fillMode 填充模式 + * @return 加密后的数据 + * @throws Exception 异常信息 + */ + public static String encryptByPublicKey(String data, String publicKey, String fillMode) throws Exception { + byte[] dataByte = data.getBytes(StandardCharsets.UTF_8); + byte[] keyBytes = Base64.decode(publicKey); + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key key = keyFactory.generatePublic(x509KeySpec); + // 对数据加密 + Cipher cipher = Cipher.getInstance(fillMode); + cipher.init(Cipher.ENCRYPT_MODE, key); + int inputLen = dataByte.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段加密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > MAX_ENCRYPT_BLOCK) { + cache = cipher.doFinal(dataByte, offSet, MAX_ENCRYPT_BLOCK); + } else { + cache = cipher.doFinal(dataByte, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * MAX_ENCRYPT_BLOCK; + } + byte[] encryptedData = out.toByteArray(); + out.close(); + return StrUtil.str(Base64.encode(encryptedData)); + } + + /** + * 私钥签名 + * + * @param data 需要加密的数据 + * @param privateKey 私钥 + * @return 加密后的数据 + * @throws Exception 异常信息 + */ + public static String encryptByPrivateKey(String data, String privateKey) throws Exception { + PKCS8EncodedKeySpec priPkcs8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey)); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + PrivateKey priKey = keyFactory.generatePrivate(priPkcs8); + Signature signature = Signature.getInstance("SHA256WithRSA"); + + signature.initSign(priKey); + signature.update(data.getBytes(StandardCharsets.UTF_8)); + byte[] signed = signature.sign(); + return StrUtil.str(Base64.encode(signed)); + } + + /** + * 私钥签名 + * + * @param data 需要加密的数据 + * @param privateKey 私钥 + * @return 加密后的数据 + * @throws Exception 异常信息 + */ + public static String encryptByPrivateKey(String data, PrivateKey privateKey) throws Exception { + Signature signature = Signature.getInstance("SHA256WithRSA"); + signature.initSign(privateKey); + signature.update(data.getBytes(StandardCharsets.UTF_8)); + byte[] signed = signature.sign(); + return StrUtil.str(Base64.encode(signed)); + } + + /** + * 公钥验证签名 + * + * @param data 需要加密的数据 + * @param sign 签名 + * @param publicKey 公钥 + * @return 验证结果 + * @throws Exception 异常信息 + */ + public static boolean checkByPublicKey(String data, String sign, String publicKey) throws Exception { + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + byte[] encodedKey = Base64.decode(publicKey); + PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); + Signature signature = Signature.getInstance("SHA256WithRSA"); + signature.initVerify(pubKey); + signature.update(data.getBytes(StandardCharsets.UTF_8)); + return signature.verify(Base64.decode(sign.getBytes(StandardCharsets.UTF_8))); + } + + /** + * 公钥验证签名 + * + * @param data 需要加密的数据 + * @param sign 签名 + * @param publicKey 公钥 + * @return 验证结果 + * @throws Exception 异常信息 + */ + public static boolean checkByPublicKey(String data, String sign, PublicKey publicKey) throws Exception { + Signature signature = Signature.getInstance("SHA256WithRSA"); + signature.initVerify(publicKey); + signature.update(data.getBytes(StandardCharsets.UTF_8)); + return signature.verify(Base64.decode(sign.getBytes(StandardCharsets.UTF_8))); + } + + /** + * 私钥解密 + * + * @param data 需要解密的数据 + * @param privateKey 私钥 + * @return 解密后的数据 + * @throws Exception 异常信息 + */ + public static String decryptByPrivateKey(String data, String privateKey) throws Exception { + return decryptByPrivateKey(data, privateKey, "RSA/ECB/PKCS1Padding"); + } + + /** + * 私钥解密 + * + * @param data 需要解密的数据 + * @param privateKey 私钥 + * @return 解密后的数据 + * @throws Exception 异常信息 + */ + public static String decryptByPrivateKeyByWx(String data, String privateKey) throws Exception { + return decryptByPrivateKey(data, privateKey, "RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"); + } + + /** + * 私钥解密 + * + * @param data 需要解密的数据 + * @param privateKey 私钥 + * @param fillMode 填充模式 + * @return 解密后的数据 + * @throws Exception 异常信息 + */ + public static String decryptByPrivateKey(String data, String privateKey, String fillMode) throws Exception { + byte[] encryptedData = Base64.decode(data); + byte[] keyBytes = Base64.decode(privateKey); + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key key = keyFactory.generatePrivate(pkcs8KeySpec); + Cipher cipher = Cipher.getInstance(fillMode); + + cipher.init(Cipher.DECRYPT_MODE, key); + int inputLen = encryptedData.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offSet = 0; + byte[] cache; + int i = 0; + // 对数据分段解密 + while (inputLen - offSet > 0) { + if (inputLen - offSet > MAX_DECRYPT_BLOCK) { + cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK); + } else { + cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet); + } + out.write(cache, 0, cache.length); + i++; + offSet = i * MAX_DECRYPT_BLOCK; + } + byte[] decryptedData = out.toByteArray(); + out.close(); + return new String(decryptedData); + } + + /** + * 从字符串中加载公钥 + * + * @param publicKeyStr 公钥数据字符串 + * @throws Exception 异常信息 + */ + public static PublicKey loadPublicKey(String publicKeyStr) throws Exception { + try { + byte[] buffer = Base64.decode(publicKeyStr); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer); + return keyFactory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此算法"); + } catch (InvalidKeySpecException e) { + throw new Exception("公钥非法"); + } catch (NullPointerException e) { + throw new Exception("公钥数据为空"); + } + } + + /** + * 从字符串中加载私钥
+ * 加载时使用的是PKCS8EncodedKeySpec(PKCS#8编码的Key指令)。 + * + * @param privateKeyStr 私钥 + * @return {@link PrivateKey} + * @throws Exception 异常信息 + */ + public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception { + try { + byte[] buffer = Base64.decode(privateKeyStr); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + return keyFactory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此算法"); + } catch (InvalidKeySpecException e) { + throw new Exception("私钥非法"); + } catch (NullPointerException e) { + throw new Exception("私钥数据为空"); + } + } + + public static String getPrivateKeyStr(PrivateKey privateKey) { + return Base64.encode(privateKey.getEncoded()); + } + + public static String getPublicKeyStr(PublicKey publicKey) { + return Base64.encode(publicKey.getEncoded()); + } + + public static void main(String[] args) throws Exception { + Map keys = getKeys(); + String publicKey = keys.get("publicKey"); + String privateKey = keys.get("privateKey"); + String content = "我是,I am "; + String encrypt = encryptByPublicKey(content, publicKey); + String decrypt = decryptByPrivateKey(encrypt, privateKey); + System.out.println("加密之后:" + encrypt); + System.out.println("解密之后:" + decrypt); + + System.out.println("======华丽的分割线========="); + + content = "我是,I am "; + encrypt = encryptByPublicKeyByWx(content, publicKey); + decrypt = decryptByPrivateKeyByWx(encrypt, privateKey); + System.out.println("加密之后:" + encrypt); + System.out.println("解密之后:" + decrypt); + + //OPPO + String sign = encryptByPrivateKey(content, privateKey); + System.out.println("加密之后:" + sign); + System.out.println(checkByPublicKey(content, sign, publicKey)); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/WxPayKit.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/WxPayKit.java new file mode 100644 index 00000000..2d23e1fc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/kit/WxPayKit.java @@ -0,0 +1,689 @@ +package cn.lili.modules.payment.kit.core.kit; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.modules.payment.kit.core.PaymentHttpResponse; +import cn.lili.modules.payment.kit.core.enums.RequestMethodEnums; +import cn.lili.modules.payment.kit.core.enums.SignType; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + + +/** + *

微信支付工具类

+ * + * @author + */ +public class WxPayKit { + private static final String FIELD_SIGN = "sign"; + private static final String FIELD_SIGN_TYPE = "sign_type"; + + public static String hmacSha256(String data, String key) { + return PayKit.hmacSha256(data, key); + } + + public static String md5(String data) { + return PayKit.md5(data); + } + + /** + * AES 解密 + * + * @param base64Data 需要解密的数据 + * @param key 密钥 + * @return 解密后的数据 + */ + public static String decryptData(String base64Data, String key) { + return PayKit.decryptData(base64Data, key); + } + + /** + * AES 加密 + * + * @param data 需要加密的数据 + * @param key 密钥 + * @return 加密后的数据 + */ + public static String encryptData(String data, String key) { + return PayKit.encryptData(data, key); + } + + public static String generateStr() { + return IdUtil.fastSimpleUUID(); + } + + + /** + * 支付异步通知时校验 sign + * + * @param params 参数 + * @param partnerKey 支付密钥 + * @return {boolean} + */ + public static boolean verifyNotify(Map params, String partnerKey) { + String sign = params.get("sign"); + String localSign = createSign(params, partnerKey, SignType.MD5); + return sign.equals(localSign); + } + + /** + * 支付异步通知时校验 sign + * + * @param params 参数 + * @param partnerKey 支付密钥 + * @param signType {@link SignType} + * @return {@link Boolean} 验证签名结果 + */ + public static boolean verifyNotify(Map params, String partnerKey, SignType signType) { + String sign = params.get("sign"); + String localSign = createSign(params, partnerKey, signType); + return sign.equals(localSign); + } + + /** + * 生成签名 + * + * @param params 需要签名的参数 + * @param partnerKey 密钥 + * @param signType 签名类型 + * @return 签名后的数据 + */ + public static String createSign(Map params, String partnerKey, SignType signType) { + if (signType == null) { + signType = SignType.MD5; + } + // 生成签名前先去除sign + params.remove(FIELD_SIGN); + String tempStr = PayKit.createLinkString(params); + String stringSignTemp = tempStr + "&key=" + partnerKey; + if (signType == SignType.MD5) { + return md5(stringSignTemp).toUpperCase(); + } else { + return hmacSha256(stringSignTemp, partnerKey).toUpperCase(); + } + } + + /** + * 生成签名 + * + * @param params 需要签名的参数 + * @param secret 企业微信支付应用secret + * @return 签名后的数据 + */ + public static String createSign(Map params, String secret) { + // 生成签名前先去除sign + params.remove(FIELD_SIGN); + String tempStr = PayKit.createLinkString(params); + String stringSignTemp = tempStr + "&secret=" + secret; + return md5(stringSignTemp).toUpperCase(); + } + + /** + * 构建签名 + * + * @param params 需要签名的参数 + * @param partnerKey 密钥 + * @param signType 签名类型 + * @return 签名后的 Map + */ + public static Map buildSign(Map params, String partnerKey, SignType signType) { + return buildSign(params, partnerKey, signType, true); + } + + /** + * 构建签名 + * + * @param params 需要签名的参数 + * @param partnerKey 密钥 + * @param signType 签名类型 + * @param haveSignType 签名是否包含 sign_type 字段 + * @return 签名后的 Map + */ + public static Map buildSign(Map params, String partnerKey, SignType signType, boolean haveSignType) { + if (haveSignType) { + params.put(FIELD_SIGN_TYPE, signType.getType()); + } + String sign = createSign(params, partnerKey, signType); + params.put(FIELD_SIGN, sign); + return params; + } + + public static StringBuffer forEachMap(Map params, String prefix, String suffix) { + return PayKit.forEachMap(params, prefix, suffix); + } + + /** + * 微信下单 map to xml + * + * @param params Map 参数 + * @return xml 字符串 + */ + public static String toXml(Map params) { + return PayKit.toXml(params); + } + + /** + * 针对支付的 xml,没有嵌套节点的简单处理 + * + * @param xmlStr xml 字符串 + * @return 转化后的 Map + */ + public static Map xmlToMap(String xmlStr) { + return PayKit.xmlToMap(xmlStr); + } + + /** + *

生成二维码链接

+ *

原生支付接口模式一(扫码模式一)

+ * + * @param sign 签名 + * @param appId 公众账号ID + * @param mchId 商户号 + * @param productId 商品ID + * @param timeStamp 时间戳 + * @param nonceStr 随机字符串 + * @return {String} + */ + public static String bizPayUrl(String sign, String appId, String mchId, String productId, String timeStamp, String nonceStr) { + String rules = "weixin://wxpay/bizpayurl?sign=Temp&appid=Temp&mch_id=Temp&product_id=Temp&time_stamp=Temp&nonce_str=Temp"; + return replace(rules, "Temp", sign, appId, mchId, productId, timeStamp, nonceStr); + } + + /** + *

生成二维码链接

+ *

原生支付接口模式一(扫码模式一)

+ * + * @param partnerKey 密钥 + * @param appId 公众账号ID + * @param mchId 商户号 + * @param productId 商品ID + * @param timeStamp 时间戳 + * @param nonceStr 随机字符串 + * @param signType 签名类型 + * @return {String} + */ + public static String bizPayUrl(String partnerKey, String appId, String mchId, String productId, String timeStamp, String nonceStr, SignType signType) { + HashMap map = new HashMap<>(5); + map.put("appid", appId); + map.put("mch_id", mchId); + map.put("time_stamp", StrUtil.isEmpty(timeStamp) ? Long.toString(System.currentTimeMillis() / 1000) : timeStamp); + map.put("nonce_str", StrUtil.isEmpty(nonceStr) ? IdUtil.fastSimpleUUID() : nonceStr); + map.put("product_id", productId); + return bizPayUrl(createSign(map, partnerKey, signType), appId, mchId, productId, timeStamp, nonceStr); + } + + /** + *

生成二维码链接

+ *

原生支付接口模式一(扫码模式一)

+ * + * @param partnerKey 密钥 + * @param appId 公众账号ID + * @param mchId 商户号 + * @param productId 商品ID + * @return {String} + */ + public static String bizPayUrl(String partnerKey, String appId, String mchId, String productId) { + String timeStamp = Long.toString(System.currentTimeMillis() / 1000); + String nonceStr = IdUtil.fastSimpleUUID(); + HashMap map = new HashMap<>(5); + map.put("appid", appId); + map.put("mch_id", mchId); + map.put("time_stamp", timeStamp); + map.put("nonce_str", nonceStr); + map.put("product_id", productId); + return bizPayUrl(createSign(map, partnerKey, null), appId, mchId, productId, timeStamp, nonceStr); + } + + + /** + * 替换url中的参数 + * + * @param str 原始字符串 + * @param regex 表达式 + * @param args 替换字符串 + * @return {String} + */ + public static String replace(String str, String regex, String... args) { + for (String arg : args) { + str = str.replaceFirst(regex, arg); + } + return str; + } + + /** + * 判断接口返回的 code + * + * @param codeValue code 值 + * @return 是否是 SUCCESS + */ + public static boolean codeIsOk(String codeValue) { + return StrUtil.isNotEmpty(codeValue) && "SUCCESS".equals(codeValue); + } + + /** + *

公众号支付-预付订单再次签名

+ *

注意此处签名方式需与统一下单的签名类型一致

+ * + * @param prepayId 预付订单号 + * @param appId 应用编号 + * @param partnerKey API Key + * @param signType 签名方式 + * @return 再次签名后的 Map + */ + public static Map prepayIdCreateSign(String prepayId, String appId, String partnerKey, SignType signType) { + Map packageParams = new HashMap<>(6); + packageParams.put("appId", appId); + packageParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); + packageParams.put("nonceStr", String.valueOf(System.currentTimeMillis())); + packageParams.put("package", "prepay_id=" + prepayId); + if (signType == null) { + signType = SignType.MD5; + } + packageParams.put("signType", signType.getType()); + String packageSign = WxPayKit.createSign(packageParams, partnerKey, signType); + packageParams.put("paySign", packageSign); + return packageParams; + } + + /** + * JS 调起支付签名 + * + * @param appId 应用编号 + * @param prepayId 预付订单号 + * @param keyPath key.pem 证书路径 + * @return 唤起支付需要的参数 + * @throws Exception 错误信息 + */ + public static Map jsApiCreateSign(String appId, String prepayId, String keyPath) throws Exception { + String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = String.valueOf(System.currentTimeMillis()); + String packageStr = "prepay_id=" + prepayId; + Map packageParams = new HashMap<>(6); + packageParams.put("appId", appId); + packageParams.put("timeStamp", timeStamp); + packageParams.put("nonceStr", nonceStr); + packageParams.put("package", packageStr); + packageParams.put("signType", SignType.RSA.toString()); + ArrayList list = new ArrayList<>(); + list.add(appId); + list.add(timeStamp); + list.add(nonceStr); + list.add(packageStr); + String packageSign = PayKit.createSign( + PayKit.buildSignMessage(list), + keyPath + ); + packageParams.put("paySign", packageSign); + return packageParams; + } + + /** + *

APP 支付-预付订单再次签名

+ *

注意此处签名方式需与统一下单的签名类型一致

+ * + * @param appId 应用编号 + * @param partnerId 商户号 + * @param prepayId 预付订单号 + * @param partnerKey API Key + * @param signType 签名方式 + * @return 再次签名后的 Map + */ + public static Map appPrepayIdCreateSign(String appId, String partnerId, String prepayId, String partnerKey, SignType signType) { + Map packageParams = new HashMap<>(8); + packageParams.put("appid", appId); + packageParams.put("partnerid", partnerId); + packageParams.put("prepayid", prepayId); + packageParams.put("package", "Sign=WXPay"); + packageParams.put("noncestr", String.valueOf(System.currentTimeMillis())); + packageParams.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000)); + if (signType == null) { + signType = SignType.MD5; + } + String packageSign = createSign(packageParams, partnerKey, signType); + packageParams.put("sign", packageSign); + return packageParams; + } + + /** + * App 调起支付签名 + * + * @param appId 应用编号 + * @param partnerId 商户编号 + * @param prepayId 预付订单号 + * @param keyPath key.pem 证书路径 + * @return 唤起支付需要的参数 + * @throws Exception 错误信息 + */ + public static Map appCreateSign(String appId, String partnerId, String prepayId, String keyPath) throws Exception { + String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = String.valueOf(System.currentTimeMillis()); + Map packageParams = new HashMap<>(8); + packageParams.put("appId", appId); + packageParams.put("partnerid", partnerId); + packageParams.put("prepayid", prepayId); + packageParams.put("package", "Sign=WXPay"); + packageParams.put("timeStamp", timeStamp); + packageParams.put("nonceStr", nonceStr); + packageParams.put("signType", SignType.RSA.toString()); + ArrayList list = new ArrayList<>(); + list.add(appId); + list.add(timeStamp); + list.add(nonceStr); + list.add(prepayId); + String packageSign = PayKit.createSign( + PayKit.buildSignMessage(list), + keyPath + ); + packageParams.put("sign", packageSign); + return packageParams; + } + + /** + *

小程序-预付订单再次签名

+ *

注意此处签名方式需与统一下单的签名类型一致

+ * + * @param appId 应用编号 + * @param prepayId 预付订单号 + * @param partnerKey API Key + * @param signType 签名方式 + * @return 再次签名后的 Map + */ + public static Map miniAppPrepayIdCreateSign(String appId, String prepayId, String partnerKey, SignType signType) { + Map packageParams = new HashMap<>(6); + packageParams.put("appId", appId); + packageParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); + packageParams.put("nonceStr", String.valueOf(System.currentTimeMillis())); + packageParams.put("package", "prepay_id=" + prepayId); + if (signType == null) { + signType = SignType.MD5; + } + packageParams.put("signType", signType.getType()); + String packageSign = createSign(packageParams, partnerKey, signType); + packageParams.put("paySign", packageSign); + return packageParams; + } + + /** + * 构建 v3 接口所需的 Authorization + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param keyPath key.pem 证书路径 + * @param body 接口请求参数 + * @param nonceStr 随机字符库 + * @param timestamp 时间戳 + * @param authType 认证类型 + * @return {@link String} 返回 v3 所需的 Authorization + * @throws Exception 异常信息 + */ + public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId, + String serialNo, String keyPath, String body, String nonceStr, + long timestamp, String authType) throws Exception { + // 构建签名参数 + String buildSignMessage = PayKit.buildSignMessage(method, urlSuffix, timestamp, nonceStr, body); + String signature = PayKit.createSign(buildSignMessage, keyPath); + // 根据平台规则生成请求头 authorization + return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType); + } + + /** + * 构建 v3 接口所需的 Authorization + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param privateKey 商户私钥 + * @param body 接口请求参数 + * @param nonceStr 随机字符库 + * @param timestamp 时间戳 + * @param authType 认证类型 + * @return {@link String} 返回 v3 所需的 Authorization + * @throws Exception 异常信息 + */ + public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId, + String serialNo, PrivateKey privateKey, String body, String nonceStr, + long timestamp, String authType) throws Exception { + // 构建签名参数 + String buildSignMessage = PayKit.buildSignMessage(method, urlSuffix, timestamp, nonceStr, body); + String signature = PayKit.createSign(buildSignMessage, privateKey); + // 根据平台规则生成请求头 authorization + return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType); + } + + /** + * 构建 v3 接口所需的 Authorization + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param keyPath key.pem 证书路径 + * @param body 接口请求参数 + * @return {@link String} 返回 v3 所需的 Authorization + * @throws Exception 异常信息 + */ + public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId, + String serialNo, String keyPath, String body) throws Exception { + + long timestamp = System.currentTimeMillis() / 1000; + String authType = "WECHATPAY2-SHA256-RSA2048"; + String nonceStr = IdUtil.fastSimpleUUID(); + + return buildAuthorization(method, urlSuffix, mchId, serialNo, keyPath, body, nonceStr, timestamp, authType); + } + + /** + * 构建 v3 接口所需的 Authorization + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlSuffix 可通过 WxApiType 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param privateKey key.pem 证书路径 + * @param body 接口请求参数 + * @return {@link String} 返回 v3 所需的 Authorization + * @throws Exception 异常信息 + */ + public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId, + String serialNo, PrivateKey privateKey, String body) throws Exception { + + long timestamp = System.currentTimeMillis() / 1000; + String authType = "WECHATPAY2-SHA256-RSA2048"; + String nonceStr = IdUtil.fastSimpleUUID(); + + return buildAuthorization(method, urlSuffix, mchId, serialNo, privateKey, body, nonceStr, timestamp, authType); + } + + /** + * 验证签名 + * + * @param response 接口请求返回的 {@link PaymentHttpResponse} + * @param certPath 平台证书路径 + * @return 签名结果 + * @throws Exception 异常信息 + */ + public static boolean verifySignature(PaymentHttpResponse response, String certPath) throws Exception { + String timestamp = response.getHeader("Wechatpay-Timestamp"); + String nonceStr = response.getHeader("Wechatpay-Nonce"); + String signature = response.getHeader("Wechatpay-Signature"); + String body = response.getBody(); + return verifySignature(signature, body, nonceStr, timestamp, FileUtil.getInputStream(certPath)); + } + + /** + * 验证签名 + * + * @param response 接口请求返回的 {@link PaymentHttpResponse} + * @param cert 平台证书 + * @return 签名结果 + * @throws Exception 异常信息 + */ + public static boolean verifySignature(PaymentHttpResponse response, X509Certificate cert) throws Exception { + String timestamp = response.getHeader("Wechatpay-Timestamp"); + String nonceStr = response.getHeader("Wechatpay-Nonce"); + String signature = response.getHeader("Wechatpay-Signature"); + String body = response.getBody(); + return verifySignature(signature, body, nonceStr, timestamp, cert); + } + + /** + * 验证签名 + * + * @param signature 待验证的签名 + * @param body 应答主体 + * @param nonce 随机串 + * @param timestamp 时间戳 + * @param publicKey 微信支付平台公钥 + * @return 签名结果 + * @throws Exception 异常信息 + */ + public static boolean verifySignature(String signature, String body, String nonce, String timestamp, String publicKey) throws Exception { + String buildSignMessage = PayKit.buildSignMessage(timestamp, nonce, body); + return RsaKit.checkByPublicKey(buildSignMessage, signature, publicKey); + } + + /** + * 验证签名 + * + * @param signature 待验证的签名 + * @param body 应答主体 + * @param nonce 随机串 + * @param timestamp 时间戳 + * @param publicKey {@link PublicKey} 微信支付平台公钥 + * @return 签名结果 + * @throws Exception 异常信息 + */ + public static boolean verifySignature(String signature, String body, String nonce, String timestamp, PublicKey publicKey) throws Exception { + String buildSignMessage = PayKit.buildSignMessage(timestamp, nonce, body); + return RsaKit.checkByPublicKey(buildSignMessage, signature, publicKey); + } + + /** + * 验证签名 + * + * @param signature 待验证的签名 + * @param body 应答主体 + * @param nonce 随机串 + * @param timestamp 时间戳 + * @param certInputStream 微信支付平台证书输入流 + * @return 签名结果 + * @throws Exception 异常信息 + */ + public static boolean verifySignature(String signature, String body, String nonce, String timestamp, InputStream certInputStream) throws Exception { + String buildSignMessage = PayKit.buildSignMessage(timestamp, nonce, body); + // 获取证书 + X509Certificate certificate = PayKit.getCertificate(certInputStream); + PublicKey publicKey = certificate.getPublicKey(); + return RsaKit.checkByPublicKey(buildSignMessage, signature, publicKey); + } + + /** + * 验证签名 + * + * @param signature 待验证的签名 + * @param body 应答主体 + * @param nonce 随机串 + * @param timestamp 时间戳 + * @param certificate 微信支付平台证书 + * @return 签名结果 + * @throws Exception 异常信息 + */ + public static boolean verifySignature(String signature, String body, String nonce, String timestamp, X509Certificate certificate) throws Exception { + String buildSignMessage = PayKit.buildSignMessage(timestamp, nonce, body); + PublicKey publicKey = certificate.getPublicKey(); + return RsaKit.checkByPublicKey(buildSignMessage, signature, publicKey); + } + + /** + * v3 支付异步通知验证签名 + * + * @param serialNo 证书序列号 + * @param body 异步通知密文 + * @param signature 签名 + * @param nonce 随机字符串 + * @param timestamp 时间戳 + * @param key api 密钥 + * @param certPath 平台证书路径 + * @return 异步通知明文 + * @throws Exception 异常信息 + */ + public static String verifyNotify(String serialNo, String body, String signature, String nonce, + String timestamp, String key, String certPath) throws Exception { + BufferedInputStream inputStream = FileUtil.getInputStream(certPath); + // 获取平台证书序列号 + X509Certificate certificate = PayKit.getCertificate(inputStream); + String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase(); + System.out.println(serialNumber); + // 验证证书序列号 + if (serialNumber.equals(serialNo)) { + boolean verifySignature = WxPayKit.verifySignature(signature, body, nonce, timestamp, certificate.getPublicKey()); + if (verifySignature) { + JSONObject resultObject = JSONUtil.parseObj(body); + JSONObject resource = resultObject.getJSONObject("resource"); + String cipherText = resource.getStr("ciphertext"); + String nonceStr = resource.getStr("nonce"); + String associatedData = resource.getStr("associated_data"); + + AesUtil aesUtil = new AesUtil(key.getBytes(StandardCharsets.UTF_8)); + // 密文解密 + return aesUtil.decryptToString( + associatedData.getBytes(StandardCharsets.UTF_8), + nonceStr.getBytes(StandardCharsets.UTF_8), + cipherText + ); + } + } + return null; + } + + /** + * v3 支付异步通知验证签名 + * + * @param serialNo 证书序列号 + * @param body 异步通知密文 + * @param signature 签名 + * @param nonce 随机字符串 + * @param timestamp 时间戳 + * @param key api 密钥 + * @return 异步通知明文 + * @throws Exception 异常信息 + */ + public static String verifyNotify(String serialNo, String body, String signature, String nonce, + String timestamp, String key, X509Certificate certificate) throws Exception { + String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase(); + // 验证证书序列号 + if (serialNumber.equals(serialNo)) { + boolean verifySignature = WxPayKit.verifySignature(signature, body, nonce, timestamp, certificate.getPublicKey()); + if (verifySignature) { + JSONObject resultObject = JSONUtil.parseObj(body); + JSONObject resource = resultObject.getJSONObject("resource"); + String cipherText = resource.getStr("ciphertext"); + String nonceStr = resource.getStr("nonce"); + String associatedData = resource.getStr("associated_data"); + + AesUtil aesUtil = new AesUtil(key.getBytes(StandardCharsets.UTF_8)); + // 密文解密 + return aesUtil.decryptToString( + associatedData.getBytes(StandardCharsets.UTF_8), + nonceStr.getBytes(StandardCharsets.UTF_8), + cipherText + ); + } + } + return null; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/core/utils/DateTimeZoneUtil.java b/framework/src/main/java/cn/lili/modules/payment/kit/core/utils/DateTimeZoneUtil.java new file mode 100644 index 00000000..ea28af4c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/core/utils/DateTimeZoneUtil.java @@ -0,0 +1,84 @@ +package cn.lili.modules.payment.kit.core.utils; + +import cn.hutool.core.util.StrUtil; +import com.xkzhangsan.time.converter.DateTimeConverterUtil; +import com.xkzhangsan.time.formatter.DateTimeFormatterUtil; + +import java.io.Serializable; +import java.time.ZonedDateTime; +import java.util.Date; + + +/** + * 时间工具类 + * 依赖 xk-time + * + * @author YunGouOS + */ +public class DateTimeZoneUtil implements Serializable { + + private static final long serialVersionUID = -1331008203306650395L; + + /** + * 时间转 TimeZone + *

+ * 2020-08-17T16:46:37+08:00 + * + * @param time 时间戳 + * @return {@link String} TimeZone 格式时间字符串 + * @throws Exception 异常信息 + */ + public static String dateToTimeZone(long time) throws Exception { + return dateToTimeZone(new Date(time)); + } + + /** + * 时间转 TimeZone + *

+ * 2020-08-17T16:46:37+08:00 + * + * @param date {@link Date} + * @return {@link String} TimeZone 格式时间字符串 + * @throws Exception 异常信息 + */ + public static String dateToTimeZone(Date date) throws Exception { + String time; + if (date == null) { + throw new Exception("date is not null"); + } + ZonedDateTime zonedDateTime = DateTimeConverterUtil.toZonedDateTime(date); + time = DateTimeFormatterUtil.format(zonedDateTime, DateTimeFormatterUtil.YYYY_MM_DD_T_HH_MM_SS_XXX_FMT); + return time; + } + + /** + * TimeZone 时间转标准时间 + *

+ * 2020-08-17T16:46:37+08:00 to 2020-08-17 16:46:37 + * + * @param str TimeZone格式时间字符串 + * @return {@link String} 标准时间字符串 + * @throws Exception 异常信息 + */ + public static String timeZoneDateToStr(String str) throws Exception { + String time; + if (StrUtil.isBlank(str)) { + throw new Exception("str is not null"); + } + ZonedDateTime zonedDateTime = DateTimeFormatterUtil.parseToZonedDateTime(str, DateTimeFormatterUtil.YYYY_MM_DD_T_HH_MM_SS_XXX_FMT); + if (zonedDateTime == null) { + throw new Exception("str to zonedDateTime fail"); + } + time = zonedDateTime.format(DateTimeFormatterUtil.YYYY_MM_DD_HH_MM_SS_FMT); + return time; + } + + + public static void main(String[] args) throws Exception { + String timeZone = dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); + String timeZone2 = dateToTimeZone(new Date()); + System.out.println(timeZone + " " + timeZone2); + String date = timeZoneDateToStr(timeZone); + System.out.println(date); + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/dto/PayParam.java b/framework/src/main/java/cn/lili/modules/payment/kit/dto/PayParam.java new file mode 100644 index 00000000..c743ca1d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/dto/PayParam.java @@ -0,0 +1,34 @@ +package cn.lili.modules.payment.kit.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +/** + * 支付参数 + * + * @author Chopper + * @date 2020/12/19 11:46 + */ +@Data +@ToString +public class PayParam { + + + @NotNull + @ApiModelProperty(value = "交易类型", allowableValues = "TRADE,ORDER,RECHARGE") + private String orderType; + + @NotNull + @ApiModelProperty(value = "订单号") + private String sn; + + @NotNull + @ApiModelProperty(value = "客户端类型") + private String clientType; + + + +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/dto/PaymentSuccessParams.java b/framework/src/main/java/cn/lili/modules/payment/kit/dto/PaymentSuccessParams.java new file mode 100644 index 00000000..fc356986 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/dto/PaymentSuccessParams.java @@ -0,0 +1,37 @@ +package cn.lili.modules.payment.kit.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * PaymentSuccessParams + * + * @author Chopper + * @version v1.0 + * 2021-04-27 16:24 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PaymentSuccessParams { + + /** + * 支付方式 + */ + private String paymentMethod; + /** + * 第三方流水 + */ + private String receivableNo; + + /** + * 支付金额 + */ + private Double price; + + /** + * 支付参数 + */ + private PayParam payParam; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/enums/CashierEnum.java b/framework/src/main/java/cn/lili/modules/payment/kit/enums/CashierEnum.java new file mode 100644 index 00000000..f5acc84f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/enums/CashierEnum.java @@ -0,0 +1,23 @@ +package cn.lili.modules.payment.kit.enums; + +/** + * 订单类型 + * + * @author Chopper + * @date 2020-12-19 11:50 + */ +public enum CashierEnum { + + /** + * 订单 + */ + ORDER, + /** + * 交易 + */ + TRADE, + /** + * 充值 + */ + RECHARGE +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/enums/PaymentClientEnum.java b/framework/src/main/java/cn/lili/modules/payment/kit/enums/PaymentClientEnum.java new file mode 100644 index 00000000..325cf6e0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/enums/PaymentClientEnum.java @@ -0,0 +1,31 @@ +package cn.lili.modules.payment.kit.enums; + +/** + * 支付方式枚举 + * + * @author Chopper + * @date 2020/12/18 18:08 + */ +public enum PaymentClientEnum { + + /** + * app支付 + **/ + APP, + /** + * 展示二维码扫描支付 + */ + NATIVE, + /** + * 公众号内部调用支付 + */ + JSAPI, + /** + * 普通移动网页调用支付app + */ + H5, + /** + * 小程序,通常指微信小程序 + */ + MP +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/enums/PaymentMethodEnum.java b/framework/src/main/java/cn/lili/modules/payment/kit/enums/PaymentMethodEnum.java new file mode 100644 index 00000000..963a1c69 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/enums/PaymentMethodEnum.java @@ -0,0 +1,53 @@ +package cn.lili.modules.payment.kit.enums; + +/** + * 支付方式枚举 + * + * @author Chopper + * @date 2020/12/18 18:08 + */ +public enum PaymentMethodEnum { + + WECHAT("wechatPlugin", "微信"), + ALIPAY("aliPayPlugin", "支付宝"), + WALLET("walletPlugin", "余额支付"), + BANK_TRANSFER("bankTransferPlugin", "线下转账"); + + /** + * 插件id 调用对象,需要实现payment接口 + */ + private final String plugin; + /** + * 支付名称 + */ + private final String paymentName; + + public String getPlugin() { + return plugin; + } + + public String paymentName() { + return paymentName; + } + + /** + * 根据支付方式名称返回对象 + * + * @param name + * @return + */ + public static PaymentMethodEnum paymentNameOf(String name) { + for (PaymentMethodEnum value : PaymentMethodEnum.values()) { + if (value.name().equals(name)) { + return value; + } + } + return null; + } + + PaymentMethodEnum(String plugin, String paymentName) { + this.plugin = plugin; + this.paymentName = paymentName; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/params/CashierExecute.java b/framework/src/main/java/cn/lili/modules/payment/kit/params/CashierExecute.java new file mode 100644 index 00000000..c756bf6d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/params/CashierExecute.java @@ -0,0 +1,45 @@ +package cn.lili.modules.payment.kit.params; + +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.enums.CashierEnum; +import cn.lili.modules.payment.kit.params.dto.CashierParam; + +/** + * 收银台接口 + * + * @author Chopper + * @date 2021-01-25 19:08 + */ +public interface CashierExecute { + + /** + * 获取支付参数 + * + * @param payParam 收银台支付参数 + * @return 收银台所需支付参数 + */ + CashierParam getPaymentParams(PayParam payParam); + + /** + * 支付成功 + * + * @param paymentSuccessParams 支付回调 + */ + void paymentSuccess(PaymentSuccessParams paymentSuccessParams); + + /** + * 支付结果查询 + * + * @param payParam + * @return + */ + Boolean paymentResult(PayParam payParam); + + /** + * 服务的枚举类型 + * + * @return + */ + CashierEnum cashierEnum(); +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/params/dto/CashierParam.java b/framework/src/main/java/cn/lili/modules/payment/kit/params/dto/CashierParam.java new file mode 100644 index 00000000..47391397 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/params/dto/CashierParam.java @@ -0,0 +1,44 @@ +package cn.lili.modules.payment.kit.params.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +import java.util.Date; +import java.util.List; + +/** + * 支付参数 + * + * @author Chopper + * @date 2021-01-25 19:09 + */ +@Data +@ToString +public class CashierParam { + + @ApiModelProperty(value = "价格") + private Double price; + + @ApiModelProperty(value = "支付title") + private String title; + + @ApiModelProperty(value = "支付详细描述") + private String detail; + + @ApiModelProperty(value = "订单sn集合") + private String orderSns; + + @ApiModelProperty(value = "支持支付方式") + private List support; + + + @ApiModelProperty(value = "订单创建时间") + private Date createTime; + + @ApiModelProperty(value = "支付自动结束时间") + private Long autoCancel; + + @ApiModelProperty(value = "剩余余额") + private Double walletValue; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/params/impl/OrderCashier.java b/framework/src/main/java/cn/lili/modules/payment/kit/params/impl/OrderCashier.java new file mode 100644 index 00000000..bc12803b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/params/impl/OrderCashier.java @@ -0,0 +1,115 @@ +package cn.lili.modules.payment.kit.params.impl; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.enums.CashierEnum; +import cn.lili.modules.payment.kit.params.CashierExecute; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.system.entity.dto.BaseSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 订单支付信息获取 + * + * @author Chopper + * @date 2021-01-25 20:00 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderCashier implements CashierExecute { + //订单 + private final OrderService orderService; + //设置 + private final SettingService settingService; + + @Override + public CashierEnum cashierEnum() { + return CashierEnum.ORDER; + } + + @Override + public CashierParam getPaymentParams(PayParam payParam) { + if (payParam.getOrderType().equals(CashierEnum.ORDER.name())) { + //准备返回的数据 + CashierParam cashierParam = new CashierParam(); + //订单信息获取 + OrderDetailVO order = orderService.queryDetail(payParam.getSn()); + + //如果订单已支付,则不能发器支付 + if (order.getOrder().getPayStatus().equals(PayStatusEnum.PAID.name())) { + throw new ServiceException(ResultCode.PAY_DOUBLE_ERROR); + } + //如果订单状态不是待付款,则抛出异常 + if (!order.getOrder().getOrderStatus().equals(OrderStatusEnum.UNPAID.name())) { + throw new ServiceException(ResultCode.PAY_BAN); + } + cashierParam.setPrice(order.getOrder().getFlowPrice()); + + try { + BaseSetting baseSetting = JSONUtil.toBean(settingService.get(SettingEnum.BASE_SETTING.name()).getSettingValue(), BaseSetting.class); + cashierParam.setTitle(baseSetting.getSiteName()); + } catch (Exception e) { + cashierParam.setTitle("多用户商城,在线支付"); + } + + + List orderItemList = order.getOrderItems(); + StringBuilder subject = new StringBuilder(); + for (OrderItem orderItem : orderItemList) { + subject.append(orderItem.getGoodsName()).append(";"); + } + + cashierParam.setDetail(subject.toString()); + + cashierParam.setOrderSns(payParam.getSn()); + cashierParam.setCreateTime(order.getOrder().getCreateTime()); + return cashierParam; + } + + return null; + } + + @Override + public void paymentSuccess(PaymentSuccessParams paymentSuccessParams) { + + PayParam payParam = paymentSuccessParams.getPayParam(); + if (payParam.getOrderType().equals(CashierEnum.ORDER.name())) { + orderService.payOrder(payParam.getSn(), + paymentSuccessParams.getPaymentMethod(), + paymentSuccessParams.getReceivableNo()); + log.info("订单{}支付成功,金额{},方式{}", payParam.getSn(), + paymentSuccessParams.getPaymentMethod(), + paymentSuccessParams.getReceivableNo()); + } + } + + @Override + public Boolean paymentResult(PayParam payParam) { + if (payParam.getOrderType().equals(CashierEnum.ORDER.name())) { + Order order = orderService.getBySn(payParam.getSn()); + if (order != null) { + return order.getPayStatus().equals(PayStatusEnum.PAID.name()); + } else { + throw new ServiceException(ResultCode.PAY_NOT_EXIST_ORDER); + } + } + return false; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/params/impl/RechargeCashier.java b/framework/src/main/java/cn/lili/modules/payment/kit/params/impl/RechargeCashier.java new file mode 100644 index 00000000..703bb73b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/params/impl/RechargeCashier.java @@ -0,0 +1,95 @@ +package cn.lili.modules.payment.kit.params.impl; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.trade.entity.dos.Recharge; +import cn.lili.modules.order.trade.service.RechargeService; +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.enums.CashierEnum; +import cn.lili.modules.payment.kit.params.CashierExecute; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.system.entity.dto.BaseSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 充值信息获取 + * + * @author Chopper + * @date 2021-01-25 20:00 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RechargeCashier implements CashierExecute { + //余额 + private final RechargeService rechargeService; + //设置 + private final SettingService settingService; + + + @Override + public CashierEnum cashierEnum() { + return CashierEnum.RECHARGE; + } + + @Override + public void paymentSuccess(PaymentSuccessParams paymentSuccessParams) { + PayParam payParam = paymentSuccessParams.getPayParam(); + if (payParam.getOrderType().equals(CashierEnum.RECHARGE.name())) { + rechargeService.paySuccess(payParam.getSn(), paymentSuccessParams.getReceivableNo()); + log.info("会员充值-订单号{},第三方流水:{}", payParam.getSn(), paymentSuccessParams.getReceivableNo()); + } + } + + + @Override + public CashierParam getPaymentParams(PayParam payParam) { + if (payParam.getOrderType().equals(CashierEnum.RECHARGE.name())) { + //准备返回的数据 + CashierParam cashierParam = new CashierParam(); + //订单信息获取 + Recharge recharge = rechargeService.getRecharge(payParam.getSn()); + + //如果订单已支付,则不能发器支付 + if (recharge.getPayStatus().equals(PayStatusEnum.PAID.name())) { + throw new ServiceException(ResultCode.PAY_DOUBLE_ERROR); + } + + + cashierParam.setPrice(recharge.getRechargeMoney()); + + try { + BaseSetting baseSetting = JSONUtil.toBean(settingService.get(SettingEnum.BASE_SETTING.name()).getSettingValue(), BaseSetting.class); + cashierParam.setTitle(baseSetting.getSiteName()); + } catch (Exception e) { + cashierParam.setTitle("多用户商城,在线充值"); + } + cashierParam.setDetail("余额充值"); + cashierParam.setCreateTime(recharge.getCreateTime()); + return cashierParam; + } + + return null; + } + + @Override + public Boolean paymentResult(PayParam payParam) { + if (payParam.getOrderType().equals(CashierEnum.RECHARGE.name())) { + Recharge recharge = rechargeService.getRecharge(payParam.getSn()); + if (recharge != null) { + return recharge.getPayStatus().equals(PayStatusEnum.PAID.name()); + } else { + throw new ServiceException(ResultCode.PAY_NOT_EXIST_ORDER); + } + } + return false; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/params/impl/TradeCashier.java b/framework/src/main/java/cn/lili/modules/payment/kit/params/impl/TradeCashier.java new file mode 100644 index 00000000..f3713c0b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/params/impl/TradeCashier.java @@ -0,0 +1,122 @@ +package cn.lili.modules.payment.kit.params.impl; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.Trade; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.order.order.service.TradeService; +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.enums.CashierEnum; +import cn.lili.modules.payment.kit.params.CashierExecute; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.system.entity.dto.BaseSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 整笔交易信息获取 + * + * @author Chopper + * @date 2021-01-25 20:00 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class TradeCashier implements CashierExecute { + + //交易 + private final TradeService tradeService; + //订单 + private final OrderService orderService; + //设置 + private final SettingService settingService; + + + @Override + public CashierEnum cashierEnum() { + return CashierEnum.TRADE; + } + + @Override + public CashierParam getPaymentParams(PayParam payParam) { + if (payParam.getOrderType().equals(CashierEnum.TRADE.name())) { + //准备返回的数据 + CashierParam cashierParam = new CashierParam(); + //订单信息获取 + Trade trade = tradeService.getBySn(payParam.getSn()); + + List orders = orderService.getByTradeSn(payParam.getSn()); + + + String orderSns = orders.stream().map(Order::getSn).collect(Collectors.joining(", ")); + cashierParam.setOrderSns(orderSns); + + for (Order order : orders) { + //如果订单已支付,则不能发器支付 + if (order.getPayStatus().equals(PayStatusEnum.PAID.name())) { + throw new ServiceException(ResultCode.PAY_PARTIAL_ERROR); + } + //如果订单状态不是待付款,则抛出异常 + if (!order.getOrderStatus().equals(OrderStatusEnum.UNPAID.name())) { + throw new ServiceException(ResultCode.PAY_BAN); + } + } + + + cashierParam.setPrice(trade.getFlowPrice()); + + try { + BaseSetting baseSetting = JSONUtil.toBean(settingService.get(SettingEnum.BASE_SETTING.name()).getSettingValue(), BaseSetting.class); + cashierParam.setTitle(baseSetting.getSiteName()); + } catch (Exception e) { + cashierParam.setTitle("多用户商城,在线支付"); + } + String subject = "在线支付"; + cashierParam.setDetail(subject); + + cashierParam.setCreateTime(trade.getCreateTime()); + return cashierParam; + } + + return null; + } + + + @Override + public void paymentSuccess(PaymentSuccessParams paymentSuccessParams) { + if (paymentSuccessParams.getPayParam().getOrderType().equals(CashierEnum.TRADE.name())) { + tradeService.payTrade(paymentSuccessParams.getPayParam().getSn(), + paymentSuccessParams.getPaymentMethod(), + paymentSuccessParams.getReceivableNo()); + log.info("交易{}支付成功,方式{},流水号{},", paymentSuccessParams.getPayParam().getSn(), + paymentSuccessParams.getPaymentMethod(), + paymentSuccessParams.getReceivableNo()); + } + } + + @Override + public Boolean paymentResult(PayParam payParam) { + + if (payParam.getOrderType().equals(CashierEnum.TRADE.name())) { + Trade trade = tradeService.getBySn(payParam.getSn()); + if (trade != null) { + return trade.equals(PayStatusEnum.PAID.name()); + } else { + throw new ServiceException(ResultCode.PAY_NOT_EXIST_ORDER); + } + } + return false; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayApi.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayApi.java new file mode 100644 index 00000000..d6158200 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayApi.java @@ -0,0 +1,999 @@ +package cn.lili.modules.payment.kit.plugin.alipay; + +import com.alibaba.fastjson.JSONObject; +import com.alipay.api.*; +import com.alipay.api.domain.*; +import com.alipay.api.internal.util.StringUtils; +import com.alipay.api.request.*; +import com.alipay.api.response.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * 支付宝支付 + * + * @author Chopper + * @date 2020/12/15 19:26 + */ + +public class AliPayApi { + + + public static T doExecute(AlipayRequest request) throws AlipayApiException { + return certificateExecute(request); + } + + public static T doExecute(AlipayRequest request, String authToken) throws AlipayApiException { + return certificateExecute(request, authToken); + } + + public static T execute(AlipayRequest request) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().execute(request); + } + + public static T execute(AlipayRequest request, String authToken) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().execute(request, authToken); + } + + public static T execute(AlipayRequest request, String accessToken, String appAuthToken) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().execute(request, accessToken, appAuthToken); + } + + public static T execute(AlipayRequest request, String accessToken, String appAuthToken, String targetAppId) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().execute(request, accessToken, appAuthToken, targetAppId); + } + + public static T pageExecute(AlipayRequest request) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().pageExecute(request); + } + + public static T pageExecute(AlipayRequest request, String method) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().pageExecute(request, method); + } + + public static T sdkExecute(AlipayRequest request) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().sdkExecute(request); + } + + public static BatchAlipayResponse execute(BatchAlipayRequest request) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().execute(request); + } + + public static T certificateExecute(AlipayRequest request) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().certificateExecute(request); + } + + public static T certificateExecute(AlipayRequest request, String authToken) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().certificateExecute(request, authToken); + } + + public static T certificateExecute(AlipayRequest request, String accessToken, String appAuthToken) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().certificateExecute(request, accessToken, appAuthToken); + } + + public static T certificateExecute(AlipayRequest request, String accessToken, String appAuthToken, String targetAppId) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().certificateExecute(request, accessToken, appAuthToken, targetAppId); + } + + /** + * APP支付 + * + * @param model {@link AlipayTradeAppPayModel} + * @param notifyUrl 异步通知 URL + * @return {@link AlipayTradeAppPayResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeAppPayResponse appPayToResponse(AlipayTradeAppPayModel model, String notifyUrl) throws AlipayApiException { + AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + return sdkExecute(request); + } + + /** + * APP支付 + * + * @param model {@link AlipayTradeAppPayModel} + * @param notifyUrl 异步通知 URL + * @param appAuthToken 应用授权token + * @return {@link AlipayTradeAppPayResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeAppPayResponse appPayToResponse(AlipayTradeAppPayModel model, String notifyUrl, String appAuthToken) throws AlipayApiException { + AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + request.putOtherTextParam("app_auth_token", appAuthToken); + return sdkExecute(request); + } + + /** + * WAP支付 + * + * @param response {@link HttpServletResponse} + * @param model {@link AlipayTradeWapPayModel} + * @param returnUrl 异步通知URL + * @param notifyUrl 同步通知URL + * @throws AlipayApiException 支付宝 Api 异常 + * @throws IOException IO 异常 + */ + public static void wapPay(HttpServletResponse response, AlipayTradeWapPayModel model, String returnUrl, String notifyUrl) throws AlipayApiException, IOException { + String form = wapPayStr(model, returnUrl, notifyUrl); + response.setContentType("text/html;charset=UTF-8") ; + PrintWriter out = response.getWriter(); + out.write(form); + out.flush(); + out.close(); + } + + /** + * WAP支付 + * + * @param response {@link HttpServletResponse} + * @param model {@link AlipayTradeWapPayModel} + * @param returnUrl 异步通知URL + * @param notifyUrl 同步通知URL + * @param appAuthToken 应用授权token + * @throws AlipayApiException 支付宝 Api 异常 + * @throws IOException IO 异常 + */ + public static void wapPay(HttpServletResponse response, AlipayTradeWapPayModel model, String returnUrl, + String notifyUrl, String appAuthToken) throws AlipayApiException, IOException { + String form = wapPayStr(model, returnUrl, notifyUrl, appAuthToken); + response.setContentType("text/html;charset=UTF-8") ; + PrintWriter out = response.getWriter(); + out.write(form); + out.flush(); + out.close(); + } + + /** + *

WAP支付

+ * + *

为了解决 Filter 中使用 OutputStream getOutputStream() 和 PrintWriter getWriter() 冲突异常问题

+ * + * @param response {@link HttpServletResponse} + * @param model {@link AlipayTradeWapPayModel} + * @param returnUrl 异步通知URL + * @param notifyUrl 同步通知URL + * @param appAuthToken 应用授权token + * @throws AlipayApiException 支付宝 Api 异常 + * @throws IOException IO 异常 + */ + public static void wapPayByOutputStream(HttpServletResponse response, AlipayTradeWapPayModel model, String returnUrl, String notifyUrl, String appAuthToken) throws AlipayApiException, IOException { + String form = wapPayStr(model, returnUrl, notifyUrl, appAuthToken); + response.setContentType("text/html;charset=UTF-8") ; + OutputStream out = response.getOutputStream(); + out.write(form.getBytes("UTF-8")); + response.getOutputStream().flush(); + } + + /** + *

WAP支付

+ * + *

为了解决 Filter 中使用 OutputStream getOutputStream() 和 PrintWriter getWriter() 冲突异常问题

+ * + * @param response {@link HttpServletResponse} + * @param model {@link AlipayTradeWapPayModel} + * @param returnUrl 异步通知URL + * @param notifyUrl 同步通知URL + * @throws AlipayApiException 支付宝 Api 异常 + * @throws IOException IO 异常 + */ + public static void wapPayByOutputStream(HttpServletResponse response, AlipayTradeWapPayModel model, String returnUrl, String notifyUrl) throws AlipayApiException, IOException { + String form = wapPayStr(model, returnUrl, notifyUrl); + response.setContentType("text/html;charset=UTF-8") ; + OutputStream out = response.getOutputStream(); + out.write(form.getBytes("UTF-8")); + response.getOutputStream().flush(); + } + + /** + * WAP支付 + * + * @param model {@link AlipayTradeWapPayModel} + * @param returnUrl 异步通知URL + * @param notifyUrl 同步通知URL + * @return {String} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static String wapPayStr(AlipayTradeWapPayModel model, String returnUrl, String notifyUrl) throws AlipayApiException { + AlipayTradeWapPayRequest aliPayRequest = new AlipayTradeWapPayRequest(); + aliPayRequest.setReturnUrl(returnUrl); + aliPayRequest.setNotifyUrl(notifyUrl); + aliPayRequest.setBizModel(model); + return pageExecute(aliPayRequest).getBody(); + } + + /** + * WAP支付 + * + * @param model {@link AlipayTradeWapPayModel} + * @param returnUrl 异步通知URL + * @param notifyUrl 同步通知URL + * @param appAuthToken 应用授权token + * @return {String} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static String wapPayStr(AlipayTradeWapPayModel model, String returnUrl, String notifyUrl, String appAuthToken) throws AlipayApiException { + AlipayTradeWapPayRequest aliPayRequest = new AlipayTradeWapPayRequest(); + aliPayRequest.setReturnUrl(returnUrl); + aliPayRequest.setNotifyUrl(notifyUrl); + aliPayRequest.setBizModel(model); + aliPayRequest.putOtherTextParam("app_auth_token", appAuthToken); + return pageExecute(aliPayRequest).getBody(); + } + + /** + * 统一收单交易支付接口接口
+ * 适用于:条形码支付、声波支付等
+ * + * @param model {@link AlipayTradePayModel} + * @param notifyUrl 异步通知URL + * @return {@link AlipayTradePayResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradePayResponse tradePayToResponse(AlipayTradePayModel model, String notifyUrl) throws AlipayApiException { + AlipayTradePayRequest request = new AlipayTradePayRequest(); + // 填充业务参数 + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + return doExecute(request); + } + + /** + * 统一收单交易支付接口接口
+ * 适用于:条形码支付、声波支付等
+ * + * @param model {AlipayTradePayModel} + * @param notifyUrl 异步通知URL + * @param appAuthToken 应用授权token + * @return {AlipayTradePayResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradePayResponse tradePayToResponse(AlipayTradePayModel model, String notifyUrl, String appAuthToken) throws AlipayApiException { + AlipayTradePayRequest request = new AlipayTradePayRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + request.putOtherTextParam("app_auth_token", appAuthToken); + return doExecute(request); + } + + /** + * 统一收单线下交易预创建
+ * 适用于:扫码支付等
+ * + * @param model {@link AlipayTradePrecreateModel} + * @param notifyUrl 异步通知URL + * @return {@link AlipayTradePrecreateResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradePrecreateResponse tradePrecreatePayToResponse(AlipayTradePrecreateModel model, String notifyUrl) throws AlipayApiException { + AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + return doExecute(request); + } + + /** + * 统一收单线下交易预创建
+ * 适用于:扫码支付等
+ * + * @param model {@link AlipayTradePrecreateModel} + * @param notifyUrl 异步通知URL + * @param appAuthToken 应用授权token + * @return {@link AlipayTradePrecreateResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradePrecreateResponse tradePrecreatePayToResponse(AlipayTradePrecreateModel model, + String notifyUrl, String appAuthToken) throws AlipayApiException { + AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + return execute(request, null, appAuthToken); + } + + /** + * 单笔转账到支付宝账户 + * + * @param model {@link AlipayFundTransToaccountTransferModel} + * @return 转账是否成功 + * @throws AlipayApiException 支付宝 Api 异常 + */ + @Deprecated + public static boolean transfer(AlipayFundTransToaccountTransferModel model) throws AlipayApiException { + AlipayFundTransToaccountTransferResponse response = transferToResponse(model); + String result = response.getBody(); + if (response.isSuccess()) { + return true; + } else { + // 调用查询接口查询数据 + JSONObject jsonObject = JSONObject.parseObject(result); + String outBizNo = jsonObject.getJSONObject("alipay_fund_trans_toaccount_transfer_response").getString("out_biz_no"); + AlipayFundTransOrderQueryModel queryModel = new AlipayFundTransOrderQueryModel(); + model.setOutBizNo(outBizNo); + return transferQuery(queryModel); + } + } + + /** + * 单笔转账到支付宝账户 + * + * @param model {@link AlipayFundTransToaccountTransferModel} + * @return {@link AlipayFundTransToaccountTransferResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundTransToaccountTransferResponse transferToResponse(AlipayFundTransToaccountTransferModel model) throws AlipayApiException { + AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 转账查询接口 + * + * @param model {@link AlipayFundTransOrderQueryModel} + * @return 是否存在此 + * @throws AlipayApiException 支付宝 Api 异常 + */ + @Deprecated + public static boolean transferQuery(AlipayFundTransOrderQueryModel model) throws AlipayApiException { + AlipayFundTransOrderQueryResponse response = transferQueryToResponse(model); + return response.isSuccess(); + } + + /** + * 转账查询接口 + * + * @param model {@link AlipayFundTransOrderQueryModel} + * @return {@link AlipayFundTransOrderQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundTransOrderQueryResponse transferQueryToResponse(AlipayFundTransOrderQueryModel model) throws AlipayApiException { + AlipayFundTransOrderQueryRequest request = new AlipayFundTransOrderQueryRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 统一转账接口 + * + * @param model model {@link AlipayFundTransUniTransferModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayFundTransUniTransferResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundTransUniTransferResponse uniTransferToResponse(AlipayFundTransUniTransferModel model, String appAuthToken) throws AlipayApiException { + AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest(); + request.setBizModel(model); + if (!StringUtils.isEmpty(appAuthToken)) { + request.putOtherTextParam("app_auth_token", appAuthToken); + } + return doExecute(request); + } + + /** + * 转账业务单据查询接口 + * + * @param model model {@link AlipayFundTransCommonQueryModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayFundTransCommonQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundTransCommonQueryResponse transCommonQueryToResponse(AlipayFundTransCommonQueryModel model, String appAuthToken) throws AlipayApiException { + AlipayFundTransCommonQueryRequest request = new AlipayFundTransCommonQueryRequest(); + request.setBizModel(model); + if (!StringUtils.isEmpty(appAuthToken)) { + request.putOtherTextParam("app_auth_token", appAuthToken); + } + return doExecute(request); + } + + /** + * 支付宝资金账户资产查询接口 + * + * @param model model {@link AlipayFundAccountQueryModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayFundAccountQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundAccountQueryResponse accountQueryToResponse(AlipayFundAccountQueryModel model, String appAuthToken) throws AlipayApiException { + AlipayFundAccountQueryRequest request = new AlipayFundAccountQueryRequest(); + request.setBizModel(model); + if (!StringUtils.isEmpty(appAuthToken)) { + request.putOtherTextParam("app_auth_token", appAuthToken); + } + return doExecute(request); + } + + + /** + * 统一收单线下交易查询接口 + * + * @param model {@link AlipayTradeQueryModel} + * @return {@link AlipayTradeQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeQueryResponse tradeQueryToResponse(AlipayTradeQueryModel model) throws AlipayApiException { + AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 统一收单线下交易查询接口 + * + * @param model {@link AlipayTradeQueryModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayTradeQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeQueryResponse tradeQueryToResponse(AlipayTradeQueryModel model, String appAuthToken) throws AlipayApiException { + AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); + request.setBizModel(model); + return execute(request, null, appAuthToken); + } + + /** + * 统一收单交易撤销接口 + * + * @param model {@link AlipayTradeCancelModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayTradeCancelResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeCancelResponse tradeCancelToResponse(AlipayTradeCancelModel model, String appAuthToken) throws AlipayApiException { + AlipayTradeCancelRequest request = new AlipayTradeCancelRequest(); + request.setBizModel(model); + return execute(request, null, appAuthToken); + } + + /** + * 统一收单交易撤销接口 + * + * @param model {@link AlipayTradeCancelModel} + * @return {@link AlipayTradeCancelResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeCancelResponse tradeCancelToResponse(AlipayTradeCancelModel model) + throws AlipayApiException { + AlipayTradeCancelRequest request = new AlipayTradeCancelRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 统一收单交易关闭接口 + * + * @param model {@link AlipayTradeCloseModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayTradeCloseResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeCloseResponse tradeCloseToResponse(AlipayTradeCloseModel model, String appAuthToken) throws AlipayApiException { + AlipayTradeCloseRequest request = new AlipayTradeCloseRequest(); + request.setBizModel(model); + return execute(request, null, appAuthToken); + + } + + /** + * 统一收单交易关闭接口 + * + * @param model {@link AlipayTradeCloseModel} + * @return {@link AlipayTradeCloseResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeCloseResponse tradeCloseToResponse(AlipayTradeCloseModel model) throws AlipayApiException { + AlipayTradeCloseRequest request = new AlipayTradeCloseRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 统一收单交易创建接口 + * + * @param model {@link AlipayTradeCreateModel} + * @param notifyUrl 异步通知URL + * @return {@link AlipayTradeCreateResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeCreateResponse tradeCreateToResponse(AlipayTradeCreateModel model, String notifyUrl) throws AlipayApiException { + AlipayTradeCreateRequest request = new AlipayTradeCreateRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + return doExecute(request); + } + + /** + * 统一收单交易创建接口 + * + * @param model {@link AlipayTradeCreateModel} + * @param notifyUrl 异步通知URL + * @param appAuthToken 应用授权token + * @return {@link AlipayTradeCreateResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeCreateResponse tradeCreateToResponse(AlipayTradeCreateModel model, String notifyUrl, String appAuthToken) throws AlipayApiException { + AlipayTradeCreateRequest request = new AlipayTradeCreateRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + return execute(request, null, appAuthToken); + } + + /** + * 统一收单交易退款接口 + * + * @param model {@link AlipayTradeRefundModel} + * @return {@link AlipayTradeRefundResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeRefundResponse tradeRefundToResponse(AlipayTradeRefundModel model) throws AlipayApiException { + AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 统一收单交易退款接口 + * + * @param model {@link AlipayTradeRefundModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayTradeRefundResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeRefundResponse tradeRefundToResponse(AlipayTradeRefundModel model, String appAuthToken) throws AlipayApiException { + AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); + request.setBizModel(model); + return execute(request, null, appAuthToken); + } + + /** + * 统一收单退款页面接口 + * + * @param model {@link AlipayTradePageRefundModel} + * @return {@link AlipayTradePageRefundResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradePageRefundResponse tradeRefundToResponse(AlipayTradePageRefundModel model) throws AlipayApiException { + AlipayTradePageRefundRequest request = new AlipayTradePageRefundRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 统一收单退款页面接口 + * + * @param model {@link AlipayTradePageRefundModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayTradePageRefundResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradePageRefundResponse tradeRefundToResponse(AlipayTradePageRefundModel model, String appAuthToken) throws AlipayApiException { + AlipayTradePageRefundRequest request = new AlipayTradePageRefundRequest(); + request.setBizModel(model); + return execute(request, null, appAuthToken); + } + + /** + * 统一收单交易退款查询 + * + * @param model {@link AlipayTradeFastpayRefundQueryModel} + * @return {@link AlipayTradeFastpayRefundQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeFastpayRefundQueryResponse tradeRefundQueryToResponse(AlipayTradeFastpayRefundQueryModel model) throws AlipayApiException { + AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 统一收单交易退款查询 + * + * @param model {@link AlipayTradeFastpayRefundQueryModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayTradeFastpayRefundQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeFastpayRefundQueryResponse tradeRefundQueryToResponse(AlipayTradeFastpayRefundQueryModel model, String appAuthToken) throws AlipayApiException { + AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest(); + request.setBizModel(model); + return execute(request, null, appAuthToken); + } + + /** + * 查询对账单下载地址 + * + * @param model {@link AlipayDataDataserviceBillDownloadurlQueryModel} + * @return 对账单下载地址 + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static String billDownloadUrlQuery(AlipayDataDataserviceBillDownloadurlQueryModel model) throws AlipayApiException { + AlipayDataDataserviceBillDownloadurlQueryResponse response = billDownloadUrlQueryToResponse(model); + return response.getBillDownloadUrl(); + } + + /** + * 查询对账单下载地址 + * + * @param model {@link AlipayDataDataserviceBillDownloadurlQueryModel} + * @return {@link AlipayDataDataserviceBillDownloadurlQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayDataDataserviceBillDownloadurlQueryResponse billDownloadUrlQueryToResponse(AlipayDataDataserviceBillDownloadurlQueryModel model) throws AlipayApiException { + AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 查询对账单下载地址 + * + * @param model {@link AlipayDataDataserviceBillDownloadurlQueryModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayDataDataserviceBillDownloadurlQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayDataDataserviceBillDownloadurlQueryResponse billDownloadUrlQueryToResponse(AlipayDataDataserviceBillDownloadurlQueryModel model, String appAuthToken) throws AlipayApiException { + AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest(); + request.setBizModel(model); + request.putOtherTextParam("app_auth_token", appAuthToken); + return doExecute(request); + } + + /** + * 统一收单交易结算接口 + * + * @param model {@link AlipayTradeOrderSettleModel} + * @param appAuthToken 应用授权token + * @return {@link AlipayTradeOrderSettleResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeOrderSettleResponse tradeOrderSettleToResponse(AlipayTradeOrderSettleModel model, String appAuthToken) throws AlipayApiException { + AlipayTradeOrderSettleRequest request = new AlipayTradeOrderSettleRequest(); + request.setBizModel(model); + return execute(request, null, appAuthToken); + } + + /** + * 统一收单交易结算接口 + * + * @param model {@link AlipayTradeOrderSettleModel} + * @return {@link AlipayTradeOrderSettleResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeOrderSettleResponse tradeOrderSettleToResponse(AlipayTradeOrderSettleModel model) throws AlipayApiException { + AlipayTradeOrderSettleRequest request = new AlipayTradeOrderSettleRequest(); + request.setBizModel(model); + return doExecute(request); + } + + + /** + * 电脑网站支付(PC支付) + * + * @param response {@link HttpServletResponse} + * @param model {@link AlipayTradePagePayModel} + * @param notifyUrl 异步通知URL + * @param returnUrl 同步通知URL + * @param appAuthToken 应用授权token + * @throws AlipayApiException 支付宝 Api 异常 + * @throws IOException IO 异常 + */ + public static void tradePage(HttpServletResponse response, AlipayTradePagePayModel model, String notifyUrl, String returnUrl, String appAuthToken) throws AlipayApiException, IOException { + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + request.setReturnUrl(returnUrl); + request.putOtherTextParam("app_auth_token", appAuthToken); + String form = pageExecute(request).getBody(); + response.setContentType("text/html;charset=UTF-8") ; + PrintWriter out = response.getWriter(); + out.write(form); + out.flush(); + out.close(); + } + + + /** + * 电脑网站支付(PC支付) + * + * @param response {@link HttpServletResponse} + * @param model {@link AlipayTradePagePayModel} + * @param notifyUrl 异步通知URL + * @param returnUrl 同步通知URL + * @throws AlipayApiException 支付宝 Api 异常 + * @throws IOException IO 异常 + */ + public static void tradePageByOutputStream(HttpServletResponse response, AlipayTradePagePayModel model, String notifyUrl, String returnUrl) throws AlipayApiException, IOException { + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + request.setReturnUrl(returnUrl); + String form = pageExecute(request).getBody(); + response.setContentType("text/html;charset=UTF-8") ; + OutputStream out = response.getOutputStream(); + out.write(form.getBytes("UTF-8")); + response.getOutputStream().flush(); + } + + /** + * 电脑网站支付(PC支付) + * + * @param response {@link HttpServletResponse} + * @param model {@link AlipayTradePagePayModel} + * @param notifyUrl 异步通知URL + * @param returnUrl 同步通知URL + * @param appAuthToken 应用授权token + * @throws AlipayApiException 支付宝 Api 异常 + * @throws IOException IO 异常 + */ + public static void tradePageByOutputStream(HttpServletResponse response, AlipayTradePagePayModel model, + String notifyUrl, String returnUrl, String appAuthToken) throws AlipayApiException, IOException { + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + request.setReturnUrl(returnUrl); + request.putOtherTextParam("app_auth_token", appAuthToken); + String form = pageExecute(request).getBody(); + response.setContentType("text/html;charset=UTF-8") ; + OutputStream out = response.getOutputStream(); + out.write(form.getBytes("UTF-8")); + response.getOutputStream().flush(); + } + + /** + * 资金预授权冻结接口 + * + * @param model {@link AlipayFundAuthOrderFreezeModel} + * @return {@link AlipayFundAuthOrderFreezeResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundAuthOrderFreezeResponse authOrderFreezeToResponse(AlipayFundAuthOrderFreezeModel model) throws AlipayApiException { + AlipayFundAuthOrderFreezeRequest request = new AlipayFundAuthOrderFreezeRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 资金授权解冻接口 + * + * @param model {@link AlipayFundAuthOrderUnfreezeModel} + * @return {@link AlipayFundAuthOrderUnfreezeResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundAuthOrderUnfreezeResponse authOrderUnfreezeToResponse(AlipayFundAuthOrderUnfreezeModel model) throws AlipayApiException { + AlipayFundAuthOrderUnfreezeRequest request = new AlipayFundAuthOrderUnfreezeRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 资金预授权冻结接口 + * + * @param model {@link AlipayFundAuthOrderVoucherCreateModel} + * @return {@link AlipayFundAuthOrderVoucherCreateResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundAuthOrderVoucherCreateResponse authOrderVoucherCreateToResponse(AlipayFundAuthOrderVoucherCreateModel model) throws AlipayApiException { + AlipayFundAuthOrderVoucherCreateRequest request = new AlipayFundAuthOrderVoucherCreateRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 资金授权撤销接口 + * + * @param model {@link AlipayFundAuthOperationCancelModel} + * @return {@link AlipayFundAuthOperationCancelResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundAuthOperationCancelResponse authOperationCancelToResponse(AlipayFundAuthOperationCancelModel model) throws AlipayApiException { + AlipayFundAuthOperationCancelRequest request = new AlipayFundAuthOperationCancelRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 资金授权操作查询接口 + * + * @param model {@link AlipayFundAuthOperationDetailQueryModel} + * @return {@link AlipayFundAuthOperationDetailQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundAuthOperationDetailQueryResponse authOperationDetailQueryToResponse(AlipayFundAuthOperationDetailQueryModel model) throws AlipayApiException { + AlipayFundAuthOperationDetailQueryRequest request = new AlipayFundAuthOperationDetailQueryRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 红包无线支付接口 + * + * @param model {@link AlipayFundCouponOrderAppPayModel} + * @return {@link AlipayFundCouponOrderAppPayResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundCouponOrderAppPayResponse fundCouponOrderAppPayToResponse(AlipayFundCouponOrderAppPayModel model) throws AlipayApiException { + AlipayFundCouponOrderAppPayRequest request = new AlipayFundCouponOrderAppPayRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 红包页面支付接口 + * + * @param model {@link AlipayFundCouponOrderPagePayModel} + * @return {@link AlipayFundCouponOrderPagePayResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundCouponOrderPagePayResponse fundCouponOrderPagePayToResponse(AlipayFundCouponOrderPagePayModel model) throws AlipayApiException { + AlipayFundCouponOrderPagePayRequest request = new AlipayFundCouponOrderPagePayRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 红包协议支付接口 + * + * @param model {@link AlipayFundCouponOrderAgreementPayModel} + * @return {@link AlipayFundCouponOrderAgreementPayResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundCouponOrderAgreementPayResponse fundCouponOrderAgreementPayToResponse(AlipayFundCouponOrderAgreementPayModel model) throws AlipayApiException { + AlipayFundCouponOrderAgreementPayRequest request = new AlipayFundCouponOrderAgreementPayRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 红包打款接口 + * + * @param model {@link AlipayFundCouponOrderDisburseModel} + * @return {@link AlipayFundCouponOrderDisburseResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundCouponOrderDisburseResponse fundCouponOrderDisburseToResponse(AlipayFundCouponOrderDisburseModel model) throws AlipayApiException { + AlipayFundCouponOrderDisburseRequest request = new AlipayFundCouponOrderDisburseRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 红包退回接口 + * + * @param model {@link AlipayFundCouponOrderRefundModel} + * @return {@link AlipayFundCouponOrderRefundResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundCouponOrderRefundResponse fundCouponOrderRefundToResponse(AlipayFundCouponOrderRefundModel model) throws AlipayApiException { + AlipayFundCouponOrderRefundRequest request = new AlipayFundCouponOrderRefundRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 红包退回接口 + * + * @param model {@link AlipayFundCouponOperationQueryModel} + * @return {@link AlipayFundCouponOperationQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayFundCouponOperationQueryResponse fundCouponOperationQueryToResponse(AlipayFundCouponOperationQueryModel model) throws AlipayApiException { + AlipayFundCouponOperationQueryRequest request = new AlipayFundCouponOperationQueryRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 应用授权 URL 拼装 + * + * @param appId 应用编号 + * @param redirectUri 回调 URI + * @return 应用授权 URL + * @throws UnsupportedEncodingException 编码异常 + */ + public static String getOauth2Url(String appId, String redirectUri) throws UnsupportedEncodingException { + return new StringBuffer().append("https://openauth.alipay.com/oauth2/appToAppAuth.htm?app_id=").append(appId).append("&redirect_uri=").append(URLEncoder.encode(redirectUri, "UTF-8")).toString(); + } + + /** + * 使用 app_auth_code 换取 app_auth_token + * + * @param model {@link AlipayOpenAuthTokenAppModel} + * @return {@link AlipayOpenAuthTokenAppResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayOpenAuthTokenAppResponse openAuthTokenAppToResponse(AlipayOpenAuthTokenAppModel model) throws AlipayApiException { + AlipayOpenAuthTokenAppRequest request = new AlipayOpenAuthTokenAppRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 查询授权信息 + * + * @param model {@link AlipayOpenAuthTokenAppQueryModel} + * @return {@link AlipayOpenAuthTokenAppQueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayOpenAuthTokenAppQueryResponse openAuthTokenAppQueryToResponse(AlipayOpenAuthTokenAppQueryModel model) throws AlipayApiException { + AlipayOpenAuthTokenAppQueryRequest request = new AlipayOpenAuthTokenAppQueryRequest(); + request.setBizModel(model); + return doExecute(request); + } + + + /** + * 将异步通知的参数转化为Map + * + * @param request {HttpServletRequest} + * @return 转化后的Map + */ + public static Map toMap(HttpServletRequest request) { + Map params = new HashMap(); + Map requestParams = request.getParameterMap(); + for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) { + String name = iter.next(); + String[] values = requestParams.get(name); + String valueStr = ""; + for (int i = 0; i < values.length; i++) { + valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; + } + params.put(name, valueStr); + } + return params; + } + + /** + * 分账关系绑定 + * + * @param model {@link AlipayTradeRoyaltyRelationBindModel} + * @return {@link AlipayTradeRoyaltyRelationBindResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeRoyaltyRelationBindResponse tradeRoyaltyRelationBind( + AlipayTradeRoyaltyRelationBindModel model) throws AlipayApiException { + AlipayTradeRoyaltyRelationBindRequest request = new AlipayTradeRoyaltyRelationBindRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 分账关系解绑 + * + * @param model {@link AlipayTradeRoyaltyRelationUnbindModel} + * @return {@link AlipayTradeRoyaltyRelationUnbindResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeRoyaltyRelationUnbindResponse tradeRoyaltyRelationUnBind( + AlipayTradeRoyaltyRelationUnbindModel model) throws AlipayApiException { + AlipayTradeRoyaltyRelationUnbindRequest request = new AlipayTradeRoyaltyRelationUnbindRequest(); + request.setBizModel(model); + return doExecute(request); + } + + /** + * 分账关系查询 + * + * @param model {@link AlipayTradeRoyaltyRelationBatchqueryModel} + * @return {@link AlipayTradeRoyaltyRelationBatchqueryResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeRoyaltyRelationBatchqueryResponse tradeRoyaltyRelationBatchQuery( + AlipayTradeRoyaltyRelationBatchqueryModel model) throws AlipayApiException { + AlipayTradeRoyaltyRelationBatchqueryRequest request = new AlipayTradeRoyaltyRelationBatchqueryRequest(); + request.setBizModel(model); + return doExecute(request); + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayApiConfigKit.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayApiConfigKit.java new file mode 100644 index 00000000..119dab5a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayApiConfigKit.java @@ -0,0 +1,80 @@ +package cn.lili.modules.payment.kit.plugin.alipay; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.SpringContextUtil; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.payment.AlipayPaymentSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.alipay.api.AlipayApiException; +import com.alipay.api.CertAlipayRequest; +import com.alipay.api.DefaultAlipayClient; + +import java.util.Date; + +/** + * AliPayApiConfigKit + * + * @author Chopper + * @date 2020-12-16 09:31 + */ +public class AliPayApiConfigKit { + + /** + * 支付配置 + */ + static DefaultAlipayClient defaultAlipayClient; + + /** + * 下次刷新时间 + */ + static Date nextRebuildDate; + + /** + * 间隔时间 + */ + static Long refreshInterval = 1000 * 60 * 3L; + + /** + * 获取支付宝支付参数 + * + * @return + * @throws AlipayApiException + */ + public static synchronized DefaultAlipayClient getAliPayApiConfig() throws AlipayApiException { + Date date = new Date(); + //如果过期,则重新构建 + if (nextRebuildDate == null || date.after(nextRebuildDate)) { + return rebuild(); + } + return defaultAlipayClient; + } + + static DefaultAlipayClient rebuild() throws AlipayApiException { + AlipayPaymentSetting setting; + try { + SettingService settingService = (SettingService) SpringContextUtil.getBean("settingServiceImpl"); + Setting systemSetting = settingService.get(SettingEnum.ALIPAY_PAYMENT.name()); + setting = JSONUtil.toBean(systemSetting.getSettingValue(), AlipayPaymentSetting.class); + } catch (Exception e) { + throw new ServiceException(ResultCode.PAY_NOT_SUPPORT); + } + CertAlipayRequest certAlipayRequest = new CertAlipayRequest(); + certAlipayRequest.setServerUrl("https://openapi.alipay.com/gateway.do"); + certAlipayRequest.setFormat("json"); + certAlipayRequest.setCharset("utf-8"); + certAlipayRequest.setSignType("RSA2"); + certAlipayRequest.setAppId(setting.getAppId()); + certAlipayRequest.setPrivateKey(setting.getPrivateKey()); + certAlipayRequest.setCertPath(setting.getCertPath()); + certAlipayRequest.setAlipayPublicCertPath(setting.getAlipayPublicCertPath()); + certAlipayRequest.setRootCertPath(setting.getRootCertPath()); + defaultAlipayClient = new DefaultAlipayClient(certAlipayRequest); + nextRebuildDate = DateUtil.date(new Date().getTime() + refreshInterval); + return defaultAlipayClient; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayPlugin.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayPlugin.java new file mode 100644 index 00000000..c82e9ab6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayPlugin.java @@ -0,0 +1,276 @@ +package cn.lili.modules.payment.kit.plugin.alipay; + +import cn.hutool.core.net.URLDecoder; +import cn.hutool.core.net.URLEncoder; +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.utils.SnowFlake; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.ResultMessage; +import cn.lili.config.properties.ApiProperties; +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.kit.CashierSupport; +import cn.lili.modules.payment.kit.Payment; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.payment.service.PaymentService; +import cn.lili.modules.payment.service.RefundLogService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.payment.AlipayPaymentSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.alibaba.fastjson.JSONObject; +import com.alipay.api.AlipayApiException; +import com.alipay.api.domain.*; +import com.alipay.api.internal.util.AlipaySignature; +import com.alipay.api.response.AlipayTradeCancelResponse; +import com.alipay.api.response.AlipayTradeRefundResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +/** + * 支付宝支付 + * + * @author Chopper + * @date 2020/12/17 09:55 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AliPayPlugin implements Payment { + //支付日志 + private final PaymentService paymentService; + //退款日志 + private final RefundLogService refundLogService; + //收银台 + private final CashierSupport cashierSupport; + //设置 + private final SettingService settingService; + //API域名 + private final ApiProperties apiProperties; + + + @Override + public ResultMessage h5pay(HttpServletRequest request, HttpServletResponse response, PayParam payParam) { + + + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + //请求订单编号 + String outTradeNo = SnowFlake.getIdStr(); + + AlipayTradeWapPayModel payModel = new AlipayTradeWapPayModel(); + payModel.setBody(cashierParam.getTitle()); + payModel.setSubject(cashierParam.getDetail()); + payModel.setTotalAmount(cashierParam.getPrice() + ""); + //回传数据 + payModel.setPassbackParams(URLEncoder.createAll().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8)); + //3分钟超时 + payModel.setTimeoutExpress("3m"); + payModel.setOutTradeNo(outTradeNo); + payModel.setProductCode("QUICK_WAP_PAY"); + try { + AliPayRequest.wapPay(response, payModel, callbackUrl(apiProperties.getBuyer(), PaymentMethodEnum.ALIPAY), + notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.ALIPAY)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + @Override + public ResultMessage JSApiPay(HttpServletRequest request, PayParam payParam) { + throw new ServiceException("当前支付通道暂不支持"); + } + + @Override + public ResultMessage appPay(HttpServletRequest request, PayParam payParam) { + try { + + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + //请求订单编号 + String outTradeNo = SnowFlake.getIdStr(); + + AlipayTradeAppPayModel payModel = new AlipayTradeAppPayModel(); + + payModel.setBody(cashierParam.getTitle()); + payModel.setSubject(cashierParam.getDetail()); + payModel.setTotalAmount(cashierParam.getPrice() + ""); + + //3分钟超时 + payModel.setTimeoutExpress("3m"); + //回传数据 + payModel.setPassbackParams(URLEncoder.createAll().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8)); + payModel.setOutTradeNo(outTradeNo); + payModel.setProductCode("QUICK_MSECURITY_PAY"); + + String orderInfo = AliPayRequest.appPayToResponse(payModel, notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.ALIPAY)).getBody(); + return ResultUtil.data(orderInfo); + } catch (AlipayApiException e) { + e.printStackTrace(); + return ResultUtil.error(ResultCode.PAY_ERROR); + } + } + + @Override + public ResultMessage nativePay(HttpServletRequest request, PayParam payParam) { + + try { + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + + AlipayTradePrecreateModel payModel = new AlipayTradePrecreateModel(); + + //请求订单编号 + String outTradeNo = SnowFlake.getIdStr(); + + payModel.setBody(cashierParam.getTitle()); + payModel.setSubject(cashierParam.getDetail()); + payModel.setTotalAmount(cashierParam.getPrice() + ""); + + //回传数据 + payModel.setPassbackParams(URLEncoder.createAll().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8)); +// payModel.setStoreId("store_id"); + payModel.setTimeoutExpress("3m"); + payModel.setOutTradeNo(outTradeNo); + + String resultStr = AliPayRequest.tradePrecreatePayToResponse(payModel, notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.ALIPAY)).getBody(); + JSONObject jsonObject = JSONObject.parseObject(resultStr); + return ResultUtil.data(jsonObject.getJSONObject("alipay_trade_precreate_response").getString("qr_code")); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + @Override + public void refund(RefundLog refundLog) { + AlipayTradeRefundModel model = new AlipayTradeRefundModel(); + //这里取支付回调时返回的流水 + if (StringUtils.isNotEmpty(refundLog.getPaymentReceivableNo())) { + model.setTradeNo(refundLog.getPaymentReceivableNo()); + } else { + throw new ServiceException(ResultCode.ERROR); + } + model.setRefundAmount(refundLog.getTotalAmount() + ""); + model.setRefundReason(refundLog.getRefundReason()); + model.setOutRequestNo(refundLog.getOutOrderNo()); + try { + AlipayTradeRefundResponse alipayTradeRefundResponse = AliPayApi.tradeRefundToResponse(model); + log.error("支付宝退款,参数:{},支付宝响应:{}", JSONUtil.toJsonStr(model), JSONUtil.toJsonStr(alipayTradeRefundResponse)); + if (alipayTradeRefundResponse.isSuccess()) { + refundLog.setIsRefund(true); + refundLog.setReceivableNo(refundLog.getOutOrderNo()); + } else { + refundLog.setErrorMessage(String.format("错误码:%s,错误原因:%s", alipayTradeRefundResponse.getSubCode(), alipayTradeRefundResponse.getSubMsg())); + } + refundLogService.save(refundLog); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + public void cancel(RefundLog refundLog) { + AlipayTradeCancelModel model = new AlipayTradeCancelModel(); + //这里取支付回调时返回的流水 + if (StringUtils.isNotEmpty(refundLog.getPaymentReceivableNo())) { + model.setTradeNo(refundLog.getPaymentReceivableNo()); + } else { + throw new ServiceException(ResultCode.ERROR); + } + try { + AlipayTradeCancelResponse alipayTradeCancelResponse = AliPayApi.tradeCancelToResponse(model); + if (alipayTradeCancelResponse.isSuccess()) { + refundLog.setIsRefund(true); + refundLog.setReceivableNo(refundLog.getOutOrderNo()); + } else { + refundLog.setErrorMessage(String.format("错误码:%s,错误原因:%s", alipayTradeCancelResponse.getSubCode(), alipayTradeCancelResponse.getSubMsg())); + } + refundLogService.save(refundLog); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void refundNotify(HttpServletRequest request) { + //不需要实现 + } + + @Override + public void callBack(HttpServletRequest request) { + verifyNotify(request); + log.info("支付同步回调:"); + } + + @Override + public void notify(HttpServletRequest request) { + verifyNotify(request); + log.info("支付异步通知:"); + } + + /** + * 验证支付结果 + * + * @param request + */ + private void verifyNotify(HttpServletRequest request) { + try { + AlipayPaymentSetting alipayPaymentSetting = alipayPaymentSetting(); + // 获取支付宝反馈信息 + Map map = AliPayApi.toMap(request); + log.info("支付回调响应:{}", JSONUtil.toJsonStr(map)); + boolean verifyResult = AlipaySignature.rsaCertCheckV1(map, alipayPaymentSetting.getAlipayPublicCertPath(), "UTF-8", + "RSA2"); + + String payParamStr = map.get("passback_params"); + String payParamJson = URLDecoder.decode(payParamStr, StandardCharsets.UTF_8); + PayParam payParam = JSONUtil.toBean(payParamJson, PayParam.class); + + + if (verifyResult) { + String tradeNo = map.get("trade_no"); + Double totalAmount = Double.parseDouble(map.get("total_amount")); + PaymentSuccessParams paymentSuccessParams = + new PaymentSuccessParams(PaymentMethodEnum.ALIPAY.name(), tradeNo, totalAmount, payParam); + + paymentService.success(paymentSuccessParams); + log.info("支付回调通知:支付成功-参数:{},回调参数:{}", map, payParam); + } else { + log.info("支付回调通知:支付失败-参数:{}", map); + } + } catch (AlipayApiException e) { + e.printStackTrace(); + throw new ServiceException("支付回调通知异常"); + } + + } + + /** + * 获取微信支付配置 + * + * @return + */ + private AlipayPaymentSetting alipayPaymentSetting() { + Setting setting = settingService.get(SettingEnum.ALIPAY_PAYMENT.name()); + if (setting != null) { + return JSONUtil.toBean(setting.getSettingValue(), AlipayPaymentSetting.class); + } + throw new ServiceException("支付未配置"); + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayRequest.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayRequest.java new file mode 100644 index 00000000..ceb0d134 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/alipay/AliPayRequest.java @@ -0,0 +1,140 @@ +package cn.lili.modules.payment.kit.plugin.alipay; + +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayRequest; +import com.alipay.api.AlipayResponse; +import com.alipay.api.domain.AlipayTradeAppPayModel; +import com.alipay.api.domain.AlipayTradePagePayModel; +import com.alipay.api.domain.AlipayTradePrecreateModel; +import com.alipay.api.domain.AlipayTradeWapPayModel; +import com.alipay.api.request.AlipayTradeAppPayRequest; +import com.alipay.api.request.AlipayTradePagePayRequest; +import com.alipay.api.request.AlipayTradePrecreateRequest; +import com.alipay.api.request.AlipayTradeWapPayRequest; +import com.alipay.api.response.AlipayTradeAppPayResponse; +import com.alipay.api.response.AlipayTradePrecreateResponse; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * 支付宝支付 + * + * @author Chopper + * @date 2020/12/15 19:26 + */ + +public class AliPayRequest { + + /** + * WAP支付 + * + * @param response {@link HttpServletResponse} + * @param model {@link AlipayTradeWapPayModel} + * @param returnUrl 异步通知URL + * @param notifyUrl 同步通知URL + * @throws AlipayApiException 支付宝 Api 异常 + * @throws IOException IO 异常 + */ + public static void wapPay(HttpServletResponse response, AlipayTradeWapPayModel model, String returnUrl, String notifyUrl) throws AlipayApiException, IOException { + String form = wapPayStr(model, returnUrl, notifyUrl); + response.setContentType("text/html;charset=UTF-8"); + + PrintWriter out = response.getWriter(); + out.write(form); + out.flush(); + out.close(); + } + + /** + * APP支付 + * + * @param model {@link AlipayTradeAppPayModel} + * @param notifyUrl 异步通知 URL + * @return {@link AlipayTradeAppPayResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradeAppPayResponse appPayToResponse(AlipayTradeAppPayModel model, String notifyUrl) throws AlipayApiException { + AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + return sdkExecute(request); + } + + /** + * 电脑网站支付(PC支付) + * + * @param response {@link HttpServletResponse} + * @param model {@link AlipayTradePagePayModel} + * @param notifyUrl 异步通知URL + * @param returnUrl 同步通知URL + * @throws AlipayApiException 支付宝 Api 异常 + * @throws IOException IO 异常 + */ + public static void tradePage(HttpServletResponse response, AlipayTradePagePayModel model, String notifyUrl, String returnUrl) throws AlipayApiException, IOException { + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + request.setReturnUrl(returnUrl); + String form = pageExecute(request).getBody(); + response.setContentType("text/html;charset=UTF-8"); + PrintWriter out = response.getWriter(); + out.write(form); + out.flush(); + out.close(); + } + + /** + * 统一收单线下交易预创建
+ * 适用于:扫码支付等
+ * + * @param model {@link AlipayTradePrecreateModel} + * @param notifyUrl 异步通知URL + * @return {@link AlipayTradePrecreateResponse} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static AlipayTradePrecreateResponse tradePrecreatePayToResponse(AlipayTradePrecreateModel model, String notifyUrl) throws AlipayApiException { + AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); + request.setBizModel(model); + request.setNotifyUrl(notifyUrl); + return doExecute(request); + } + + + /** + * WAP支付 + * + * @param model {@link AlipayTradeWapPayModel} + * @param returnUrl 异步通知URL + * @param notifyUrl 同步通知URL + * @return {String} + * @throws AlipayApiException 支付宝 Api 异常 + */ + public static String wapPayStr(AlipayTradeWapPayModel model, String returnUrl, String notifyUrl) throws AlipayApiException { + AlipayTradeWapPayRequest aliPayRequest = new AlipayTradeWapPayRequest(); + aliPayRequest.setReturnUrl(returnUrl); + aliPayRequest.setNotifyUrl(notifyUrl); + aliPayRequest.setBizModel(model); + return pageExecute(aliPayRequest).getBody(); + } + + + public static T doExecute(AlipayRequest request) throws AlipayApiException { + return certificateExecute(request); + } + + public static T certificateExecute(AlipayRequest request) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().certificateExecute(request); + } + + public static T pageExecute(AlipayRequest request) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().pageExecute(request); + } + + public static T sdkExecute(AlipayRequest request) throws AlipayApiException { + return AliPayApiConfigKit.getAliPayApiConfig().sdkExecute(request); + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/bankTransfer/BankTransferPlugin.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/bankTransfer/BankTransferPlugin.java new file mode 100644 index 00000000..529f9427 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/bankTransfer/BankTransferPlugin.java @@ -0,0 +1,77 @@ +package cn.lili.modules.payment.kit.plugin.bankTransfer; + +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.kit.Payment; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.payment.service.PaymentService; +import cn.lili.modules.payment.service.RefundLogService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * 线下收款 + * + * @author Chopper + * @version v1.0 + * 2021-02-20 10:14 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BankTransferPlugin implements Payment { + //退款日志 + private final RefundLogService refundLogService; + //支付日志 + private PaymentService paymentService; + + @Override + public void refund(RefundLog refundLog) { + try { + refundLog.setIsRefund(true); + refundLogService.save(refundLog); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 支付订单 + * + * @param order 订单 + */ + public void callBack(Order order) { + + //收银参数 + CashierParam cashierParam = new CashierParam(); + cashierParam.setPrice(order.getFlowPrice()); + //支付参数 + PayParam payParam = new PayParam(); + payParam.setOrderType("ORDER"); + payParam.setSn(order.getSn()); + payParam.setClientType(ClientTypeEnum.PC.name()); + + PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams( + PaymentMethodEnum.BANK_TRANSFER.name(), + "", + order.getFlowPrice(), + payParam + ); + + //记录支付日志 + paymentService.adminPaySuccess(paymentSuccessParams); + log.info("支付回调通知:线上支付:{}", payParam); + } + + @Autowired + private void setPaymentService(PaymentService paymentService) { + this.paymentService = paymentService; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wallet/WalletPlugin.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wallet/WalletPlugin.java new file mode 100644 index 00000000..54bc5fdd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wallet/WalletPlugin.java @@ -0,0 +1,177 @@ +package cn.lili.modules.payment.kit.plugin.wallet; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.member.service.MemberWalletService; +import cn.lili.modules.order.trade.entity.enums.DepositServiceTypeEnum; +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.kit.CashierSupport; +import cn.lili.modules.payment.kit.Payment; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.payment.service.PaymentService; +import cn.lili.modules.payment.service.RefundLogService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * WalletPlugin + * + * @author Chopper + * @version v1.0 + * 2021-02-20 10:14 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WalletPlugin implements Payment { + + //支付日志 + private final PaymentService paymentService; + //退款日志 + private final RefundLogService refundLogService; + //会员余额 + private final MemberWalletService memberWalletService; + //收银台 + private CashierSupport cashierSupport; + + @Override + public ResultMessage h5pay(HttpServletRequest request, HttpServletResponse response, PayParam payParam) { + + savePaymentLog(payParam); + return ResultUtil.success(ResultCode.PAY_SUCCESS); + } + + @Override + public ResultMessage JSApiPay(HttpServletRequest request, PayParam payParam) { + savePaymentLog(payParam); + return ResultUtil.success(ResultCode.PAY_SUCCESS); + } + + @Override + public ResultMessage appPay(HttpServletRequest request, PayParam payParam) { + savePaymentLog(payParam); + return ResultUtil.success(ResultCode.PAY_SUCCESS); + } + + @Override + public ResultMessage nativePay(HttpServletRequest request, PayParam payParam) { + savePaymentLog(payParam); + return ResultUtil.success(ResultCode.PAY_SUCCESS); + } + + @Override + public ResultMessage mpPay(HttpServletRequest request, PayParam payParam) { + + savePaymentLog(payParam); + return ResultUtil.success(ResultCode.PAY_SUCCESS); + } + + @Override + public void cancel(RefundLog refundLog) { + + try { + memberWalletService.increase(refundLog.getTotalAmount(), + refundLog.getMemberId(), + "取消[" + refundLog.getOrderSn() + "]订单,退还金额[" + refundLog.getTotalAmount() + "]", + DepositServiceTypeEnum.WALLET_REFUND.name()); + refundLog.setIsRefund(true); + refundLogService.save(refundLog); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 保存支付日志 + * + * @param payParam 支付参数 + */ + private void savePaymentLog(PayParam payParam) { + //获取支付收银参数 + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + this.callBack(payParam, cashierParam); + } + + @Override + public void refund(RefundLog refundLog) { + try { + memberWalletService.increase(refundLog.getTotalAmount(), + refundLog.getMemberId(), + "售后[" + refundLog.getAfterSaleNo() + "]审批,退还金额[" + refundLog.getTotalAmount() + "]", DepositServiceTypeEnum.WALLET_REFUND.name()); + refundLog.setIsRefund(true); + refundLogService.save(refundLog); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 支付订单 + * + * @param payParam 支付参数 + * @param cashierParam 收银台参数 + */ + public void callBack(PayParam payParam, CashierParam cashierParam) { + + //支付信息 + try { + if (UserContext.getCurrentUser() == null) { + throw new ServiceException(ResultCode.USER_NOT_LOGIN); + } + boolean result = memberWalletService.reduce( + cashierParam.getPrice(), + UserContext.getCurrentUser().getId(), + "订单[" + cashierParam.getOrderSns() + "]支付金额[" + cashierParam.getPrice() + "]", + DepositServiceTypeEnum.WALLET_PAY.name() + ); + if (result) { + try { + PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams( + PaymentMethodEnum.WALLET.name(), + "", + cashierParam.getPrice(), + payParam + ); + + paymentService.success(paymentSuccessParams); + log.info("支付回调通知:余额支付:{}", payParam); + } catch (ServiceException e) { + //业务异常,则支付手动回滚 + memberWalletService.increase( + cashierParam.getPrice(), + UserContext.getCurrentUser().getId(), + "订单[" + cashierParam.getOrderSns() + "]支付异常,余额返还[" + cashierParam.getPrice() + "]", + DepositServiceTypeEnum.WALLET_REFUND.name() + ); + throw e; + } + } else { + throw new ServiceException(ResultCode.WALLET_INSUFFICIENT); + } + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + log.info("余额支付异常", e); + throw new ServiceException(ResultCode.WALLET_INSUFFICIENT); + } + + } + + + @Autowired + public void setCashierSupport(CashierSupport cashierSupport) { + this.cashierSupport = cashierSupport; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatApi.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatApi.java new file mode 100644 index 00000000..81413802 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatApi.java @@ -0,0 +1,858 @@ +package cn.lili.modules.payment.kit.plugin.wechat; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.ContentType; +import cn.lili.modules.payment.kit.core.PaymentHttpResponse; +import cn.lili.modules.payment.kit.core.enums.RequestMethodEnums; +import cn.lili.modules.payment.kit.core.kit.HttpKit; +import cn.lili.modules.payment.kit.core.kit.PayKit; +import cn.lili.modules.payment.kit.core.kit.WxPayKit; +import cn.lili.modules.payment.kit.plugin.wechat.enums.WechatDomain; + +import java.io.File; +import java.io.InputStream; +import java.security.PrivateKey; +import java.util.HashMap; +import java.util.Map; + +/** + * 微信支付相关接口 + * + * @author Chopper + * @date 2021/1/26 15:25 + */ + +public class WechatApi { + + private WechatApi() { + } + + /** + * 获取接口请求的 URL + * + * @param wechatApi {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 支付 API 接口枚举 + * @return {@link String} 返回完整的接口请求URL + */ + public static String getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi wechatApi) { + return getReqUrl(wechatApi, null, false); + } + + /** + * 获取接口请求的 URL + * + * @param wechatApi {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 支付 API 接口枚举 + * @param isSandBox 是否是沙箱环境 + * @return {@link String} 返回完整的接口请求URL + */ + public static String getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi wechatApi, boolean isSandBox) { + return getReqUrl(wechatApi, null, isSandBox); + } + + /** + * 获取接口请求的 URL + * + * @param wechatApi {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 支付 API 接口枚举 + * @param wechatDomain {@link WechatDomain} 支付 API 接口域名枚举 + * @param isSandBox 是否是沙箱环境 + * @return {@link String} 返回完整的接口请求URL + */ + public static String getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi wechatApi, WechatDomain wechatDomain, boolean isSandBox) { + if (wechatDomain == null) { + wechatDomain = WechatDomain.CHINA; + } + return wechatDomain.getType() + .concat(isSandBox ? cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.SAND_BOX_NEW.getUrl() : "") + .concat(wechatApi.getUrl()); + } + + /** + * 发起请求 + * + * @param apiUrl 接口 URL + * 通过 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi)} + * 或者 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi, WechatDomain, boolean)} 来获取 + * @param params 接口请求参数 + * @return {@link String} 请求返回的结果 + */ + public static String execution(String apiUrl, Map params) { + return doPost(apiUrl, params); + } + + /** + * 发起请求 + * + * @param apiUrl 接口 URL + * 通过 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi)} + * 或者 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi, WechatDomain, boolean)} 来获取 + * @param params 接口请求参数 + * @return {@link String} 请求返回的结果 + */ + public static String executionByGet(String apiUrl, Map params) { + return doGet(apiUrl, params); + } + + /** + * 发起请求 + * + * @param apiUrl 接口 URL + * 通过 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi)} + * 或者 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi, WechatDomain, boolean)} 来获取 + * @param params 接口请求参数 + * @param certPath 证书文件路径 + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String execution(String apiUrl, Map params, String certPath, String certPass) { + return doPostSsl(apiUrl, params, certPath, certPass); + } + + /** + * 发起请求 + * + * @param apiUrl 接口 URL + * 通过 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi)} + * 或者 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi, WechatDomain, boolean)} 来获取 + * @param params 接口请求参数 + * @param certPath 证书文件路径 + * @return {@link String} 请求返回的结果 + */ + public static String execution(String apiUrl, Map params, String certPath) { + return doPostSsl(apiUrl, params, certPath); + } + + /** + * 发起请求 + * + * @param apiUrl 接口 URL + * 通过 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi)} + * 或者 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi, WechatDomain, boolean)} 来获取 + * @param params 接口请求参数 + * @param certFile 证书文件输入流 + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String execution(String apiUrl, Map params, InputStream certFile, String certPass) { + return doPostSsl(apiUrl, params, certFile, certPass); + } + + /** + * 发起请求 + * + * @param apiUrl 接口 URL + * 通过 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi)} + * 或者 {@link WechatApi#getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi, WechatDomain, boolean)} 来获取 + * @param params 接口请求参数 + * @param certFile 证书文件输入流 + * @return {@link String} 请求返回的结果 + */ + public static String execution(String apiUrl, Map params, InputStream certFile) { + return doPostSsl(apiUrl, params, certFile); + } + + public static String execution(String apiUrl, Map params, + String certPath, String certPass, String filePath) { + return doUploadSsl(apiUrl, params, certPath, certPass, filePath); + } + + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号,接口中包含敏感信息时必传 + * @param keyPath apiclient_key.pem 证书路径 + * @param body 接口请求参数 + * @param nonceStr 随机字符库 + * @param timestamp 时间戳 + * @param authType 认证类型 + * @param file 文件 + * @return {@link PaymentHttpResponse} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix, + String mchId, String serialNo, String platSerialNo, String keyPath, + String body, String nonceStr, long timestamp, String authType, + File file) throws Exception { + // 构建 Authorization + String authorization = WxPayKit.buildAuthorization(method, urlSuffix, mchId, serialNo, + keyPath, body, nonceStr, timestamp, authType); + + if (StrUtil.isEmpty(platSerialNo)) { + platSerialNo = serialNo; + } + if (method == RequestMethodEnums.GET) { + return get(urlPrefix.concat(urlSuffix), authorization, platSerialNo, null); + } else if (method == RequestMethodEnums.POST) { + return post(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body); + } else if (method == RequestMethodEnums.DELETE) { + return delete(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body); + } else if (method == RequestMethodEnums.UPLOAD) { + return upload(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body, file); + } else if (method == RequestMethodEnums.PUT) { + return put(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body); + } + return null; + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号,接口中包含敏感信息时必传 + * @param privateKey 商户私钥 + * @param body 接口请求参数 + * @param nonceStr 随机字符库 + * @param timestamp 时间戳 + * @param authType 认证类型 + * @param file 文件 + * @return {@link PaymentHttpResponse} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix, + String mchId, String serialNo, String platSerialNo, PrivateKey privateKey, + String body, String nonceStr, long timestamp, String authType, + File file) throws Exception { + // 构建 Authorization + String authorization = WxPayKit.buildAuthorization(method, urlSuffix, mchId, serialNo, + privateKey, body, nonceStr, timestamp, authType); + + if (StrUtil.isEmpty(platSerialNo)) { + platSerialNo = serialNo; + } + + if (method == RequestMethodEnums.GET) { + return get(urlPrefix.concat(urlSuffix), authorization, platSerialNo, null); + } else if (method == RequestMethodEnums.POST) { + return post(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body); + } else if (method == RequestMethodEnums.DELETE) { + return delete(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body); + } else if (method == RequestMethodEnums.UPLOAD) { + return upload(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body, file); + } else if (method == RequestMethodEnums.PUT) { + return put(urlPrefix.concat(urlSuffix), authorization, platSerialNo, body); + } + return null; + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号 + * @param keyPath apiclient_key.pem 证书路径 + * @param body 接口请求参数 + * @return {@link PaymentHttpResponse} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix, String mchId, + String serialNo, String platSerialNo, String keyPath, String body) throws Exception { + long timestamp = System.currentTimeMillis() / 1000; + String authType = "WECHATPAY2-SHA256-RSA2048"; + String nonceStr = WxPayKit.generateStr(); + return v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body, nonceStr, timestamp, authType, null); + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号 + * @param privateKey 商户私钥 + * @param body 接口请求参数 + * @return {@link PaymentHttpResponse} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix, String mchId, + String serialNo, String platSerialNo, PrivateKey privateKey, String body) throws Exception { + long timestamp = System.currentTimeMillis() / 1000; + String authType = "WECHATPAY2-SHA256-RSA2048"; + String nonceStr = WxPayKit.generateStr(); + return v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, privateKey, body, nonceStr, timestamp, authType, null); + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号 + * @param keyPath apiclient_key.pem 证书路径 + * @param params Get 接口请求参数 + * @return {@link PaymentHttpResponse} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix, + String mchId, String serialNo, String platSerialNo, String keyPath, + Map params) throws Exception { + long timestamp = System.currentTimeMillis() / 1000; + String authType = "WECHATPAY2-SHA256-RSA2048"; + String nonceStr = WxPayKit.generateStr(); + if (null != params && !params.keySet().isEmpty()) { + urlSuffix = urlSuffix.concat("?").concat(PayKit.createLinkString(params, true)); + } + return v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, "", nonceStr, timestamp, authType, null); + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号 + * @param privateKey 商户私钥 + * @param params Get 接口请求参数 + * @return {@link PaymentHttpResponse} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + public static PaymentHttpResponse v3(RequestMethodEnums method, String urlPrefix, String urlSuffix, + String mchId, String serialNo, String platSerialNo, PrivateKey privateKey, + Map params) throws Exception { + long timestamp = System.currentTimeMillis() / 1000; + String authType = "WECHATPAY2-SHA256-RSA2048"; + String nonceStr = WxPayKit.generateStr(); + if (null != params && !params.keySet().isEmpty()) { + urlSuffix = urlSuffix.concat("?").concat(PayKit.createLinkString(params, true)); + } + return v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, privateKey, "", nonceStr, timestamp, authType, null); + } + + /** + * V3 接口统一执行入口 + * + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号 + * @param keyPath apiclient_key.pem 证书路径 + * @param body 接口请求参数 + * @param file 文件 + * @return {@link PaymentHttpResponse} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + public static PaymentHttpResponse v3(String urlPrefix, String urlSuffix, String mchId, String serialNo, String platSerialNo, String keyPath, String body, File file) throws Exception { + long timestamp = System.currentTimeMillis() / 1000; + String authType = "WECHATPAY2-SHA256-RSA2048"; + String nonceStr = WxPayKit.generateStr(); + return v3(RequestMethodEnums.UPLOAD, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body, nonceStr, timestamp, authType, file); + } + + /** + * V3 接口统一执行入口 + * + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号 + * @param privateKey 商户私钥 + * @param body 接口请求参数 + * @param file 文件 + * @return {@link PaymentHttpResponse} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + public static PaymentHttpResponse v3(String urlPrefix, String urlSuffix, String mchId, String serialNo, + String platSerialNo, PrivateKey privateKey, String body, File file) throws Exception { + long timestamp = System.currentTimeMillis() / 1000; + String authType = "WECHATPAY2-SHA256-RSA2048"; + String nonceStr = WxPayKit.generateStr(); + return v3(RequestMethodEnums.UPLOAD, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, privateKey, body, nonceStr, timestamp, authType, file); + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号,接口中包含敏感信息时必传 + * @param keyPath apiclient_key.pem 证书路径 + * @param body 接口请求参数 + * @param nonceStr 随机字符库 + * @param timestamp 时间戳 + * @param authType 认证类型 + * @param file 文件 + * @return {@link Map} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + @Deprecated + public static Map v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix, + String mchId, String serialNo, String platSerialNo, String keyPath, + String body, String nonceStr, long timestamp, String authType, + File file) throws Exception { + PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body, nonceStr, timestamp, authType, file); + return buildResMap(response); + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param keyPath apiclient_key.pem 证书路径 + * @param body 接口请求参数 + * @return {@link Map} 请求返回的结果 + */ + @Deprecated + public static Map v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix, String mchId, + String serialNo, String keyPath, String body) throws Exception { + PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, null, keyPath, body); + return buildResMap(response); + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号 + * @param keyPath apiclient_key.pem 证书路径 + * @param body 接口请求参数 + * @return {@link Map} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + @Deprecated + public static Map v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix, String mchId, + String serialNo, String platSerialNo, String keyPath, String body) throws Exception { + PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body); + return buildResMap(response); + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号 + * @param keyPath apiclient_key.pem 证书路径 + * @param params Get 接口请求参数 + * @return {@link Map} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + @Deprecated + public static Map v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix, + String mchId, String serialNo, String platSerialNo, String keyPath, + Map params) throws Exception { + PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, params); + return buildResMap(response); + } + + /** + * V3 接口统一执行入口 + * + * @param method {@link RequestMethodEnums} 请求方法 + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param keyPath apiclient_key.pem 证书路径 + * @param params Get 接口请求参数 + * @return {@link Map} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + @Deprecated + public static Map v3Execution(RequestMethodEnums method, String urlPrefix, String urlSuffix, + String mchId, String serialNo, String keyPath, + Map params) throws Exception { + PaymentHttpResponse response = v3(method, urlPrefix, urlSuffix, mchId, serialNo, null, keyPath, params); + return buildResMap(response); + } + + /** + * V3 接口统一执行入口 + * + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param platSerialNo 平台序列号 + * @param keyPath apiclient_key.pem 证书路径 + * @param body 接口请求参数 + * @param file 文件 + * @return {@link Map} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + @Deprecated + public static Map v3Upload(String urlPrefix, String urlSuffix, String mchId, String serialNo, String platSerialNo, String keyPath, String body, File file) throws Exception { + PaymentHttpResponse response = v3(urlPrefix, urlSuffix, mchId, serialNo, platSerialNo, keyPath, body, file); + return buildResMap(response); + } + + /** + * V3 接口统一执行入口 + * + * @param urlPrefix 可通过 {@link WechatDomain}来获取 + * @param urlSuffix 可通过 {@link cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi} 来获取,URL挂载参数需要自行拼接 + * @param mchId 商户Id + * @param serialNo 商户 API 证书序列号 + * @param keyPath apiclient_key.pem 证书路径 + * @param body 接口请求参数 + * @param file 文件 + * @return {@link Map} 请求返回的结果 + * @throws Exception 接口执行异常 + */ + @Deprecated + public static Map v3Upload(String urlPrefix, String urlSuffix, String mchId, String serialNo, String keyPath, String body, File file) throws Exception { + return v3Upload(urlPrefix, urlSuffix, mchId, serialNo, null, keyPath, body, file); + } + + /** + * 发放企业红包 + * + * @param params 请求参数 + * @param certPath 证书文件路径 + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String sendWorkWxRedPack(Map params, String certPath, String certPass) { + return execution(getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.SEND_WORK_WX_RED_PACK), params, certPath, certPass); + } + + /** + * 发放企业红包 + * + * @param params 请求参数 + * @param certFile 证书文件的 InputStream + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String sendWorkWxRedPack(Map params, InputStream certFile, String certPass) { + return execution(getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.SEND_WORK_WX_RED_PACK), params, certFile, certPass); + } + + /** + * 查询向员工付款记录 + * + * @param params 请求参数 + * @param certPath 证书文件路径 + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String queryWorkWxRedPack(Map params, String certPath, String certPass) { + return execution(getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.QUERY_WORK_WX_RED_PACK), params, certPath, certPass); + } + + /** + * 查询向员工付款记录 + * + * @param params 请求参数 + * @param certFile 证书文件的 InputStream + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String queryWorkWxRedPack(Map params, InputStream certFile, String certPass) { + return execution(getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.QUERY_WORK_WX_RED_PACK), params, certFile, certPass); + } + + /** + * 向员工付款 + * + * @param params 请求参数 + * @param certPath 证书文件路径 + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String trans2pocket(Map params, String certPath, String certPass) { + return execution(getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.PAY_WWS_TRANS_2_POCKET), params, certPath, certPass); + } + + /** + * 向员工付款 + * + * @param params 请求参数 + * @param certFile 证书文件的 InputStream + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String trans2pocket(Map params, InputStream certFile, String certPass) { + return execution(getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.PAY_WWS_TRANS_2_POCKET), params, certFile, certPass); + } + + /** + * 查询向员工付款记录 + * + * @param params 请求参数 + * @param certPath 证书文件路径 + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String queryTrans2pocket(Map params, String certPath, String certPass) { + return execution(getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.QUERY_WWS_TRANS_2_POCKET), params, certPath, certPass); + } + + /** + * 查询向员工付款记录 + * + * @param params 请求参数 + * @param certFile 证书文件的 InputStream + * @param certPass 证书密码 + * @return {@link String} 请求返回的结果 + */ + public static String queryTrans2pocket(Map params, InputStream certFile, String certPass) { + return execution(getReqUrl(cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.QUERY_WWS_TRANS_2_POCKET), params, certFile, certPass); + } + + /** + * @param url 请求url + * @param params 请求参数 + * @return {@link String} 请求返回的结果 + */ + public static String doGet(String url, Map params) { + return HttpKit.getDelegate().get(url, params); + } + + /** + * get 请求 + * + * @param url 请求url + * @param params 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse get(String url, Map params, Map headers) { + return HttpKit.getDelegate().get(url, params, headers); + } + + /** + * get 请求 + * + * @param url 请求url + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @param params 请求参数 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse get(String url, String authorization, String serialNumber, Map params) { + return get(url, params, getHeaders(authorization, serialNumber)); + } + + /** + * post 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse post(String url, String data, Map headers) { + return HttpKit.getDelegate().post(url, data, headers); + } + + /** + * post 请求 + * + * @param url 请求url + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @param data 请求参数 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse post(String url, String authorization, String serialNumber, String data) { + return post(url, data, getHeaders(authorization, serialNumber)); + } + + /** + * delete 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse delete(String url, String data, Map headers) { + return HttpKit.getDelegate().delete(url, data, headers); + } + + /** + * delete 请求 + * + * @param url 请求url + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @param data 请求参数 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse delete(String url, String authorization, String serialNumber, String data) { + return delete(url, data, getHeaders(authorization, serialNumber)); + } + + /** + * upload 请求 + * + * @param url 请求url + * @param params 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse upload(String url, Map params, Map headers) { + return HttpKit.getDelegate().post(url, params, headers); + } + + /** + * upload 请求 + * + * @param url 请求url + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @param data 请求参数 + * @param file 上传文件 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse upload(String url, String authorization, String serialNumber, String data, File file) { + Map paramMap = new HashMap<>(2); + paramMap.put("file", file); + paramMap.put("meta", data); + return upload(url, paramMap, getUploadHeaders(authorization, serialNumber)); + } + + + /** + * put 请求 + * + * @param url 请求url + * @param data 请求参数 + * @param headers 请求头 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse put(String url, String data, Map headers) { + return HttpKit.getDelegate().put(url, data, headers); + } + + /** + * put 请求 + * + * @param url 请求url + * @param authorization 授权信息 + * @param serialNumber 公钥证书序列号 + * @param data 请求参数 + * @return {@link PaymentHttpResponse} 请求返回的结果 + */ + public static PaymentHttpResponse put(String url, String authorization, String serialNumber, String data) { + return put(url, data, getHeaders(authorization, serialNumber)); + } + + public static String doPost(String url, Map params) { + return HttpKit.getDelegate().post(url, WxPayKit.toXml(params)); + } + + public static String doPostSsl(String url, Map params, String certPath, String certPass) { + return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certPath, certPass); + } + + public static String doPostSsl(String url, Map params, InputStream certFile, String certPass) { + return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certFile, certPass); + } + + public static String doPostSsl(String url, Map params, String certPath) { + if (params.isEmpty() || !params.containsKey("mch_id")) { + throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。"); + } + String certPass = params.get("mch_id"); + return doPostSsl(url, params, certPath, certPass); + } + + public static String doPostSsl(String url, Map params, InputStream certFile) { + if (params.isEmpty() || !params.containsKey("mch_id")) { + throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。"); + } + String certPass = params.get("mch_id"); + return doPostSsl(url, params, certFile, certPass); + } + + public static String doUploadSsl(String url, Map params, String certPath, String certPass, String filePath) { + return HttpKit.getDelegate().upload(url, WxPayKit.toXml(params), certPath, certPass, filePath); + } + + public static String doUploadSsl(String url, Map params, String certPath, String filePath) { + if (params.isEmpty() || !params.containsKey("mch_id")) { + throw new RuntimeException("请求参数中必须包含 mch_id,如接口参考中不包 mch_id, 请使用其他同名构造方法。"); + } + String certPass = params.get("mch_id"); + return doUploadSsl(url, params, certPath, certPass, filePath); + } + + + public static Map getBaseHeaders(String authorization) { + Map headers = new HashMap<>(5); + headers.put("Accept", ContentType.JSON.toString()); + headers.put("Authorization", authorization); + return headers; + } + + public static Map getHeaders(String authorization, String serialNumber) { + Map headers = getBaseHeaders(authorization); + headers.put("Content-Type", ContentType.JSON.toString()); + if (StrUtil.isNotEmpty(serialNumber)) { + headers.put("Wechatpay-Serial", serialNumber); + } + return headers; + } + + public static Map getUploadHeaders(String authorization, String serialNumber) { + Map headers = getBaseHeaders(authorization); + headers.put("Content-Type", "multipart/form-data;boundary=\"boundary\""); + if (StrUtil.isNotEmpty(serialNumber)) { + headers.put("Wechatpay-Serial", serialNumber); + } + return headers; + } + + /** + * 构建返回参数 + * + * @param response {@link PaymentHttpResponse} + * @return {@link Map} + */ + public static Map buildResMap(PaymentHttpResponse response) { + if (response == null) { + return null; + } + Map map = new HashMap<>(6); + String timestamp = response.getHeader("Wechatpay-Timestamp"); + String nonceStr = response.getHeader("Wechatpay-Nonce"); + String serialNo = response.getHeader("Wechatpay-Serial"); + String signature = response.getHeader("Wechatpay-Signature"); + String body = response.getBody(); + int status = response.getStatus(); + map.put("timestamp", timestamp); + map.put("nonceStr", nonceStr); + map.put("serialNumber", serialNo); + map.put("signature", signature); + map.put("body", body); + map.put("status", status); + return map; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatPlugin.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatPlugin.java new file mode 100644 index 00000000..059d29e3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/WechatPlugin.java @@ -0,0 +1,668 @@ +package cn.lili.modules.payment.kit.plugin.wechat; + +import cn.hutool.core.net.URLDecoder; +import cn.hutool.core.net.URLEncoder; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.utils.SnowFlake; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.ResultMessage; +import cn.lili.config.properties.ApiProperties; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.connect.entity.Connect; +import cn.lili.modules.connect.entity.enums.ConnectEnum; +import cn.lili.modules.connect.service.ConnectService; +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.kit.CashierSupport; +import cn.lili.modules.payment.kit.Payment; +import cn.lili.modules.payment.kit.core.PaymentHttpResponse; +import cn.lili.modules.payment.kit.core.enums.RequestMethodEnums; +import cn.lili.modules.payment.kit.core.enums.SignType; +import cn.lili.modules.payment.kit.core.kit.*; +import cn.lili.modules.payment.kit.core.utils.DateTimeZoneUtil; +import cn.lili.modules.payment.kit.dto.PayParam; +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.payment.kit.params.dto.CashierParam; +import cn.lili.modules.payment.kit.plugin.wechat.enums.WechatDomain; +import cn.lili.modules.payment.kit.plugin.wechat.model.*; +import cn.lili.modules.payment.service.PaymentService; +import cn.lili.modules.payment.service.RefundLogService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.connect.WechatConnectSetting; +import cn.lili.modules.system.entity.dto.connect.dto.WechatConnectSettingItem; +import cn.lili.modules.system.entity.dto.payment.WechatPaymentSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Map; +import java.util.Objects; + +/** + * 微信支付 + * + * @author Chopper + * @date 2020/12/21 17:44 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WechatPlugin implements Payment { + //收银台 + private final CashierSupport cashierSupport; + //支付日志 + private final PaymentService paymentService; + //缓存 + private final Cache cache; + //退款日志 + private final RefundLogService refundLogService; + //API域名 + private final ApiProperties apiProperties; + //配置 + private final SettingService settingService; + //联合登陆 + private final ConnectService connectService; + + + @Override + public ResultMessage h5pay(HttpServletRequest request, HttpServletResponse response1, PayParam payParam) { + + try { + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + + //支付参数准备 + SceneInfo sceneInfo = new SceneInfo(); + sceneInfo.setPayer_client_ip(IpKit.getRealIp(request)); + H5Info h5Info = new H5Info(); + h5Info.setType("WAP"); + sceneInfo.setH5_info(h5Info); + + //支付金额 + Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); + //第三方付款订单 + String outOrderNo = SnowFlake.getIdStr(); + //过期时间 + String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); + + //回传数据 + String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); + + WechatPaymentSetting setting = wechatPaymentSetting(); + UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() + .setAppid(setting.getAppId()) + .setMchid(setting.getMchId()) + .setDescription(cashierParam.getDetail()) + .setOut_trade_no(outOrderNo) + .setTime_expire(timeExpire) + .setAttach(attach) + .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT)) + .setAmount(new Amount().setTotal(fen)).setScene_info(sceneInfo); + + log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); + PaymentHttpResponse response = WechatApi.v3( + RequestMethodEnums.POST, + WechatDomain.CHINA.toString(), + cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.H5_PAY.toString(), + setting.getMchId(), + setting.getSerialNumber(), + null, + setting.getApiclient_key(), + JSONUtil.toJsonStr(unifiedOrderModel) + ); + + return ResultUtil.data(JSONUtil.toJsonStr(response.getBody())); + } catch (Exception e) { + e.printStackTrace(); + return ResultUtil.error(ResultCode.PAY_ERROR); + } + } + + @Override + public ResultMessage JSApiPay(HttpServletRequest request, PayParam payParam) { + + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Connect::getUserId, UserContext.getCurrentUser().getId()) + .eq(Connect::getUnionType, ConnectEnum.WECHAT_OPEN_ID.name()); + Connect connect = connectService.getOne(queryWrapper); + if (connect == null) { + return null; + } + + Payer payer = new Payer(); + payer.setOpenid(connect.getUnionId()); + + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + + //支付金额 + Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); + //第三方付款订单 + String outOrderNo = SnowFlake.getIdStr(); + //过期时间 + String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); + + String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); + + WechatPaymentSetting setting = wechatPaymentSetting(); + UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() + .setAppid(setting.getAppId()) + .setMchid(setting.getMchId()) + .setDescription(cashierParam.getDetail()) + .setOut_trade_no(outOrderNo) + .setTime_expire(timeExpire) + .setAttach(attach) + .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT)) + .setAmount(new Amount().setTotal(fen)) + .setPayer(payer); + + log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); + PaymentHttpResponse response = WechatApi.v3( + RequestMethodEnums.POST, + WechatDomain.CHINA.toString(), + cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.JS_API_PAY.toString(), + setting.getMchId(), + setting.getSerialNumber(), + null, + setting.getApiclient_key(), + JSONUtil.toJsonStr(unifiedOrderModel) + ); + // 根据证书序列号查询对应的证书来验证签名结果 + boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert()); + log.info("verifySignature: {}", verifySignature); + log.info("统一下单响应 {}", response); + + if (verifySignature) { + String body = response.getBody(); + JSONObject jsonObject = JSONUtil.parseObj(body); + String prepayId = jsonObject.getStr("prepay_id"); + Map map = WxPayKit.jsApiCreateSign(setting.getAppId(), prepayId, setting.getApiclient_key()); + log.info("唤起支付参数:{}", map); + + return ResultUtil.data(map); + } + log.error("微信支付参数验证错误,请及时处理"); + return ResultUtil.error(ResultCode.PAY_ERROR); + } catch (Exception e) { + log.error("支付异常", e); + return ResultUtil.error(ResultCode.PAY_ERROR); + } + } + + @Override + public ResultMessage appPay(HttpServletRequest request, PayParam payParam) { + + try { + + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + + //支付金额 + Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); + //第三方付款订单 + String outOrderNo = SnowFlake.getIdStr(); + //过期时间 + String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); + + String attach =URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); + + WechatPaymentSetting setting = wechatPaymentSetting(); + UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() + .setAppid(setting.getAppId()) + .setMchid(setting.getMchId()) + .setDescription(cashierParam.getDetail()) + .setOut_trade_no(outOrderNo) + .setTime_expire(timeExpire) + .setAttach(attach) + .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT)) + .setAmount(new Amount().setTotal(fen)); + + + log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); + PaymentHttpResponse response = WechatApi.v3( + RequestMethodEnums.POST, + WechatDomain.CHINA.toString(), + cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.APP_PAY.toString(), + setting.getMchId(), + setting.getSerialNumber(), + null, + setting.getApiclient_key(), + JSONUtil.toJsonStr(unifiedOrderModel) + ); + // 根据证书序列号查询对应的证书来验证签名结果 + boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert()); + log.info("verifySignature: {}", verifySignature); + log.info("统一下单响应 {}", response); + + if (verifySignature) { + JSONObject jsonObject = JSONUtil.parseObj(response.getBody()); + String prepayId = jsonObject.getStr("prepay_id"); + Map map = WxPayKit.appPrepayIdCreateSign(setting.getAppId(), + setting.getMchId(), + prepayId, + setting.getApiclient_key(), SignType.HMACSHA256); + log.info("唤起支付参数:{}", map); + + return ResultUtil.data(map); + } + log.error("微信支付参数验证错误,请及时处理"); + return ResultUtil.error(ResultCode.PAY_ERROR); + } catch (Exception e) { + log.error("支付异常", e); + return ResultUtil.error(ResultCode.PAY_ERROR); + } + } + + @Override + public ResultMessage nativePay(HttpServletRequest request, PayParam payParam) { + + try { + + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + + //支付金额 + Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); + //第三方付款订单 + String outOrderNo = SnowFlake.getIdStr(); + //过期时间 + String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); + + String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); + + WechatPaymentSetting setting = wechatPaymentSetting(); + UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() + .setAppid(setting.getAppId()) + .setMchid(setting.getMchId()) + .setDescription(cashierParam.getDetail()) + .setOut_trade_no(outOrderNo) + .setTime_expire(timeExpire) + //回传参数 + .setAttach(attach) + .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT)) + .setAmount(new Amount().setTotal(fen)); + + log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); + PaymentHttpResponse response = WechatApi.v3( + RequestMethodEnums.POST, + WechatDomain.CHINA.toString(), + cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.NATIVE_PAY.toString(), + setting.getMchId(), + setting.getSerialNumber(), + null, + setting.getApiclient_key(), + JSONUtil.toJsonStr(unifiedOrderModel) + ); + log.info("统一下单响应 {}", response); + // 根据证书序列号查询对应的证书来验证签名结果 + boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert()); + log.info("verifySignature: {}", verifySignature); + + if (verifySignature) { + return ResultUtil.data(new JSONObject(response.getBody()).getStr("code_url")); + } else { + log.error("微信支付参数验证错误,请及时处理"); + return ResultUtil.error(ResultCode.PAY_ERROR); + } + } catch (ServiceException e) { + log.error("支付异常", e); + return ResultUtil.error(ResultCode.PAY_ERROR); + } catch (Exception e) { + log.error("支付异常", e); + return ResultUtil.error(ResultCode.PAY_ERROR); + } + } + + @Override + public ResultMessage mpPay(HttpServletRequest request, PayParam payParam) { + + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Connect::getUserId, UserContext.getCurrentUser().getId()) + .eq(Connect::getUnionType, ConnectEnum.WECHAT_MP_OPEN_ID.name()); + Connect connect = connectService.getOne(queryWrapper); + if (connect == null) { + return null; + } + + Payer payer = new Payer(); + payer.setOpenid(connect.getUnionId()); + + CashierParam cashierParam = cashierSupport.cashierParam(payParam); + + //支付金额 + Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); + //第三方付款订单 + String outOrderNo = SnowFlake.getIdStr(); + //过期时间 + String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); + + //微信小程序,appid 需要单独获取,这里读取了联合登陆配置的appid ,实际场景小程序自动登录,所以这个appid是最为保险的做法 + //如果有2开需求,这里需要调整,修改这个appid的获取途径即可 + String appid = getWechatMPSetting().getAppId(); + + String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); + + WechatPaymentSetting setting = wechatPaymentSetting(); + UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() + .setAppid(appid) + .setMchid(setting.getMchId()) + .setDescription(cashierParam.getDetail()) + .setOut_trade_no(outOrderNo) + .setTime_expire(timeExpire) + .setAttach(attach) + .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT)) + .setAmount(new Amount().setTotal(fen)) + .setPayer(payer); + + log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); + PaymentHttpResponse response = WechatApi.v3( + RequestMethodEnums.POST, + WechatDomain.CHINA.toString(), + cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.JS_API_PAY.toString(), + setting.getMchId(), + setting.getSerialNumber(), + null, + setting.getApiclient_key(), + JSONUtil.toJsonStr(unifiedOrderModel) + ); + // 根据证书序列号查询对应的证书来验证签名结果 + boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert()); + log.info("verifySignature: {}", verifySignature); + log.info("统一下单响应 {}", response); + + if (verifySignature) { + String body = response.getBody(); + JSONObject jsonObject = JSONUtil.parseObj(body); + String prepayId = jsonObject.getStr("prepay_id"); + Map map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key()); + log.info("唤起支付参数:{}", map); + + return ResultUtil.data(map); + } + log.error("微信支付参数验证错误,请及时处理"); + return ResultUtil.error(ResultCode.PAY_ERROR); + } catch (Exception e) { + log.error("支付异常", e); + return ResultUtil.error(ResultCode.PAY_ERROR); + } + + } + + @Override + public void callBack(HttpServletRequest request) { + try { + verifyNotify(request); + } catch (Exception e) { + log.error("支付异常", e); + } + } + + @Override + public void notify(HttpServletRequest request) { + try { + verifyNotify(request); + } catch (Exception e) { + log.error("支付异常", e); + } + } + + /** + * 验证结果,执行支付回调 + * + * @param request + * @throws Exception + */ + private void verifyNotify(HttpServletRequest request) throws Exception { + + String timestamp = request.getHeader("Wechatpay-Timestamp"); + String nonce = request.getHeader("Wechatpay-Nonce"); + String serialNo = request.getHeader("Wechatpay-Serial"); + String signature = request.getHeader("Wechatpay-Signature"); + + log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature); + String result = HttpKit.readData(request); + log.info("微信支付通知密文 {}", result); + + WechatPaymentSetting setting = wechatPaymentSetting(); + // 校验服务器端响应¬ + String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, + setting.getApiKey3(), Objects.requireNonNull(getPlatformCert())); + + log.info("微信支付通知明文 {}", plainText); + + JSONObject jsonObject = JSONUtil.parseObj(plainText); + + String payParamStr = jsonObject.getStr("attach"); + String payParamJson = URLDecoder.decode(payParamStr,StandardCharsets.UTF_8); + PayParam payParam = JSONUtil.toBean(payParamJson,PayParam.class); + + + String tradeNo = jsonObject.getStr("transaction_id"); + Double totalAmount = CurrencyUtil.reversalFen(jsonObject.getJSONObject("amount").getDouble("total")); + + PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams( + PaymentMethodEnum.WECHAT.name(), + tradeNo, + totalAmount, + payParam + ); + + paymentService.success(paymentSuccessParams); + log.info("微信支付回调:支付成功{}", plainText); + } + + @Override + public void refund(RefundLog refundLog) { + + try { + + Amount amount = new Amount().setRefund(CurrencyUtil.fen(refundLog.getTotalAmount())) + .setTotal(CurrencyUtil.fen(refundLog.getPayPrice())); + + // 退款参数准备 + RefundModel refundModel = new RefundModel() + .setTransaction_id(refundLog.getPaymentReceivableNo()) + .setOut_refund_no(refundLog.getOutOrderNo()) + .setReason(refundLog.getRefundReason()) + .setAmount(amount) + .setNotify_url(refundNotifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT)); + + WechatPaymentSetting setting = wechatPaymentSetting(); + + log.info("微信退款参数 {}", JSONUtil.toJsonStr(refundModel)); + PaymentHttpResponse response = WechatApi.v3( + RequestMethodEnums.POST, + WechatDomain.CHINA.toString(), + cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.DOMESTIC_REFUNDS.toString(), + setting.getMchId(), + setting.getSerialNumber(), + null, + setting.getApiclient_key(), + JSONUtil.toJsonStr(refundModel) + ); + log.info("微信退款响应 {}", response); + // 退款申请成功 + if (response.getStatus() == 200) { + refundLogService.save(refundLog); + } else { + // 退款申请失败 + refundLog.setErrorMessage(response.getBody()); + refundLogService.save(refundLog); + } + } catch (Exception e) { + log.error("微信退款申请失败", e); + } + + } + + @Override + public void cancel(RefundLog refundLog) { + this.refund(refundLog); + } + + + @Override + public void refundNotify(HttpServletRequest request) { + String timestamp = request.getHeader("Wechatpay-Timestamp"); + String nonce = request.getHeader("Wechatpay-Nonce"); + String serialNo = request.getHeader("Wechatpay-Serial"); + String signature = request.getHeader("Wechatpay-Signature"); + + log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature); + String result = HttpKit.readData(request); + log.info("微信退款通知密文 {}", result); + JSONObject ciphertext = JSONUtil.parseObj(result); + if (!ciphertext.getStr("event_type").equals("REFUND.SUCCESS")) { + return; + } + try { + // 校验服务器端响应¬ + String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, + wechatPaymentSetting().getApiKey3(), Objects.requireNonNull(getPlatformCert())); + log.info("微信退款通知明文 {}", plainText); + JSONObject jsonObject = JSONUtil.parseObj(plainText); + String transactionId = jsonObject.getStr("transaction_id"); + String refundId = jsonObject.getStr("refund_id"); + + RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper().eq(RefundLog::getPaymentReceivableNo, transactionId)); + if (refundLog != null) { + refundLog.setIsRefund(true); + refundLog.setReceivableNo(refundId); + refundLogService.save(refundLog); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 获取微信支付配置 + * + * @return + */ + private WechatPaymentSetting wechatPaymentSetting() { + try { + Setting systemSetting = settingService.get(SettingEnum.WECHAT_PAYMENT.name()); + WechatPaymentSetting wechatPaymentSetting = JSONUtil.toBean(systemSetting.getSettingValue(), WechatPaymentSetting.class); + return wechatPaymentSetting; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(ResultCode.PAY_NOT_SUPPORT); + } + } + + /** + * 获取平台公钥 + * + * @return 平台公钥 + */ + private X509Certificate getPlatformCert() { + //获取缓存中的平台公钥,如果有则直接返回,否则去微信请求 + String publicCert = cache.getString(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix()); + if (!StringUtils.isEmpty(publicCert)) { + return PayKit.getCertificate(publicCert); + } + // 获取平台证书列表 + try { + + WechatPaymentSetting setting = wechatPaymentSetting(); + + PaymentHttpResponse response = WechatApi.v3( + RequestMethodEnums.GET, + WechatDomain.CHINA.toString(), + cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApi.GET_CERTIFICATES.toString(), + setting.getMchId(), + setting.getSerialNumber(), + null, + setting.getApiclient_key(), + "" + ); + String body = response.getBody(); + log.info("获取微信平台证书body: {}", body); + if (response.getStatus() == 200) { + JSONObject jsonObject = JSONUtil.parseObj(body); + JSONArray dataArray = jsonObject.getJSONArray("data"); + // 默认认为只有一个平台证书 + JSONObject encryptObject = dataArray.getJSONObject(0); + JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate"); + String associatedData = encryptCertificate.getStr("associated_data"); + String cipherText = encryptCertificate.getStr("ciphertext"); + String nonce = encryptCertificate.getStr("nonce"); + publicCert = getPlatformCertStr(associatedData, nonce, cipherText); + long second = (PayKit.getCertificate(publicCert).getNotAfter().getTime() - new Date().getTime()) / 1000; + cache.put(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix(), publicCert, second); + } else { + log.error("证书获取失败:{}" + body); + throw new ServiceException("证书获取失败:" + body); + } + return PayKit.getCertificate(publicCert); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取平台证书缓存的字符串 + * 下列各个密钥参数 + * + * @param associatedData 密钥参数 + * @param nonce 密钥参数 + * @param cipherText 密钥参数 + * @return platform key + * @throws GeneralSecurityException 密钥获取异常 + */ + private String getPlatformCertStr(String associatedData, String nonce, String cipherText) throws GeneralSecurityException { + + + AesUtil aesUtil = new AesUtil(wechatPaymentSetting().getApiKey3().getBytes(StandardCharsets.UTF_8)); + // 平台证书密文解密 + // encrypt_certificate 中的 associated_data nonce ciphertext + return aesUtil.decryptToString( + associatedData.getBytes(StandardCharsets.UTF_8), + nonce.getBytes(StandardCharsets.UTF_8), + cipherText + ); + } + + + /** + * 获取微信小程序配置 + * + * @return + */ + private WechatConnectSettingItem getWechatMPSetting() { + Setting setting = settingService.get(SettingEnum.WECHAT_CONNECT.name()); + + WechatConnectSetting wechatConnectSetting = JSONUtil.toBean(setting.getSettingValue(), WechatConnectSetting.class); + + if (wechatConnectSetting == null) { + return null; + } + //寻找对应对微信小程序登录配置 + for (WechatConnectSettingItem wechatConnectSettingItem : wechatConnectSetting.getWechatConnectSettingItems()) { + if (wechatConnectSettingItem.getClientType().equals(ClientTypeEnum.WECHAT_MP.name())) { + return wechatConnectSettingItem; + } + } + + return null; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatApi.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatApi.java new file mode 100644 index 00000000..864d1476 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatApi.java @@ -0,0 +1,577 @@ +package cn.lili.modules.payment.kit.plugin.wechat.enums; + +/** + * 微信api列表 + * + * @author Chopper + * @date 2020/12/17 17:43 + */ +public enum WechatApi { + + /** + * 沙箱环境 + */ + SAND_BOX_NEW("/sandboxnew"), + /** + * 获取沙箱环境验签秘钥 + */ + GET_SIGN_KEY("/sandboxnew/pay/getsignkey"), + /** + * 获取平台证书列表 + */ + GET_CERTIFICATES("/v3/certificates"), + + /** + * 营销专用-图片上传 + */ + MARKETING_UPLOAD_MEDIA("/v3/marketing/favor/media/image-upload"), + /** + * 通用接口-图片上传 + */ + MERCHANT_UPLOAD_MEDIA("/v3/merchant/media/upload"), + /** + * 通用接口-视频上传 + */ + MERCHANT_VIDEO_UPLOAD("/v3/merchant/media/video_upload"), + + + /** + * 创建/查询支付分订单 + */ + PAY_SCORE_SERVICE_ORDER("/v3/payscore/serviceorder"), + /** + * 取消支付分订单 + */ + PAY_SCORE_SERVICE_ORDER_CANCEL("/v3/payscore/serviceorder/%s/cancel"), + /** + * 修改支付分订单金额 + */ + PAY_SCORE_SERVICE_ORDER_MODIFY("/v3/payscore/serviceorder/%s/modify"), + /** + * 完结支付分订单 + */ + PAY_SCORE_SERVICE_ORDER_COMPLETE("/v3/payscore/serviceorder/%s/complete"), + /** + * 支付分订单收款 + */ + PAY_SCORE_SERVICE_ORDER_PAY("/v3/payscore/serviceorder/%s/pay"), + /** + * 同步服务订单信息 + */ + PAY_SCORE_SERVICE_ORDER_SYNC("/v3/payscore/serviceorder/%s/sync"), + /** + * 查询用户支付分开启状态 + */ + PAY_SCORE_USER_SERVICE_STATE("/v3/payscore/user-service-state"), + /** + * 商户解除用户授权关系 + */ + PAY_SCORE_PERMISSIONS_TERMINATE("/payscore/users/%s/permissions/%s/terminate"), + + /** + * 特约商户进件-提交申请单 + */ + APPLY_4_SUB("/v3/applyment4sub/applyment/"), + /** + * 特约商户进件-通过业务申请编号查询申请状态 + */ + GET_APPLY_STATE("/v3/applyment4sub/applyment/business_code/%s"), + /** + * 特约商户进件-通过申请单号查询申请状态 + */ + GET_APPLY_STATE_BY_ID("/v3/applyment4sub/applyment/applyment_id/%s"), + /** + * 特约商户进件-修改结算帐号 + */ + MODIFY_SETTLEMENT("/v3/apply4sub/sub_merchants/%s/modify-settlement"), + /** + * 特约商户进件-查询结算账户 + */ + GET_SETTLEMENT("/v3/apply4sub/sub_merchants/%s/settlement"), + + /** + * 商户开户意愿确认-提交申请单 OR 查询申请单审核结果 + */ + MER_OPEN_APPLY_SUBMIT_OR_RESULT("/v3/apply4subject/applyment"), + /** + * 商户开户意愿确认-撤销申请单 + */ + MER_OPEN_APPLY_CANCEL("/v3/apply4subject/applyment/%s/cancel"), + /** + * 商户开户意愿确认-获取商户开户意愿确认状态 + */ + GET_MER_OPEN_APPLY_STATE("/v3/apply4subject/applyment/merchants/%s/state"), + + /** + * 商业支付投诉-查询投诉信息 + */ + MERCHANT_SERVICE_COMPLAINTS("/v3/merchant-service/complaints"), + /** + * 商业支付投诉-创建/查询/更新/删除投诉通知回调 + */ + MERCHANT_SERVICE_COMPLAINTS_NOTIFICATIONS("/v3/merchant-service/complaint-notifications"), + + + /** + * 代金券-创建代金券批次 + */ + CREATE_COUPON_STOCKS("/v3/marketing/favor/coupon-stocks"), + /** + * 代金券-激活代金券批次 + */ + START_COUPON_STOCKS("/v3/marketing/favor/stocks/%s/start"), + /** + * 代金券-发放代金券 + */ + COUPON_SEND("/v3/marketing/favor/users/%s/coupons"), + /** + * 代金券-暂停代金券批次 + */ + PAUSE_COUPON_STOCKS("/v3/marketing/favor/stocks/%s/pause"), + /** + * 代金券-重启代金券批次 + */ + RESTART_COUPON_STOCKS("/v3/marketing/favor/stocks/%s/restart"), + /** + * 代金券-条件查询批次列表 + */ + QUERY_COUPON_STOCKS("/v3/marketing/favor/stocks"), + /** + * 代金券-查询批次详情 + */ + QUERY_COUPON_STOCKS_INFO("/v3/marketing/favor/stocks/%s"), + /** + * 代金券-查询代金券详情 + */ + QUERY_COUPON_INFO("/v3/marketing/favor/users/%s/coupons/%s"), + /** + * 代金券-查询代金券可用商户 + */ + QUERY_COUPON_MERCHANTS("/v3/marketing/favor/stocks/%s/merchants"), + /** + * 代金券-查询代金券可用单品 + */ + QUERY_COUPON_ITEMS("/v3/marketing/favor/stocks/%s/items"), + /** + * 代金券-根据商户号查用户的券 + */ + QUERY_USER_COUPON("/v3/marketing/favor/users/%s/coupons"), + /** + * 代金券-下载批次核销明细 + */ + COUPON_STOCKS_USER_FLOW_DOWNLOAD("/v3/marketing/favor/stocks/%s/use-flow"), + /** + * 代金券-下载批次退款明细 + */ + COUPON_STOCKS_REFUND_FLOW_DOWNLOAD("/v3/marketing/favor/stocks/%s/refund-flow"), + /** + * 代金券-设置消息通知地址 + */ + SETTING_COUPON_CALLBACKS("/v3/marketing/favor/callbacks"), + /** + * 商家券-创建商家券 + */ + CREATE_BUSINESS_COUPON("/v3/marketing/busifavor/stocks"), + /** + * 发放消费卡 + */ + SEND_BUSINESS_COUPON("/v3/marketing/busifavor/coupons/%s/send"), + /** + * H5 发券 + */ + H5_SEND_COUPON("/busifavor/getcouponinfo"), + /** + * 商家券-查询商家券批次详情 + */ + QUERY_BUSINESS_COUPON_STOCKS_INFO("/v3/marketing/busifavor/stocks/%s"), + /** + * 商家券-查询商家券批次详情 + */ + USE_BUSINESS_COUPON("/v3/marketing/busifavor/coupons/use"), + /** + * 商家券-根据过滤条件查询用户券 + */ + QUERY_BUSINESS_USER_COUPON("/v3/marketing/busifavor/users/%s/coupons"), + /** + * 商家券-查询用户单张券详情 + */ + QUERY_BUSINESS_USER_COUPON_INFO("/v3/marketing/busifavor/users/%s/coupons/%s/appids/%s"), + /** + * 商家券-上传预存code + */ + BUSINESS_COUPON_UPLOAD_CODE("/v3/marketing/busifavor/stocks/%/couponcodes"), + /** + * 商家券-设置/查询商家券事件通知地址 + */ + BUSINESS_COUPON_CALLBACKS("/v3/marketing/busifavor/callbacks"), + /** + * 关联订单信息 + */ + BUSINESS_COUPON_ASSOCIATE("/v3/marketing/busifavor/coupons/associate"), + /** + * 取消关联订单信息 + */ + BUSINESS_COUPON_DISASSOCIATE("/v3/marketing/busifavor/coupons/disassociate"), + + /** + * 支付有礼-创建全场满额送活动 + */ + PAY_GIFT_ACTIVITY("/v3/marketing/paygiftactivity/unique-threshold-activity"), + /** + * 支付有礼-获取支付有礼活动列表 + */ + PAY_GIFT_ACTIVITY_GET("/v3/marketing/paygiftactivity/activities"), + /** + * 支付有礼-查询活动详情接口 + */ + PAY_GIFT_ACTIVITY_INFO("/v3/marketing/paygiftactivity/activities/%s"), + /** + * 支付有礼-查询活动发券商户号 + */ + PAY_GIFT_ACTIVITY_QUERY_MER("/v3/marketing/paygiftactivity/activities/%s/merchants"), + /** + * 支付有礼-查询活动指定商品列表 + */ + PAY_GIFT_ACTIVITY_QUERY_GOODS("/v3/marketing/paygiftactivity/activities/%s/goods"), + /** + * 支付有礼-终止活动 + */ + PAY_GIFT_ACTIVITY_TERMINATE("/v3/marketing/paygiftactivity/activities/%s/terminate"), + /** + * 支付有礼-新增活动发券商户号 + */ + PAY_GIFT_ACTIVITY_ADD_MERCHANTS("/v3/marketing/paygiftactivity/activities/%s/merchants/add"), + /** + * 支付有礼-删除活动发券商户号 + */ + PAY_GIFT_ACTIVITY_DELETE_MERCHANTS("/v3/marketing/paygiftactivity/activities/%s/merchants/delete"), + + + /** + * 点金计划-点金计划管理 + */ + CHANGE_GOLD_PLAN_STATUS("/v3/goldplan/merchants/changegoldplanstatus"), + /** + * 点金计划-商家小票管理 + */ + CHANGE_CUSTOM_PAGE_STATUS("/v3/goldplan/merchants/changecustompagestatus"), + /** + * 点金计划-同业过滤标签管理 + */ + SET_ADVERTISING_INDUSTRY_FILTER("/v3/goldplan/merchants/set-advertising-industry-filter"), + + + /** + * 电商收付通-二级商户进件 + */ + E_COMMERCE_APPLY("/v3/ecommerce/applyments/"), + /** + * 电商收付通-查询进件申请状态 + */ + E_COMMERCE_APPLY_STATE("/v3/ecommerce/applyments/%s"), + /** + * 电商收付通-通过业务申请编号查询申请状态 + */ + E_COMMERCE_APPLY_STATE_BY_NO("/v3/ecommerce/applyments/out-request-no/%s"), + + /** + * 合单下单-APP支付 + */ + COMBINE_TRANSACTIONS_APP("/v3/combine-transactions/app"), + /** + * 合单下单-JS支付 + */ + COMBINE_TRANSACTIONS_JS("/v3/combine-transactions/jsapi"), + /** + * 合单下单-H5支付 + */ + COMBINE_TRANSACTIONS_H5("/v3/combine-transactions/h5"), + /** + * 合单下单-Native支付 + */ + COMBINE_TRANSACTIONS_NATIVE("/v3/combine-transactions/native"), + /** + * 合单下单-合单查询订单 + */ + COMBINE_TRANSACTIONS_QUERY("/v3/combine-transactions/out-trade-no/%s"), + /** + * 合单下单-合单关闭订单 + */ + COMBINE_TRANSACTIONS_CLOSE("/v3/combine-transactions/out-trade-no/%s/close"), + + /** + * 电商收付通-补差接口-请求补差 + */ + CREATE_SUBSIDIES("v3/ecommerce/subsidies/create"), + /** + * 电商收付通-补差接口-请求补差回退 + */ + RETURN_SUBSIDIES("/v3/ecommerce/subsidies/return"), + /** + * 电商收付通-补差接口-取消补差 + */ + CANCEL_SUBSIDIES("/v3/ecommerce/subsidies/cancel"), + /** + * 电商收付通-分账接口-请求分账/查询分账结果 + */ + PROFIT_SHARING_ORDERS("/v3/ecommerce/profitsharing/orders"), + /** + * 电商收付通-分账接口-查询分账回退结果 + */ + PROFIT_SHARING_RETURN_ORDERS("/v3/ecommerce/profitsharing/returnorders"), + /** + * 电商收付通-分账接口-完结分账 + */ + PROFIT_SHARING_FINISH_ORDER("/v3/ecommerce/profitsharing/finish-order"), + /** + * 添加分账接收方 + */ + PROFIT_SHARING_RECEIVERS_ADD("/v3/ecommerce/profitsharing/receivers/add"), + /** + * 删除分账接收方 + */ + PROFIT_SHARING_RECEIVERS_DELETE("/v3/ecommerce/profitsharing/receivers/delete"), + /** + * 退款接口-退款申请 + */ + DOMESTIC_REFUNDS("/v3/refund/domestic/refunds"), + /** + * 电商收付通-退款接口-退款申请 + */ + E_COMMERCE_REFUNDS("/v3/ecommerce/refunds/apply"), + /** + * 电商收付通-退款接口-通过微信支付退款单号查询退款 + */ + QUERY_REFUND("/v3/ecommerce/refunds/id/%s"), + /** + * 电商收付通-退款接口-通过商户退款单号查询退款 + */ + QUERY_REFUNDS_BY_REFUND_NO("/v3/ecommerce/refunds/out-refund-no/%s"), + /** + * 电商收付通-余额查询接口 + */ + QUERY_BALANCE("/v3/ecommerce/fund/balance/%s"), + /** + * 查询二级商户账户日终余额 + */ + QUERY_END_DAY_BALANCE("/v3/ecommerce/fund/enddaybalance/%s"), + /** + * 查询电商平台账户实时余额 + */ + QUERY_MERCHANT_BALANCE("/v3/merchant/fund/balance/%s"), + /** + * 查询电商平台账户日终余额 + */ + QUERY_MERCHANT_END_DAY_BALANCE("/v3/merchant/fund/dayendbalance/%s"), + /** + * 电商收付通-提现接口-账户余额提现 + */ + WITHDRAW("/v3/ecommerce/fund/withdraw"), + /** + * 电商收付通-提现接口-提现状态查询 + */ + QUERY_WITHDRAW("/v3/ecommerce/fund/withdraw/%s"), + /** + * 电商收付通-提现接口-商户提现单号查询 + */ + QUERY_WITHDRAW_BY_OUT_REQUEST_NO("/v3/ecommerce/fund/withdraw/out-request-no/%s"), + /** + * 电商收付通-提现接口-电商平台提现 + */ + MERCHANT_WITHDRAW("/v3/merchant/fund/withdraw"), + /** + * 电商收付通-提现接口-微信支付提现单号查询 + */ + QUERY_MERCHANT_WITHDRAW("/v3/ecommerce/fund/withdraw/%s"), + /**` + * 电商收付通-提现接口-商户提现单号查询 + */ + QUERY_MERCHANT_WITHDRAW_BY_OUT_REQUEST_NO("/v3/merchant/fund/withdraw/out-request-no/%s"), + /** + * 电商收付通-提现接口-按日下载提现异常文件 + */ + WITHDRAW_BILL("/v3/merchant/fund/withdraw/bill-type/%s"), + /** + * 申请交易账单 + */ + TRADE_BILL("/v3/bill/tradebill"), + /** + * 申请资金账单 + */ + FUND_FLOW_BILL("/v3/bill/fundflowbill"), + /** + * 下载账单 + */ + BILL_DOWNLOAD("/v3/billdownload/file"), + + /** + * 银行特约商户违规信息查询 + */ + GET_VIOLATION("/risk/getviolation"), + /** + * 事前-风险商户核查接口 + */ + QUERY_MCH_RISK("/mchrisk/querymchrisk"), + /** + * 事后-风险商户处理结果同步接口 + */ + SYNC_MCH_RISK_RESULT("/mchrisk/syncmchriskresult"), + /** + * 间联模式查询商户审核状态开放接口 + */ + BANK_QUERY_MCH_AUDIT_INFO("/mchrisk/bankquerymchauditinfo"), + /** + * 渠道商查询商户审核信息 + */ + CHANNEL_QUERY_MCH_AUDIT_INFO("/mchrisk/channelquerymchauditinfo"), + /** + * 设置风险通知回调链接 + */ + SET_MCH_RISK_CALLBACK("/mchrisk/setmchriskcallback"), + + /** + * 向员工付款 + */ + PAY_WWS_TRANS_2_POCKET("/mmpaymkttransfers/promotion/paywwsptrans2pocket"), + /** + * 查询向员工付款记录 + */ + QUERY_WWS_TRANS_2_POCKET("/mmpaymkttransfers/promotion/querywwsptrans2pocket"), + /** + * 发放企业红包 + */ + SEND_WORK_WX_RED_PACK("/mmpaymkttransfers/sendworkwxredpack"), + /** + * 查询企业红包记录 + */ + QUERY_WORK_WX_RED_PACK("/mmpaymkttransfers/queryworkwxredpack"), + + /** + * 查询/更新先享卡订单 + */ + DISCOUNT_CARD_ORDER("/v3/discount-card/orders/%s"), + /** + * 查询先享卡订单 + */ + DISCOUNT_CARD_ORDER_TRADE_NO("/v3/discount-card/orders/out-trade-no/%s"), + + + /** + * 服务人员注册 + */ + SMART_GUIDE_GUIDES("/v3/smartguide/guides"), + /** + * 服务人员分配 + */ + SMART_GUIDE_GUIDES_ASSIGN("/v3/smartguide/guides/%s/assign"), + /** + * 服务人员信息更新 + */ + SMART_GUIDE_GUIDES_UPDATE("/v3/smartguide/guides/%s"), + + /** + * 报关接口-订单附加信息提交接口 + */ + CUSTOM_DECLARE_ORDER("/cgi-bin/mch/customs/customdeclareorder"), + /** + * 报关接口-订单附加信息查询接口 + */ + CUSTOM_DECLARE_QUERY("/cgi-bin/mch/customs/customdeclarequery"), + /** + * 报关接口-订单附加信息重推接口 + */ + CUSTOM_DECLARE_RE_DECLARE("/cgi-bin/mch/newcustoms/customdeclareredeclare"), + + /** + * APP 下单 API + */ + APP_PAY("/v3/pay/transactions/app"), + PARTNER_APP_PAY("/v3/pay/partner/transactions/app"), + /** + * JS API 下单 API + */ + JS_API_PAY("/v3/pay/transactions/jsapi"), + PARTNER_JS_API_PAY("/v3/pay/partner/transactions/jsapi"), + /** + * Native 下单 API + */ + NATIVE_PAY("/v3/pay/transactions/native"), + PARTNER_NATIVE_PAY("/v3/pay/partner/transactions/native"), + /** + * H5 下单 API + */ + H5_PAY("/v3/pay/transactions/h5"), + PARTNER_H5_PAY("/v3/pay/partner/transactions/h5"), + /** + * 微信支付订单号查询 + */ + ORDER_QUERY_BY_ID("/v3/pay/transactions/id/%s"), + PARTNER_ORDER_QUERY_BY_ID("/v3/pay/partner/transactions/id/%s"), + /** + * 商户订单号查询 + */ + ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"), + PARTNER_ORDER_QUERY_BY_NO("/v3/pay/partner/transactions/out-trade-no/%s"), + /** + * 关闭订单 + */ + CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"), + PARTNER_CLOSE_ORDER_BY_NO("/v3/pay/partner/transactions/out-trade-no/%s/close"), + + + /** + * 委托营销-建立合作关系 + */ + PARTNERSHIPS_BUILD("/v3/marketing/partnerships/build"), + /** + * 委托营销-终止合作关系 + */ + PARTNERSHIPS_TERMINATE("/v3/marketing/partnerships/terminate"), + + + /** + * 智慧商圈-商圈积分同步 + */ + BUSINESS_CIRCLE_POINTS_NOTIFY("/v3/businesscircle/points/notify"), + + + /** + * 连锁品牌-分账-查询分账 + */ + BRAND_PROFIT_SHARING_ORDERS("/v3/brand/profitsharing/orders"), + /** + * 连锁品牌-分账回退-查询分账回退 + */ + BRAND_PROFIT_SHARING_RETURN_ORDERS("/v3/brand/profitsharing/returnorders"), + /** + * 连锁品牌-完结分账 + */ + BRAND_PROFIT_SHARING_FINISH_ORDER("/v3/brand/profitsharing/finish-order"), + /** + * 连锁品牌-添加分账接收方 + */ + BRAND_PROFIT_SHARING_RECEIVERS_ADD("/v3/brand/profitsharing/receivers/add"), + /** + * 连锁品牌-删除分账接收方 + */ + BRAND_PROFIT_SHARING_RECEIVERS_delete("/v3/brand/profitsharing/receivers/delete"), + ; + + /** + * 类型 + */ + private final String url; + + WechatApi(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + @Override + public String toString() { + return url; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatDomain.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatDomain.java new file mode 100644 index 00000000..b9fc7866 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/enums/WechatDomain.java @@ -0,0 +1,58 @@ +package cn.lili.modules.payment.kit.plugin.wechat.enums; + +/** + * 微信支付域名 + * + * @author Chopper + * @date 2020/12/17 17:44 + */ +public enum WechatDomain { + /** + * 中国国内 + */ + CHINA("https://api.mch.weixin.qq.com"), + /** + * 中国国内(备用域名) + */ + CHINA2("https://api2.mch.weixin.qq.com"), + /** + * 东南亚 + */ + HK("https://apihk.mch.weixin.qq.com"), + /** + * 其它 + */ + US("https://apius.mch.weixin.qq.com"), + /** + * 获取公钥 + */ + FRAUD("https://fraud.mch.weixin.qq.com"), + /** + * 活动 + */ + ACTION("https://action.weixin.qq.com"), + /** + * 刷脸支付 + * PAY_APP + */ + PAY_APP("https://payapp.weixin.qq.com"); + + + /** + * 域名 + */ + private final String domain; + + WechatDomain(String domain) { + this.domain = domain; + } + + public String getType() { + return domain; + } + + @Override + public String toString() { + return domain; + } +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/Amount.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/Amount.java new file mode 100644 index 00000000..cc8e6d31 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/Amount.java @@ -0,0 +1,30 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 统一下单-订单金额 + * + * @author Chopper + * @date 2020/12/17 17:44 + */ +@Data +@Accessors(chain = true) +public class Amount { + + /** + * 总金额 + */ + private Integer total; + + /** + * 货币类型 + */ + private String currency = "CNY"; + + /** + * 退款金额 + */ + private Integer refund; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/Detail.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/Detail.java new file mode 100644 index 00000000..b8a550a5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/Detail.java @@ -0,0 +1,30 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + + +/** + * 统一下单-优惠功能 + * + * @author Chopper + * @date 2020/12/17 17:46 + */ +@Data +@Accessors(chain = true) +public class Detail { + /** + * 订单原价 + */ + private int cost_price; + /** + * 商品小票ID + */ + private String invoice_id; + /** + * 单品列表 + */ + private List goods_detail; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/GoodsDetail.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/GoodsDetail.java new file mode 100644 index 00000000..3225d939 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/GoodsDetail.java @@ -0,0 +1,36 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 统一下单-单品列表 + * + * @author Chopper + * @date 2020/12/17 17:47 + */ + +@Data +@Accessors(chain = true) +public class GoodsDetail { + /** + * 商户侧商品编码 + */ + private String merchant_goods_id; + /** + * 微信侧商品编码 + */ + private String wechatpay_goods_id; + /** + * 商品名称 + */ + private String goods_name; + /** + * 商品数量 + */ + private int quantity; + /** + * 商品单价 + */ + private int unit_price; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/H5Info.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/H5Info.java new file mode 100644 index 00000000..d63430d9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/H5Info.java @@ -0,0 +1,36 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 统一下单-H5 场景信息 + * + * @author Chopper + * @date 2020/12/17 17:56 + */ + +@Data +@Accessors(chain = true) +public class H5Info { + /** + * 场景类型 + */ + private String type; + /** + * 应用名称 + */ + private String app_name; + /** + * 网站URL + */ + private String app_url; + /** + * iOS 平台 BundleID + */ + private String bundle_id; + /** + * Android 平台 PackageName + */ + private String package_name; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/Payer.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/Payer.java new file mode 100644 index 00000000..f6e0cf7d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/Payer.java @@ -0,0 +1,28 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 统一下单-支付者 + * + * @author Chopper + * @date 2020/12/17 17:56 + */ + +@Data +@Accessors(chain = true) +public class Payer { + /** + * 用户标识 + */ + private String openid; + /** + * 用户服务标识 + */ + private String sp_openid; + /** + * 用户子标识 + */ + private String sub_openid; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/RefundModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/RefundModel.java new file mode 100644 index 00000000..f855e6bb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/RefundModel.java @@ -0,0 +1,50 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + + +/** + * 国内退款-退款申请 + * + * @author Chopper + * @date 2020/12/17 17:58 + */ +@Data +@Accessors(chain = true) +public class RefundModel { + + + /** + * 原支付交易对应的微信订单号 + */ + private String transaction_id; + + /** + * 商户订单号 + */ + private String out_trade_no; + + /** + * 商户退款单号 + */ + private String out_refund_no; + + /** + * 退款理由 + */ + private String reason; + + /** + * 退款金额 + */ + private Amount amount; + + /** + * 通知地址 + */ + private String notify_url; + +} + + diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/SceneInfo.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/SceneInfo.java new file mode 100644 index 00000000..879511ba --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/SceneInfo.java @@ -0,0 +1,32 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 统一下单-场景信息 + * + * @author Chopper + * @date 2020/12/17 17:57 + */ + +@Data +@Accessors(chain = true) +public class SceneInfo { + /** + * 用户终端IP + */ + private String payer_client_ip; + /** + * 商户端设备号 + */ + private String device_id; + /** + * 商户门店信息 + */ + private StoreInfo store_info; + /** + * H5 场景信息 + */ + private H5Info h5_info; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/SettleInfo.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/SettleInfo.java new file mode 100644 index 00000000..9e11f8fc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/SettleInfo.java @@ -0,0 +1,23 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 统一下单-结算信息 + * + * @author Chopper + * @date 2020/12/17 17:58 + */ +@Data +@Accessors(chain = true) +public class SettleInfo { + /** + * 是否指定分账 + */ + private boolean profit_sharing; + /** + * 补差金额 + */ + private int subsidy_amount; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/StoreInfo.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/StoreInfo.java new file mode 100644 index 00000000..a4aef092 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/StoreInfo.java @@ -0,0 +1,32 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 统一下单-商户门店信息 + * + * @author Chopper + * @date 2020/12/17 17:58 + */ + +@Data +@Accessors(chain = true) +public class StoreInfo { + /** + * 门店编号 + */ + private String id; + /** + * 门店名称 + */ + private String name; + /** + * 地区编码 + */ + private String area_code; + /** + * 详细地址 + */ + private String address; +} diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/UnifiedOrderModel.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/UnifiedOrderModel.java new file mode 100644 index 00000000..91adc5be --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/UnifiedOrderModel.java @@ -0,0 +1,87 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; + +import lombok.Data; +import lombok.experimental.Accessors; + + +/** + * 统一下单-商户门店信息 + * + * @author Chopper + * @date 2020/12/17 17:58 + */ + +@Data +@Accessors(chain = true) +public class UnifiedOrderModel { + /** + * 公众号ID + */ + private String appid; + /** + * 服务商公众号ID + */ + private String sp_appid; + /** + * 直连商户号 + */ + private String mchid; + /** + * 服务商户号 + */ + private String sp_mchid; + /** + * 子商户公众号ID + */ + private String sub_appid; + /** + * 子商户号 + */ + private String sub_mchid; + /** + * 商品描述 + */ + private String description; + /** + * 商户订单号 + */ + private String out_trade_no; + /** + * 交易结束时间 + */ + private String time_expire; + /** + * 附加数据 + */ + private String attach; + /** + * 通知地址 + */ + private String notify_url; + /** + * 订单优惠标记 + */ + private String goods_tag; + /** + * 结算信息 + */ + private SettleInfo settle_info; + /** + * 订单金额 + */ + private Amount amount; + /** + * 支付者 + */ + private Payer payer; + /** + * 优惠功能 + */ + private Detail detail; + /** + * 场景信息 + */ + private SceneInfo scene_info; +} + + diff --git a/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/package-info.java b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/package-info.java new file mode 100644 index 00000000..f32da037 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/kit/plugin/wechat/model/package-info.java @@ -0,0 +1,2 @@ +package cn.lili.modules.payment.kit.plugin.wechat.model; +//这个目录的很多类的属性都是下划线分割,不符合本产品的驼峰类型约定,后续会进行处理 \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/mapper/RefundLogMapper.java b/framework/src/main/java/cn/lili/modules/payment/mapper/RefundLogMapper.java new file mode 100644 index 00000000..73a4ade7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/mapper/RefundLogMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.payment.mapper; + +import cn.lili.modules.payment.entity.RefundLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 退款日志数据层 + * @author Chopper + * @date 2020-12-19 09:25 + */ +public interface RefundLogMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/service/PaymentService.java b/framework/src/main/java/cn/lili/modules/payment/service/PaymentService.java new file mode 100644 index 00000000..f78efc8d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/service/PaymentService.java @@ -0,0 +1,41 @@ +package cn.lili.modules.payment.service; + +import cn.lili.modules.order.order.entity.vo.PaymentLog; +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +/** + * 支付日志 业务层 + * + * @author Chopper + * @date 2020-12-19 09:25 + */ +public interface PaymentService { + + /** + * 支付成功通知 + * + * @param paymentSuccessParams 支付成功回调参数 + */ + void success(PaymentSuccessParams paymentSuccessParams); + + + /** + * 平台支付成功 + * + * @param paymentSuccessParams 支付成功回调参数 + */ + void adminPaySuccess(PaymentSuccessParams paymentSuccessParams); + + + /** + * 获取支付日志 + * + * @param initPage + * @param initWrapper + * @return + */ + IPage page(Page initPage, QueryWrapper initWrapper); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/service/RefundLogService.java b/framework/src/main/java/cn/lili/modules/payment/service/RefundLogService.java new file mode 100644 index 00000000..6b01ea1c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/service/RefundLogService.java @@ -0,0 +1,13 @@ +package cn.lili.modules.payment.service; + +import cn.lili.modules.payment.entity.RefundLog; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 退款日志 业务层 + * + * @author Chopper + * @date 2020-12-19 09:25 + */ +public interface RefundLogService extends IService { +} diff --git a/framework/src/main/java/cn/lili/modules/payment/serviceimpl/PaymentServiceImpl.java b/framework/src/main/java/cn/lili/modules/payment/serviceimpl/PaymentServiceImpl.java new file mode 100644 index 00000000..ec52e5a9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/serviceimpl/PaymentServiceImpl.java @@ -0,0 +1,69 @@ +package cn.lili.modules.payment.serviceimpl; + +import cn.lili.modules.order.order.entity.vo.PaymentLog; +import cn.lili.modules.order.order.mapper.OrderMapper; +import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; +import cn.lili.modules.payment.kit.CashierSupport; +import cn.lili.modules.payment.kit.params.CashierExecute; +import cn.lili.modules.payment.service.PaymentService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 支付日志 业务实现 + * + * @author Chopper + * @date 2020-12-19 09:25 + */ +@Slf4j +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PaymentServiceImpl implements PaymentService { + + @Autowired + private List cashierExecutes; + @Autowired + private CashierSupport cashierSupport; + + private final OrderMapper orderMapper; + + @Override + public void success(PaymentSuccessParams paymentSuccessParams) { + + boolean paymentResult = cashierSupport.paymentResult(paymentSuccessParams.getPayParam()); + if (paymentResult) { + log.warn("订单支付状态后,调用支付成功接口,流水号:{}", paymentSuccessParams.getReceivableNo()); + return; + } + + log.debug("支付成功,第三方流水:{}", paymentSuccessParams.getReceivableNo()); + //支付结果处理 + for (CashierExecute cashierExecute : cashierExecutes) { + cashierExecute.paymentSuccess(paymentSuccessParams); + } + } + + @Override + public void adminPaySuccess(PaymentSuccessParams paymentSuccessParams) { + + log.debug("支付状态修改成功->银行转账"); + //支付结果处理 + for (CashierExecute cashierExecute : cashierExecutes) { + cashierExecute.paymentSuccess(paymentSuccessParams); + } + } + + @Override + public IPage page(Page initPage, QueryWrapper initWrapper) { + return orderMapper.queryPaymentLogs(initPage, initWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/payment/serviceimpl/RefundLogServiceImpl.java b/framework/src/main/java/cn/lili/modules/payment/serviceimpl/RefundLogServiceImpl.java new file mode 100644 index 00000000..601374ac --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/payment/serviceimpl/RefundLogServiceImpl.java @@ -0,0 +1,23 @@ +package cn.lili.modules.payment.serviceimpl; + +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.mapper.RefundLogMapper; +import cn.lili.modules.payment.service.RefundLogService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 退款日志 业务实现 + * + * @author Chopper + * @date 2020-12-19 09:25 + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class RefundLogServiceImpl extends ServiceImpl implements RefundLogService { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/SettingKeys.java b/framework/src/main/java/cn/lili/modules/permission/SettingKeys.java new file mode 100644 index 00000000..5065f4c0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/SettingKeys.java @@ -0,0 +1,17 @@ +package cn.lili.modules.permission; + +/** + * 系统秘钥 + * + * @author paulG + * @date 2020/11/17 4:23 下午 + */ +public enum SettingKeys { + + + /** + * ES_SIGN 分词词库秘钥 + */ + ES_SIGN + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/dos/AdminUser.java b/framework/src/main/java/cn/lili/modules/permission/entity/dos/AdminUser.java new file mode 100644 index 00000000..52993199 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/dos/AdminUser.java @@ -0,0 +1,66 @@ +package cn.lili.modules.permission.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 管理员类 + * + * @author Chopper + * @date 2020/11/19 11:42 + */ +@Data +@Entity +@Table(name = "li_admin_user") +@TableName("li_admin_user") +@ApiModel(value = "管理员") +public class AdminUser extends BaseEntity { + + private static final long serialVersionUID = 2918352800205024873L; + + @ApiModelProperty(value = "用户名") + @Column(unique = true, nullable = false, columnDefinition = "varchar(200)") + private String username; + + @ApiModelProperty(value = "密码") + private String password; + + @ApiModelProperty(value = "昵称") + private String nickName; + + @ApiModelProperty(value = "手机") + private String mobile; + + @ApiModelProperty(value = "邮件") + private String email; + + @ApiModelProperty(value = "用户头像") + @Column(length = 1000) + private String avatar = "https://i.loli.net/2020/11/19/LyN6JF7zZRskdIe.png"; + + @ApiModelProperty(value = "是否是超级管理员 超级管理员/普通管理员") + private Boolean isSuper = false; + + @ApiModelProperty(value = "状态 默认true正常 false禁用") + private Boolean status = true; + + @ApiModelProperty(value = "描述/详情/备注") + private String description; + + @ApiModelProperty(value = "所属部门id") + private String departmentId; + /** + * 冗余字段 + */ + @ApiModelProperty(value = "角色id集合") + private String roleIds; + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/dos/Department.java b/framework/src/main/java/cn/lili/modules/permission/entity/dos/Department.java new file mode 100644 index 00000000..090c633f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/dos/Department.java @@ -0,0 +1,39 @@ +package cn.lili.modules.permission.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.math.BigDecimal; + + +/** + * 部门 + * + * @author Chopper + * @date 2020/11/19 11:57 + */ +@Data +@Entity +@Table(name = "li_department") +@TableName("li_department") +@ApiModel(value = "部门") +public class Department extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "部门名称") + private String title; + + @ApiModelProperty(value = "父id") + private String parentId; + + @ApiModelProperty(value = "排序值") + @Column(precision = 10, scale = 2) + private BigDecimal sortOrder; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/dos/DepartmentRole.java b/framework/src/main/java/cn/lili/modules/permission/entity/dos/DepartmentRole.java new file mode 100644 index 00000000..892747ab --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/dos/DepartmentRole.java @@ -0,0 +1,38 @@ +package cn.lili.modules.permission.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 角色部门绑定关系 + * + * @author Chopper + * @date 2020/11/19 12:18 + */ +@Data +@Entity +@Table(name = "li_department_role") +@TableName("li_department_role") +@ApiModel(value = "角色部门") +@NoArgsConstructor +@AllArgsConstructor +public class DepartmentRole extends BaseEntity { + + + private static final long serialVersionUID = 2342812932116647050L; + + @ApiModelProperty(value = "角色id") + private String roleId; + + @ApiModelProperty(value = "部门id") + private String departmentId; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/dos/Menu.java b/framework/src/main/java/cn/lili/modules/permission/entity/dos/Menu.java new file mode 100644 index 00000000..8d34b045 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/dos/Menu.java @@ -0,0 +1,61 @@ +package cn.lili.modules.permission.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * 菜单权限 + * + * @author Chopper + * @date 2020/11/19 12:12 + */ +@Data +@Entity +@Table(name = "li_menu") +@TableName("li_menu") +@ApiModel(value = "菜单权限") +public class Menu extends BaseEntity { + + private static final long serialVersionUID = 7050744476203495207L; + + @ApiModelProperty(value = "菜单/权限名称") + private String name; + + @ApiModelProperty(value = "层级") + private Integer level; + + @ApiModelProperty(value = "菜单标题") + private String title; + + @ApiModelProperty(value = "赋权API地址,正则表达式") + private String path; + + @ApiModelProperty(value = "前端路由") + private String frontRoute; + + @ApiModelProperty(value = "图标") + private String icon; + + @ApiModelProperty(value = "父id") + private String parentId = "0"; + + @ApiModelProperty(value = "说明备注") + private String description; + + @ApiModelProperty(value = "排序值") + @Column(precision = 10, scale = 2) + private BigDecimal sortOrder; + + @ApiModelProperty(value = "文件地址") + private String frontComponent; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/dos/Role.java b/framework/src/main/java/cn/lili/modules/permission/entity/dos/Role.java new file mode 100644 index 00000000..72e33f86 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/dos/Role.java @@ -0,0 +1,38 @@ +package cn.lili.modules.permission.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 角色 + * + * @author Chopper + * @date 2020/11/19 11:57 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Entity +@Table(name = "li_role") +@TableName("li_role") +@ApiModel(value = "角色") +public class Role extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "角色名") + private String name; + + @ApiModelProperty(value = "是否为注册默认角色") + private Boolean defaultRole; + + @ApiModelProperty(value = "备注") + private String description; + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/dos/RoleMenu.java b/framework/src/main/java/cn/lili/modules/permission/entity/dos/RoleMenu.java new file mode 100644 index 00000000..46177b63 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/dos/RoleMenu.java @@ -0,0 +1,36 @@ +package cn.lili.modules.permission.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 角色权限绑定关系 + * + * @author Chopper + * @date 2020/11/19 12:18 + */ +@Data +@Entity +@Table(name = "li_role_menu") +@TableName("li_role_menu") +@ApiModel(value = "角色权限") +public class RoleMenu extends BaseEntity { + + private static final long serialVersionUID = -4680260092546996026L; + + @ApiModelProperty(value = "角色id") + private String roleId; + + @ApiModelProperty(value = "菜单") + private String menuId; + + @ApiModelProperty(value = "是否拥有操作数据权限,为否则只有查看权限") + private Boolean isSuper; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/dos/UserRole.java b/framework/src/main/java/cn/lili/modules/permission/entity/dos/UserRole.java new file mode 100644 index 00000000..7ce23182 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/dos/UserRole.java @@ -0,0 +1,39 @@ +package cn.lili.modules.permission.entity.dos; + +import cn.lili.base.IdEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 用户角色 + * + * @author Chopper + * @date 2020/11/19 12:18 + */ +@Data +@Entity +@Table(name = "li_user_role") +@TableName("li_user_role") +@ApiModel(value = "用户角色") +public class UserRole extends IdEntity { + + @ApiModelProperty(value = "用户唯一id") + private String userId; + + @ApiModelProperty(value = "角色唯一id") + private String roleId; + + public UserRole(String userId, String roleId) { + this.userId = userId; + this.roleId = roleId; + } + + public UserRole() { + + } +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/dto/AdminUserDTO.java b/framework/src/main/java/cn/lili/modules/permission/entity/dto/AdminUserDTO.java new file mode 100644 index 00000000..7986dc9b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/dto/AdminUserDTO.java @@ -0,0 +1,48 @@ +package cn.lili.modules.permission.entity.dto; + +import cn.lili.base.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.validation.constraints.NotEmpty; + +/** + * 管理员入库dto + * + * @author Chopper + * @date 2020/11/16 19:55 + */ +@Data +@ApiModel(value = "管理员入库dto") +public class AdminUserDTO extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "用户名") + @NotEmpty(message = "用户名不能为空") + @Column(unique = true, nullable = false) + private String username; + + @NotEmpty(message = "密码不能为空") + @ApiModelProperty(value = "密码") + private String password; + + @ApiModelProperty(value = "昵称") + private String nickName; + + @ApiModelProperty(value = "手机") + private String mobile; + + @ApiModelProperty(value = "邮件") + private String email; + + @ApiModelProperty(value = "描述/详情/备注") + private String description; + + @ApiModelProperty(value = "所属部门id") + private String departmentId; + + private boolean isSuper; +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/dto/MenuSearchParams.java b/framework/src/main/java/cn/lili/modules/permission/entity/dto/MenuSearchParams.java new file mode 100644 index 00000000..a822758a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/dto/MenuSearchParams.java @@ -0,0 +1,33 @@ +package cn.lili.modules.permission.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 权限搜索参数 + * + * @author Chopper + * @date 2020-11-19 16:46 + */ +@Data +public class MenuSearchParams { + + @ApiModelProperty(value = "菜单/权限名称") + private String name; + + @ApiModelProperty(value = "层级") + private Integer level; + + @ApiModelProperty(value = "菜单标题") + private String title; + + @ApiModelProperty(value = "赋权API地址,正则表达式") + private String path; + + @ApiModelProperty(value = "前端路由") + private String frontRoute; + + @ApiModelProperty(value = "图标") + private String icon; + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/vo/AdminUserVO.java b/framework/src/main/java/cn/lili/modules/permission/entity/vo/AdminUserVO.java new file mode 100644 index 00000000..72750041 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/vo/AdminUserVO.java @@ -0,0 +1,37 @@ +package cn.lili.modules.permission.entity.vo; + +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.permission.entity.dos.AdminUser; +import cn.lili.modules.permission.entity.dos.Menu; +import cn.lili.modules.permission.entity.dos.Role; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 管理员VO + * + * @author Chopper + * @date 2020-11-22 09:17 + */ +@Data +public class AdminUserVO extends AdminUser { + + private static final long serialVersionUID = -2378384199695839312L; + + @ApiModelProperty(value = "所属部门名称") + private String departmentTitle; + + @ApiModelProperty(value = "用户拥有角色") + private List roles; + + @ApiModelProperty(value = "用户拥有的权限") + private List menus; + + + public AdminUserVO(AdminUser user) { + BeanUtil.copyProperties(user, this); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/vo/DepartmentVO.java b/framework/src/main/java/cn/lili/modules/permission/entity/vo/DepartmentVO.java new file mode 100644 index 00000000..b5c291c9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/vo/DepartmentVO.java @@ -0,0 +1,27 @@ +package cn.lili.modules.permission.entity.vo; + +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.permission.entity.dos.Department; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 部门VO + * + * @author Chopper + * @date 2020-11-23 18:48 + */ +@Data +public class DepartmentVO extends Department { + + private List children = new ArrayList<>(); + + public DepartmentVO() { + } + + public DepartmentVO(Department department) { + BeanUtil.copyProperties(department, this); + } +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/vo/MenuVO.java b/framework/src/main/java/cn/lili/modules/permission/entity/vo/MenuVO.java new file mode 100644 index 00000000..3aba6f18 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/vo/MenuVO.java @@ -0,0 +1,45 @@ +package cn.lili.modules.permission.entity.vo; + +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.permission.entity.dos.Menu; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * 菜单VO 展示模型 + * + * @author Chopper + * @date 2020/11/20 15:38 + */ + +@Data +public class MenuVO extends Menu { + + @ApiModelProperty(value = "子菜单") + private List children = new ArrayList<>(); + + public MenuVO() { + + } + + public MenuVO(Menu menu) { + BeanUtil.copyProperties(menu, this); + } + + public List getChildren() { + if (children != null) { + children.sort(new Comparator() { + @Override + public int compare(MenuVO o1, MenuVO o2) { + return o1.getSortOrder().compareTo(o2.getSortOrder()); + } + }); + return children; + } + return null; + } +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/vo/RoleVO.java b/framework/src/main/java/cn/lili/modules/permission/entity/vo/RoleVO.java new file mode 100644 index 00000000..a9eb9ded --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/vo/RoleVO.java @@ -0,0 +1,23 @@ +package cn.lili.modules.permission.entity.vo; + +import cn.lili.modules.permission.entity.dos.Role; +import cn.lili.modules.permission.entity.dos.RoleMenu; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * RoleVO + * + * @author Chopper + * @date 2020-11-22 17:42 + */ +@Data +public class RoleVO extends Role { + + private static final long serialVersionUID = 8625345346785692513L; + + @ApiModelProperty(value = "拥有权限") + private List roleMenus; +} diff --git a/framework/src/main/java/cn/lili/modules/permission/entity/vo/UserMenuVO.java b/framework/src/main/java/cn/lili/modules/permission/entity/vo/UserMenuVO.java new file mode 100644 index 00000000..671495a0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/entity/vo/UserMenuVO.java @@ -0,0 +1,22 @@ +package cn.lili.modules.permission.entity.vo; + +import cn.lili.modules.permission.entity.dos.Menu; +import lombok.Data; + +/** + * RoleMenuVO + * + * @author Chopper + * @date 2020-11-24 11:45 + */ +@Data +public class UserMenuVO extends Menu { + + private static final long serialVersionUID = -7478870595109016162L; + + /** + * 是否是超级管理员 + */ + private Boolean isSupper; + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/mapper/AdminUserMapper.java b/framework/src/main/java/cn/lili/modules/permission/mapper/AdminUserMapper.java new file mode 100644 index 00000000..75ff89ae --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/mapper/AdminUserMapper.java @@ -0,0 +1,38 @@ +package cn.lili.modules.permission.mapper; + +import cn.lili.modules.permission.entity.dos.AdminUser; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +import java.util.List; + +/** + * 用户数据处理层 + * + * @author Chopper + * @date 2020-11-22 09:17 + */ +public interface AdminUserMapper extends BaseMapper { + + /** + * 通过用户名获取用户 + * @param username + * @return + */ + AdminUser findByUsername(String username); + + + /** + * 通过部门id获取 + * @param departmentId + * @return + */ + List findByDepartmentId(String departmentId); + + /** + * 通过用户名模糊搜索 + * @param username + * @param status + * @return + */ + List findByUsernameLikeAndStatus(String username, Integer status); +} diff --git a/framework/src/main/java/cn/lili/modules/permission/mapper/DepartmentMapper.java b/framework/src/main/java/cn/lili/modules/permission/mapper/DepartmentMapper.java new file mode 100644 index 00000000..e6e7c6d3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/mapper/DepartmentMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.permission.mapper; + +import cn.lili.modules.permission.entity.dos.Department; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 部门数据处理层 + * + * @author Chopper + * @date 2020-11-22 09:17 + */ +public interface DepartmentMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/mapper/DepartmentRoleMapper.java b/framework/src/main/java/cn/lili/modules/permission/mapper/DepartmentRoleMapper.java new file mode 100644 index 00000000..ca1220fb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/mapper/DepartmentRoleMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.permission.mapper; + +import cn.lili.modules.permission.entity.dos.DepartmentRole; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 部门角色数据处理层 + * + * @author Chopper + * @date 2020-11-22 09:17 + */ +public interface DepartmentRoleMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/mapper/MenuMapper.java b/framework/src/main/java/cn/lili/modules/permission/mapper/MenuMapper.java new file mode 100644 index 00000000..047b203b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/mapper/MenuMapper.java @@ -0,0 +1,30 @@ +package cn.lili.modules.permission.mapper; + +import cn.lili.modules.permission.entity.dos.Menu; +import cn.lili.modules.permission.entity.vo.UserMenuVO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 菜单数据处理层 + * + * @author Chopper + * @date 2020-11-22 09:17 + */ +public interface MenuMapper extends BaseMapper { + + @Select("SELECT menu.* FROM li_menu AS menu WHERE menu.id IN (" + + "SELECT rm.menu_id FROM li_role_menu AS rm WHERE rm.role_id IN (" + + "SELECT ur.role_id FROM li_user_role AS ur WHERE ur.user_id=#{userId}) OR rm.role_id IN (" + + "SELECT dr.role_id FROM li_department_role AS dr WHERE dr.id=(" + + "SELECT department_id FROM li_admin_user AS au WHERE au.id = #{userId})))") + List findByUserId(String userId); + + @Select("SELECT rm.is_super,m.*FROM li_menu AS m INNER JOIN li_role_menu AS rm ON rm.menu_id=m.id WHERE rm.role_id IN (" + + "SELECT ur.role_id FROM li_user_role AS ur WHERE ur.user_id=#{userId}) OR rm.role_id IN (" + + "SELECT dr.role_id FROM li_department_role AS dr INNER JOIN li_admin_user AS au ON au.department_id=dr.department_id " + + "WHERE au.id=#{userId}) GROUP BY m.id,rm.is_super ORDER BY rm.is_super desc") + List getUserRoleMenu(String userId); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/mapper/RoleDepartmentMapper.java b/framework/src/main/java/cn/lili/modules/permission/mapper/RoleDepartmentMapper.java new file mode 100644 index 00000000..12c82357 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/mapper/RoleDepartmentMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.permission.mapper; + +import cn.lili.modules.permission.entity.dos.DepartmentRole; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 角色部门数据处理层 + * + * @author Chopper + * @date 2020-11-22 09:17 + */ +public interface RoleDepartmentMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/mapper/RoleMapper.java b/framework/src/main/java/cn/lili/modules/permission/mapper/RoleMapper.java new file mode 100644 index 00000000..6fc2af6b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/mapper/RoleMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.permission.mapper; + +import cn.lili.modules.permission.entity.dos.Role; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 角色数据处理层 + * + * @author Chopper + * @date 2020-11-22 09:17 + */ +public interface RoleMapper extends BaseMapper { + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/mapper/RoleMenuMapper.java b/framework/src/main/java/cn/lili/modules/permission/mapper/RoleMenuMapper.java new file mode 100644 index 00000000..3e64bec2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/mapper/RoleMenuMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.permission.mapper; + +import cn.lili.modules.permission.entity.dos.RoleMenu; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 角色菜单数据处理层 + * + * @author Chopper + * @date 2020-11-22 09:17 + */ +public interface RoleMenuMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/mapper/RolePermissionMapper.java b/framework/src/main/java/cn/lili/modules/permission/mapper/RolePermissionMapper.java new file mode 100644 index 00000000..978d2681 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/mapper/RolePermissionMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.permission.mapper; + +import cn.lili.modules.permission.entity.dos.RoleMenu; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 角色权限数据处理层 + * + * @author Chopper + * @date 2020-11-22 09:17 + */ +public interface RolePermissionMapper extends BaseMapper { +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/mapper/UserRoleMapper.java b/framework/src/main/java/cn/lili/modules/permission/mapper/UserRoleMapper.java new file mode 100644 index 00000000..57fe3be8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/mapper/UserRoleMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.permission.mapper; + +import cn.lili.modules.permission.entity.dos.UserRole; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 角色权限数据处理层 + * @author Chopper + * @date 2020-11-22 09:17 + */ +public interface UserRoleMapper extends BaseMapper { + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/service/AdminUserService.java b/framework/src/main/java/cn/lili/modules/permission/service/AdminUserService.java new file mode 100644 index 00000000..7eb150f7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/service/AdminUserService.java @@ -0,0 +1,101 @@ +package cn.lili.modules.permission.service; + + +import cn.lili.common.token.Token; +import cn.lili.modules.permission.entity.dos.AdminUser; +import cn.lili.modules.permission.entity.dto.AdminUserDTO; +import cn.lili.modules.permission.entity.vo.AdminUserVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.cache.annotation.CacheConfig; + +import java.util.List; + +/** + * 用户业务层 + * + * @author Chopper + * @date 2020/11/17 3:42 下午 + */ +@CacheConfig(cacheNames = "{adminuser}") +public interface AdminUserService extends IService { + + + /** + * 获取管理员分页 + * + * @param initPage + * @param initWrapper + * @return + */ + IPage adminUserPage(Page initPage, QueryWrapper initWrapper); + + /** + * 通过用户名获取用户 + * + * @param username + * @return + */ + AdminUser findByUsername(String username); + + + /** + * 更新管理员 + * + * @param adminUser + * @param roles + * @return + */ + boolean updateAdminUser(AdminUser adminUser, List roles); + + + /** + * 修改管理员密码 + * + * @param password + * @param newPassword + */ + void editPassword(String password, String newPassword); + + /** + * 重置密码 + * + * @param ids id集合 + */ + void resetPassword(List ids); + + /** + * 新增管理员 + * + * @param adminUser + * @param roles + */ + void saveAdminUser(AdminUserDTO adminUser, List roles); + + /** + * 彻底删除 + * + * @param ids + */ + void deleteCompletely(List ids); + + + /** + * 登录 + * + * @param username + * @param password + */ + Token login(String username, String password); + + /** + * 刷新token + * + * @param refreshToken + * @return + */ + Object refreshToken(String refreshToken); + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/service/DepartmentRoleService.java b/framework/src/main/java/cn/lili/modules/permission/service/DepartmentRoleService.java new file mode 100644 index 00000000..ee9e1b03 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/service/DepartmentRoleService.java @@ -0,0 +1,39 @@ +package cn.lili.modules.permission.service; + +import cn.lili.modules.permission.entity.dos.DepartmentRole; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 部门角色业务层 + * + * @author Chopper + * @date 2020/11/22 12:08 + */ +public interface DepartmentRoleService extends IService { + + /** + * 根据部门获取角色集合 + * + * @param departmentId + * @return + */ + List listByDepartmentId(String departmentId); + + + /** + * 更新部门角色关联 + * + * @param departmentId + * @param departmentRoles + */ + void updateByDepartmentId(String departmentId, List departmentRoles); + + /** + * 根据部门id删除部门与角色关联 + * + * @param ids id集合 + */ + void deleteByDepartment(List ids); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/service/DepartmentService.java b/framework/src/main/java/cn/lili/modules/permission/service/DepartmentService.java new file mode 100644 index 00000000..b13c7126 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/service/DepartmentService.java @@ -0,0 +1,32 @@ +package cn.lili.modules.permission.service; + +import cn.lili.modules.permission.entity.dos.Department; +import cn.lili.modules.permission.entity.vo.DepartmentVO; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 部门业务层 + * + * @author Chopper + * @date 2020/11/17 3:43 下午 + */ +public interface DepartmentService extends IService { + + /** + * 获取部门树 + * + * @param initWrapper + * @return + */ + List tree(QueryWrapper initWrapper); + + /** + * 删除部门 + * + * @param ids + */ + void deleteByIds(List ids); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/service/MenuService.java b/framework/src/main/java/cn/lili/modules/permission/service/MenuService.java new file mode 100644 index 00000000..da5280f1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/service/MenuService.java @@ -0,0 +1,66 @@ +package cn.lili.modules.permission.service; + +import cn.lili.modules.permission.entity.dto.MenuSearchParams; +import cn.lili.modules.permission.entity.dos.Menu; +import cn.lili.modules.permission.entity.vo.MenuVO; +import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.cache.annotation.CacheConfig; + +import java.util.List; + +/** + * 权限业务层 + * + * @author Chopper + * @date 2020/11/17 3:45 下午 + */ +@CacheConfig(cacheNames = "{menu}") +public interface MenuService extends IService { + + /** + * 通过用户的菜单权限 + * + * @return + */ + List findUserTree(); + + /** + * 通过用户id获取 + * + * @param userId + * @return + */ + List findUserList(String userId); + + + /** + * 根据角色id获取菜单集合 + * + * @param roleIds + * @return + */ + List findByRoleIds(String roleIds); + + /** + * 树形结构 + * + * @return + */ + List tree(); + + /** + * 查询列表 + * + * @param menuSearchParams + * @return + */ + List searchList(MenuSearchParams menuSearchParams); + + /** + * 批量删除 + * + * @param ids + */ + void deleteIds(List ids); + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/service/RoleMenuService.java b/framework/src/main/java/cn/lili/modules/permission/service/RoleMenuService.java new file mode 100644 index 00000000..681124b7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/service/RoleMenuService.java @@ -0,0 +1,57 @@ +package cn.lili.modules.permission.service; + +import cn.lili.modules.permission.entity.dos.RoleMenu; +import cn.lili.modules.permission.entity.vo.UserMenuVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 角色菜单接口 + * + * @author Chopper + * @date 2020/11/22 11:43 + */ +public interface RoleMenuService extends IService { + + /** + * 通过角色获取菜单权限列表 + * + * @param roleId + * @return + */ + List findByRoleId(String roleId); + + + /** + * 根据角色集合获取拥有的菜单具体权限 + * + * @param userId + * @return + */ + List findAllMenu(String userId); + + + /** + * 更新某角色拥有到菜单 + * + * @param roleId + * @param roleMenus + */ + void updateRoleMenu(String roleId, List roleMenus); + + /** + * 根据角色id 删除数据 + * + * @param roleId + */ + void deleteRoleMenu(String roleId); + + /** + * 根据角色id 删除数据 + * + * @param roleId + */ + void deleteRoleMenu(List roleId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/service/RoleService.java b/framework/src/main/java/cn/lili/modules/permission/service/RoleService.java new file mode 100644 index 00000000..ba8f0e69 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/service/RoleService.java @@ -0,0 +1,31 @@ +package cn.lili.modules.permission.service; + + +import cn.lili.modules.permission.entity.dos.Role; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 角色业务层 + * + * @author Chopper + * @date 2020/11/17 3:45 下午 + */ +public interface RoleService extends IService { + + /** + * 获取默认角色 + * + * @param defaultRole + * @return + */ + List findByDefaultRole(Boolean defaultRole); + + + /** + * 批量删除角色 + * @param roleIds + */ + void deleteRoles(List roleIds); +} diff --git a/framework/src/main/java/cn/lili/modules/permission/service/SystemLogService.java b/framework/src/main/java/cn/lili/modules/permission/service/SystemLogService.java new file mode 100644 index 00000000..e0dbd816 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/service/SystemLogService.java @@ -0,0 +1,46 @@ +package cn.lili.modules.permission.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.base.entity.systemlog.SystemLogVO; +import com.baomidou.mybatisplus.core.metadata.IPage; + +import java.util.List; + +/** + * 系统日志业务层 + * + * @author Chopper + * @date 2020/11/17 3:45 下午 + */ +public interface SystemLogService { + + /** + * 添加日志 + * + * @param systemLogVO + * @return + */ + void saveLog(SystemLogVO systemLogVO); + + /** + * 通过id删除日志 + * + * @param id + */ + void deleteLog(List id); + + /** + * 删除全部日志 + */ + void flushAll(); + + /** + * 分页搜索获取日志 + * + * @param key + * @param searchVo + * @return + */ + IPage queryLog(String storeId, String operatorName, String key, SearchVO searchVo, PageVO pageVO); +} diff --git a/framework/src/main/java/cn/lili/modules/permission/service/UserRoleService.java b/framework/src/main/java/cn/lili/modules/permission/service/UserRoleService.java new file mode 100644 index 00000000..1a6014f9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/service/UserRoleService.java @@ -0,0 +1,41 @@ +package cn.lili.modules.permission.service; + +import cn.lili.modules.permission.entity.dos.UserRole; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 管理员业务层 + * + * @author Chopper + * @date 2020/11/17 3:46 下午 + */ +public interface UserRoleService extends IService { + + /** + * 根据用户查看拥有的角色 + * + * @param userId + * @return + */ + List listByUserId(String userId); + + /** + * 根据用户查看拥有的角色 + * + * @param userId + * @return + */ + List listId(String userId); + + /** + * 更新用户拥有的角色 + * + * @param userId + * @return + */ + void updateUserRole(String userId, List userRoles); + + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/AdminUserServiceImpl.java b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/AdminUserServiceImpl.java new file mode 100644 index 00000000..1be2912c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/AdminUserServiceImpl.java @@ -0,0 +1,260 @@ +package cn.lili.modules.permission.serviceimpl; + +import cn.lili.common.aop.syslog.annotation.SystemLogPoint; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.token.Token; +import cn.lili.common.token.base.generate.ManagerTokenGenerate; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.permission.entity.dos.AdminUser; +import cn.lili.modules.permission.entity.dos.Department; +import cn.lili.modules.permission.entity.dos.Role; +import cn.lili.modules.permission.entity.dos.UserRole; +import cn.lili.modules.permission.entity.dto.AdminUserDTO; +import cn.lili.modules.permission.entity.vo.AdminUserVO; +import cn.lili.modules.permission.mapper.AdminUserMapper; +import cn.lili.modules.permission.service.*; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 用户业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:46 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AdminUserServiceImpl extends ServiceImpl implements AdminUserService { + + private UserRoleService userRoleService; + + private RoleService roleService; + + private DepartmentService departmentService; + + private MenuService menuService; + + private ManagerTokenGenerate managerTokenGenerate; + + @Override + public IPage adminUserPage(Page initPage, QueryWrapper initWrapper) { + Page adminUserPage = page(initPage, initWrapper); + List roles = roleService.list(); + List departments = departmentService.list(); + + List result = new ArrayList<>(); + + adminUserPage.getRecords().forEach(adminUser -> { + AdminUserVO adminUserVO = new AdminUserVO(adminUser); + if (!StringUtils.isEmpty(adminUser.getDepartmentId())) { + try { + adminUserVO.setDepartmentTitle( + departments.stream().filter + (department -> department.getId().equals(adminUser.getDepartmentId())) + .collect(Collectors.toList()) + .get(0) + .getTitle() + ); + } catch (Exception e) { + log.error("填充部门信息异常", e); + } + } + if (!StringUtils.isEmpty(adminUser.getRoleIds())) { + try { + List memberRoles = Arrays.asList(adminUser.getRoleIds().split(",")); + adminUserVO.setRoles( + roles.stream().filter + (role -> memberRoles.contains(role.getId())) + .collect(Collectors.toList()) + ); + } catch (Exception e) { + log.error("填充部门信息异常", e); + } + } + result.add(adminUserVO); + }); + Page pageResult = new Page(adminUserPage.getCurrent(), adminUserPage.getSize(), adminUserPage.getTotal()); + pageResult.setRecords(result); + return pageResult; + + } + + @Autowired + private void setManagerTokenGenerate(ManagerTokenGenerate managerTokenGenerate) { + this.managerTokenGenerate = managerTokenGenerate; + } + + @Override + @SystemLogPoint(description = "管理员登录", customerLog = "'管理员登录请求:'+#username") + public Token login(String username, String password) { + AdminUser adminUser = this.findByUsername(username); + + if (adminUser == null || !adminUser.getStatus()) { + throw new ServiceException(ResultCode.USER_PASSWORD_ERROR); + } + if (!new BCryptPasswordEncoder().matches(password, adminUser.getPassword())) { + throw new ServiceException(ResultCode.USER_PASSWORD_ERROR); + } + try { + return managerTokenGenerate.createToken(username, false); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + + } + + + @Override + public Object refreshToken(String refreshToken) { + return managerTokenGenerate.refreshToken(refreshToken); + } + + + @Override + public AdminUser findByUsername(String username) { + + AdminUser user = getOne(new LambdaQueryWrapper().eq(AdminUser::getUsername, username)); + + if (user == null) { + return null; + } + AdminUserVO adminUserVO = new AdminUserVO(user); + // 关联部门 + if (user.getDepartmentId() != null) { + Department department = departmentService.getById(user.getDepartmentId()); + if (department != null) { + adminUserVO.setDepartmentTitle(department.getTitle()); + } + } + adminUserVO.setMenus(menuService.findUserList(user.getId())); + return user; + } + + + @Override + @SystemLogPoint(description = "修改管理员", customerLog = "'修改管理员:'+#adminUser.username") + public boolean updateAdminUser(AdminUser adminUser, List roles) { + if (roles.size() > 10) { + throw new ServiceException(ResultCode.PERMISSION_BEYOND_TEN); + } + if (roles != null && roles.size() > 0) { + adminUser.setRoleIds(StringUtils.join(",", roles)); + } + + this.updateById(adminUser); + updateRole(adminUser.getId(), roles); + return true; + } + + + @Override + public void editPassword(String password, String newPassword) { + AuthUser tokenUser = UserContext.getCurrentUser(); + AdminUser user = this.getById(tokenUser.getId()); + if (!new BCryptPasswordEncoder().matches(password, user.getPassword())) { + throw new ServiceException(ResultCode.USER_OLD_PASSWORD_ERROR); + } + String newEncryptPass = new BCryptPasswordEncoder().encode(newPassword); + user.setPassword(newEncryptPass); + this.updateById(user); + } + + @Override + public void resetPassword(List ids) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper(); + lambdaQueryWrapper.in(AdminUser::getId, ids); + List adminUsers = this.list(lambdaQueryWrapper); + String password = StringUtils.md5("123456"); + String newEncryptPass = new BCryptPasswordEncoder().encode(password); + if (null != adminUsers && adminUsers.size() > 0) { + adminUsers.forEach(item -> item.setPassword(newEncryptPass)); + this.updateBatchById(adminUsers); + } + } + + @Override + public void saveAdminUser(AdminUserDTO adminUser, List roles) { + AdminUser dbUser = new AdminUser(); + BeanUtil.copyProperties(adminUser, dbUser); + dbUser.setPassword(new BCryptPasswordEncoder().encode(dbUser.getPassword())); + if (roles.size() > 10) { + throw new ServiceException(ResultCode.PERMISSION_BEYOND_TEN); + } + if (roles != null && roles.size() > 0) { + dbUser.setRoleIds(StringUtils.join(",", roles)); + } + + + this.save(dbUser); + updateRole(adminUser.getId(), roles); + } + + + @Override + public void deleteCompletely(List ids) { + //彻底删除超级管理员 + this.removeByIds(ids); + + //删除管理员角色 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in("user_id", ids); + userRoleService.remove(queryWrapper); + + } + + /** + * 更新用户角色 + * + * @param userId 用户id + * @param roles 角色id集合 + */ + private void updateRole(String userId, List roles) { + if (!StringUtils.isNotEmpty(roles)) { + return; + } + List userRoles = new ArrayList<>(roles.size()); + roles.forEach(id -> userRoles.add(new UserRole(userId, id))); + userRoleService.updateUserRole(userId, userRoles); + } + + + @Autowired + public void setUserRoleService(UserRoleService userRoleService) { + this.userRoleService = userRoleService; + } + + @Autowired + public void setRoleService(RoleService roleService) { + this.roleService = roleService; + } + + @Autowired + public void setDepartmentService(DepartmentService departmentService) { + this.departmentService = departmentService; + } + + @Autowired + public void setMenuService(MenuService menuService) { + this.menuService = menuService; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/DepartmentRoleServiceImpl.java b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/DepartmentRoleServiceImpl.java new file mode 100644 index 00000000..2eb7397e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/DepartmentRoleServiceImpl.java @@ -0,0 +1,50 @@ +package cn.lili.modules.permission.serviceimpl; + +import cn.lili.modules.permission.entity.dos.DepartmentRole; +import cn.lili.modules.permission.mapper.DepartmentRoleMapper; +import cn.lili.modules.permission.service.DepartmentRoleService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 部门角色业务层实现 + * + * @author Chopper + * @date 2020/11/22 12:08 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DepartmentRoleServiceImpl extends ServiceImpl implements DepartmentRoleService { + + private final DepartmentRoleMapper departmentRoleMapper; + + @Override + public List listByDepartmentId(String departmentId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("department_id", departmentId); + return departmentRoleMapper.selectList(queryWrapper); + } + + @Override + public void updateByDepartmentId(String departmentId, List departmentRoles) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("department_id", departmentId); + departmentRoleMapper.delete(queryWrapper); + + this.saveBatch(departmentRoles); + } + + @Override + public void deleteByDepartment(List ids) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in("department_id", ids); + departmentRoleMapper.delete(queryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/DepartmentServiceImpl.java b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/DepartmentServiceImpl.java new file mode 100644 index 00000000..59f89c96 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/DepartmentServiceImpl.java @@ -0,0 +1,100 @@ +package cn.lili.modules.permission.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.permission.entity.dos.AdminUser; +import cn.lili.modules.permission.entity.dos.Department; +import cn.lili.modules.permission.entity.vo.DepartmentVO; +import cn.lili.modules.permission.mapper.DepartmentMapper; +import cn.lili.modules.permission.service.AdminUserService; +import cn.lili.modules.permission.service.DepartmentRoleService; +import cn.lili.modules.permission.service.DepartmentService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 部门业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:47 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DepartmentServiceImpl extends ServiceImpl implements DepartmentService { + + //管理员 + private AdminUserService adminUserService; + //部门角色 + private DepartmentRoleService departmentRoleService; + + @Autowired + public void setDepartmentRoleService(DepartmentRoleService departmentRoleService) { + this.departmentRoleService = departmentRoleService; + } + + @Autowired + public void setAdminUserService(AdminUserService adminUserService) { + this.adminUserService = adminUserService; + } + + @Override + public void deleteByIds(List ids) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in("department_id", ids); + if (adminUserService.count(queryWrapper) > 0) { + throw new ServiceException(ResultCode.PERMISSION_DEPARTMENT_DELETE_ERROR); + } + this.removeByIds(ids); + departmentRoleService.deleteByDepartment(ids); + } + + @Override + public List tree(QueryWrapper initWrapper) { + try { + List departments = this.list(initWrapper); + + List all = new ArrayList<>(); + departments.forEach(item -> all.add(new DepartmentVO(item))); + + List tree = new ArrayList<>(); + all.forEach(item -> { + if (item.getParentId().equals("0")) { + initChild(item, all); + tree.add(item); + } + }); + + return tree; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + /** + * 递归初始化子树 + * + * @param tree 树结构 + * @param departmentVOS 数据库对象集合 + */ + private void initChild(DepartmentVO tree, List departmentVOS) { + departmentVOS.stream() + .filter(item -> (item.getParentId().equals(tree.getId()))) + .forEach(child -> { + DepartmentVO childTree = new DepartmentVO(child); + initChild(childTree, departmentVOS); + tree.getChildren().add(childTree); + }); + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/MenuServiceImpl.java b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/MenuServiceImpl.java new file mode 100644 index 00000000..cdd430c0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/MenuServiceImpl.java @@ -0,0 +1,157 @@ +package cn.lili.modules.permission.serviceimpl; + +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.permission.entity.dto.MenuSearchParams; +import cn.lili.modules.permission.entity.dos.Menu; +import cn.lili.modules.permission.entity.dos.RoleMenu; +import cn.lili.modules.permission.entity.vo.MenuVO; +import cn.lili.modules.permission.mapper.MenuMapper; +import cn.lili.modules.permission.service.MenuService; +import cn.lili.modules.permission.service.RoleMenuService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * 权限业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:49 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MenuServiceImpl extends ServiceImpl implements MenuService { + //菜单 + private final MenuMapper menuMapper; + //菜单角色 + private RoleMenuService roleMenuService; + + @Autowired + public void setRoleMenuService(RoleMenuService roleMenuService) { + this.roleMenuService = roleMenuService; + } + + @Override + public void deleteIds(List ids) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in("menu_id", ids); + //如果已有角色绑定菜单,则不能直接删除 + if (roleMenuService.count(queryWrapper) > 0) { + throw new ServiceException(ResultCode.PERMISSION_MENU_ROLE_ERROR); + } + this.removeByIds(ids); + //删除关联关系 + roleMenuService.deleteRoleMenu(ids); + } + + + @Override + public List findUserTree() { + AuthUser authUser = UserContext.getCurrentUser(); + if (authUser.getIsSuper()) { + return this.tree(); + } + List userMenus = menuMapper.findByUserId(authUser.getId()); + return this.tree(userMenus); + } + + @Override + public List findUserList(String userId) { + return menuMapper.findByUserId(userId); + } + + @Override + public List findByRoleIds(String roleId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("role_id", roleId); + return this.list(queryWrapper); + } + + @Override + public List searchList(MenuSearchParams menuSearchParams) { + //title 需要特殊处理 + String title = null; + if (StringUtils.isNotEmpty(menuSearchParams.getTitle())) { + title = menuSearchParams.getTitle(); + menuSearchParams.setTitle(null); + } + QueryWrapper queryWrapper = PageUtil.initWrapper(menuSearchParams, new SearchVO()); + if (StringUtils.isNotEmpty(title)) { + queryWrapper.like("title", title); + } + queryWrapper.orderByDesc("sort_order"); + return menuMapper.selectList(queryWrapper); + } + + + @Override + public List tree() { + try { + List menus = this.list(); + return tree(menus); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 传入自定义菜单集合 + * + * @param menus + * @return + */ + private List tree(List menus) { + List tree = new ArrayList<>(); + menus.forEach(item -> { + if (item.getLevel() == 0) { + MenuVO treeItem = new MenuVO(item); + initChild(treeItem, menus); + tree.add(treeItem); + } + }); + //对一级菜单排序 + tree.sort(new Comparator() { + @Override + public int compare(MenuVO o1, MenuVO o2) { + return o1.getSortOrder().compareTo(o2.getSortOrder()); + } + }); + return tree; + } + + /** + * 递归初始化子树 + * + * @param tree 树结构 + * @param menus 数据库对象集合 + */ + private void initChild(MenuVO tree, List menus) { + if (menus == null) { + return; + } + menus.stream() + .filter(item -> (item.getParentId().equals(tree.getId()))) + .forEach(child -> { + MenuVO childTree = new MenuVO(child); + initChild(childTree, menus); + tree.getChildren().add(childTree); + }); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/RoleMenuServiceImpl.java b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/RoleMenuServiceImpl.java new file mode 100644 index 00000000..7cfb9e9f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/RoleMenuServiceImpl.java @@ -0,0 +1,72 @@ +package cn.lili.modules.permission.serviceimpl; + +import cn.lili.modules.permission.entity.dos.RoleMenu; +import cn.lili.modules.permission.entity.vo.UserMenuVO; +import cn.lili.modules.permission.mapper.MenuMapper; +import cn.lili.modules.permission.mapper.RoleMenuMapper; +import cn.lili.modules.permission.service.RoleMenuService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 角色菜单业务层实现 + * + * @author Chopper + * @date 2020/11/22 11:43 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RoleMenuServiceImpl extends ServiceImpl implements RoleMenuService { + + //菜单 + private final MenuMapper menuMapper; + //角色菜单 + private final RoleMenuMapper roleMenuMapper; + + @Override + public List findByRoleId(String roleId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("role_id", roleId); + return roleMenuMapper.selectList(queryWrapper); + } + + @Override + public List findAllMenu(String userId) { + return menuMapper.getUserRoleMenu(userId); + } + + + @Override + public void updateRoleMenu(String roleId, List roleMenus) { + try { + //删除角色已经绑定的菜单 + this.deleteRoleMenu(roleId); + //重新保存角色菜单关系 + this.saveBatch(roleMenus); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void deleteRoleMenu(String roleId) { + //删除 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("role_id", roleId); + roleMenuMapper.delete(queryWrapper); + } + @Override + public void deleteRoleMenu(List roleId) { + //删除 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in("role_id", roleId); + roleMenuMapper.delete(queryWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/RoleServiceImpl.java b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/RoleServiceImpl.java new file mode 100644 index 00000000..a7e2dd4e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/RoleServiceImpl.java @@ -0,0 +1,67 @@ +package cn.lili.modules.permission.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.modules.permission.entity.dos.Role; +import cn.lili.modules.permission.mapper.RoleMapper; +import cn.lili.modules.permission.service.DepartmentRoleService; +import cn.lili.modules.permission.service.RoleService; +import cn.lili.modules.permission.service.UserRoleService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 角色业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:50 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RoleServiceImpl extends ServiceImpl implements RoleService { + + //部门角色 + private DepartmentRoleService departmentRoleService; + //用户权限 + private UserRoleService userRoleService; + + @Autowired + public void setDepartmentRoleService(DepartmentRoleService departmentRoleService) { + this.departmentRoleService = departmentRoleService; + } + + @Autowired + public void setUserRoleService(UserRoleService userRoleService) { + this.userRoleService = userRoleService; + } + + @Override + public List findByDefaultRole(Boolean defaultRole) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("default_role", true); + return baseMapper.selectList(queryWrapper); + } + + @Override + public void deleteRoles(List roleIds) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in("role_id", roleIds); + if (departmentRoleService.count(queryWrapper) > 0) { + throw new ServiceException(ResultCode.PERMISSION_DEPARTMENT_ROLE_ERROR); + } + if (userRoleService.count(queryWrapper) > 0) { + throw new ServiceException(ResultCode.PERMISSION_USER_ROLE_ERROR); + } + //删除角色 + this.removeByIds(roleIds); + //删除角色与菜单关联 + userRoleService.remove(queryWrapper); + } +} diff --git a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/SystemLogServiceImpl.java b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/SystemLogServiceImpl.java new file mode 100644 index 00000000..1b8667b0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/SystemLogServiceImpl.java @@ -0,0 +1,100 @@ +package cn.lili.modules.permission.serviceimpl; + +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.base.entity.systemlog.SystemLogVO; +import cn.lili.modules.permission.service.SystemLogService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.regex.Pattern; + +/** + * 系统日志 + * + * @author Chopper + * @date 2020/11/17 3:45 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SystemLogServiceImpl implements SystemLogService { + + + private final MongoTemplate mongoTemplate; + + @Override + public void saveLog(SystemLogVO systemLogVO) { + mongoTemplate.save(systemLogVO); + } + + @Override + public void deleteLog(List id) { + mongoTemplate.remove(new Query().addCriteria(Criteria.where("id").is(id)), SystemLogVO.class); + } + + @Override + public void flushAll() { + mongoTemplate.dropCollection(SystemLogVO.class); + } + + @Override + public IPage queryLog(String storeId, String operatorName, String key, SearchVO searchVo, PageVO pageVO) { + Query query = new Query(); + + if (StringUtils.isNotEmpty(storeId)) { + query.addCriteria(Criteria.where("storeId").is(storeId)); + } + + if (StringUtils.isNotEmpty(operatorName)) { + query.addCriteria(Criteria.where("username").regex(Pattern.compile("^.*" + operatorName + ".*$", Pattern.CASE_INSENSITIVE))); + } + + if (StringUtils.isNotEmpty(key)) { + query.addCriteria(new Criteria().orOperator( + Criteria.where("requestUrl").regex(Pattern.compile("^.*" + key + ".*$", Pattern.CASE_INSENSITIVE)), + Criteria.where("requestParam").regex(Pattern.compile("^.*" + key + ".*$", Pattern.CASE_INSENSITIVE)), + Criteria.where("responseBody").regex(Pattern.compile("^.*" + key + ".*$", Pattern.CASE_INSENSITIVE)), + Criteria.where("name").regex(Pattern.compile("^.*" + key + ".*$", Pattern.CASE_INSENSITIVE)) + )); + } + //时间有效性判定 + if (searchVo.getConvertStartDate() != null && searchVo.getConvertEndDate() != null) { + System.out.println(DateUtil.toString(searchVo.getConvertStartDate(), DateUtil.STANDARD_FORMAT)); + System.out.println(DateUtil.toString(searchVo.getConvertEndDate(), DateUtil.STANDARD_FORMAT)); + + //大于方法 + Criteria gt = Criteria.where("createTime").gt(searchVo.getConvertStartDate()); + //小于方法 + Criteria lt = Criteria.where("createTime").lte(searchVo.getConvertEndDate()); + query.addCriteria(new Criteria().andOperator(gt, lt)); + + } + + IPage iPage = new Page<>(); + + iPage.setTotal(mongoTemplate.count(query, SystemLogVO.class)); + query.with(PageRequest.of(pageVO.getMongoPageNumber(), pageVO.getPageSize())); + + query.with(Sort.by(Sort.Direction.DESC, "createTime")); + + List systemLogVOS = mongoTemplate.find(query, SystemLogVO.class); + iPage.setCurrent(pageVO.getPageNumber()); + iPage.setSize(pageVO.getPageSize()); + iPage.setRecords(systemLogVOS); + return iPage; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/permission/serviceimpl/UserRoleServiceImpl.java b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/UserRoleServiceImpl.java new file mode 100644 index 00000000..9b819904 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/permission/serviceimpl/UserRoleServiceImpl.java @@ -0,0 +1,56 @@ +package cn.lili.modules.permission.serviceimpl; + +import cn.lili.modules.permission.entity.dos.UserRole; +import cn.lili.modules.permission.mapper.UserRoleMapper; +import cn.lili.modules.permission.service.UserRoleService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用户权限业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:52 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class UserRoleServiceImpl extends ServiceImpl implements UserRoleService { + + private final UserRoleMapper userRoleMapper; + + @Override + public List listByUserId(String userId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("user_id", userId); + return userRoleMapper.selectList(queryWrapper); + } + + @Override + public List listId(String userId) { + List userRoleList = this.listByUserId(userId); + List strings = new ArrayList<>(); + userRoleList.forEach(item -> strings.add(item.getRoleId())); + return strings; + } + + @Override + public void updateUserRole(String userId, List userRoles) { + + //删除 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("user_id", userId); + userRoleMapper.delete(queryWrapper); + + //保存 + this.saveBatch(userRoles); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dos/Coupon.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/Coupon.java new file mode 100644 index 00000000..ae410350 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/Coupon.java @@ -0,0 +1,84 @@ +package cn.lili.modules.promotion.entity.dos; + +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 优惠券活动实体类 + * + * @author Chopper + * @date 2020-03-19 10:44 上午 + */ +@Data +@Entity +@Table(name = "li_coupon") +@TableName("li_coupon") +@ApiModel(value = "优惠券活动实体类") +public class Coupon extends BasePromotion { + + + private static final long serialVersionUID = 8372820376262437018L; + + + @ApiModelProperty(value = "优惠券名称") + private String couponName; + + /** + * POINT("打折"), PRICE("减免现金"); + * + * @see cn.lili.modules.promotion.entity.enums.CouponTypeEnum + */ + @ApiModelProperty(value = "活动类型") + private String couponType; + + /** + * @see cn.lili.modules.promotion.entity.enums.CouponScopeTypeEnum + */ + @ApiModelProperty(value = "关联范围类型") + private String scopeType; + + @ApiModelProperty(value = "面额") + private Double price; + + @ApiModelProperty(value = "折扣") + private Double couponDiscount; + + @ApiModelProperty(value = "范围关联的id") + @Column(columnDefinition = "TEXT") + private String scopeId; + + /** + * @see cn.lili.modules.promotion.entity.enums.CouponGetEnum + */ + @ApiModelProperty(value = "优惠券类型,分为免费领取和活动赠送") + private String getType; + + @ApiModelProperty(value = "店铺承担比例,平台发布时可以提供一定返点") + private Double storeCommission; + + @ApiModelProperty(value = "活动描述") + private String description; + + @ApiModelProperty(value = "发行数量") + private Integer publishNum; + + @ApiModelProperty(value = "领取限制") + private Integer couponLimitNum; + + @ApiModelProperty(value = "已被使用的数量") + private Integer usedNum; + + @ApiModelProperty(value = "已被领取的数量") + private Integer receivedNum; + + @ApiModelProperty(value = "消费门槛") + private Double consumeThreshold; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dos/FullDiscount.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/FullDiscount.java new file mode 100644 index 00000000..d78c7a7c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/FullDiscount.java @@ -0,0 +1,75 @@ +package cn.lili.modules.promotion.entity.dos; + +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.DecimalMax; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 满优惠活动实体类 + * + * @author Chopper + * @date 2020-03-19 10:44 上午 + */ +@Data +@Entity +@Table(name = "li_full_discount") +@TableName("li_full_discount") +@ApiModel(value = "满优惠活动") +public class FullDiscount extends BasePromotion { + + private static final long serialVersionUID = 430433787214894166L; + + @NotNull(message = "请填写优惠门槛") + @DecimalMax(value = "99999999.00", message = "优惠券门槛金额超出限制") + @ApiModelProperty(value = "优惠门槛金额", required = true) + private Double fullMoney; + + @ApiModelProperty(value = "活动是否减现金") + private Boolean isFullMinus; + + @ApiModelProperty(value = "减现金") + private Double fullMinus; + + @ApiModelProperty(value = "是否打折") + private Boolean isFullRate; + + @ApiModelProperty(value = "打折") + private Double fullRate; + + @ApiModelProperty(value = "是否赠送积分") + private Boolean isPoint; + + @ApiModelProperty(value = "赠送多少积分") + private Integer point; + + @ApiModelProperty(value = "是否包邮") + private Boolean isFreeFreight; + + @ApiModelProperty(value = "是否有赠品") + private Boolean isGift; + + @ApiModelProperty(value = "赠品id") + private String giftId; + + @ApiModelProperty(value = "是否赠优惠券") + private Boolean isCoupon; + + @ApiModelProperty(value = "优惠券id") + private String couponId; + + @NotEmpty(message = "请填写活动标题") + @ApiModelProperty(value = "活动标题", required = true) + private String title; + + @ApiModelProperty(value = "活动说明") + private String description; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dos/MemberCoupon.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/MemberCoupon.java new file mode 100644 index 00000000..62028958 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/MemberCoupon.java @@ -0,0 +1,127 @@ +package cn.lili.modules.promotion.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.promotion.entity.enums.MemberCouponStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; + +/** + * 会员优惠券实体类 + * + * @author Chopper + * @date 2020-03-19 10:44 上午 + */ +@Data +@Entity +@Table(name = "li_member_coupon") +@TableName("li_member_coupon") +@ApiModel(value = "会员优惠券") +public class MemberCoupon extends BaseEntity { + + private static final long serialVersionUID = -7290310311125273760L; + + @ApiModelProperty(value = "从哪个模版领取的优惠券") + private String couponId; + + @ApiModelProperty(value = "商家id,如果是平台发送,这个值为 platform") + private String storeId; + + @ApiModelProperty(value = "商家名称,如果是平台,这个值为 platform") + private String storeName; + + @ApiModelProperty(value = "面额") + private Double price; + + @ApiModelProperty(value = "折扣") + private Double discount; + + @ApiModelProperty(value = "消费门槛") + private Double consumeThreshold; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "会员id") + private String memberId; + + /** + * @see cn.lili.modules.promotion.entity.enums.CouponScopeTypeEnum + */ + @ApiModelProperty(value = "关联范围类型") + private String scopeType; + + /** + * POINT("打折"), PRICE("减免现金"); + * + * @see cn.lili.modules.promotion.entity.enums.CouponTypeEnum + */ + @ApiModelProperty(value = "活动类型") + private String couponType; + + + @ApiModelProperty(value = "范围关联的id") + @Column(columnDefinition = "TEXT") + private String scopeId; + + @ApiModelProperty(value = "使用起始时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @ApiModelProperty(value = "使用截止时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + /** + * @see cn.lili.modules.promotion.entity.enums.CouponGetEnum + */ + @ApiModelProperty(value = "优惠券类型,分为免费领取和活动赠送") + private String getType; + + @ApiModelProperty(value = "是否是平台优惠券") + private Boolean isPlatform; + + @ApiModelProperty(value = "店铺承担比例") + private Double storeCommission; + + @ApiModelProperty(value = "核销时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date consumptionTime; + + /** + * @see MemberCouponStatusEnum + */ + @ApiModelProperty(value = "会员优惠券状态") + private String memberCouponStatus; + + public MemberCoupon() { + } + + public MemberCoupon(Coupon coupon) { + setCouponId(coupon.getId()); + setStoreId(coupon.getStoreId()); + setStoreName(coupon.getStoreName()); + setPrice(coupon.getPrice()); + setDiscount(coupon.getCouponDiscount()); + setConsumeThreshold(coupon.getConsumeThreshold()); + setScopeType(coupon.getScopeType()); + setScopeId(coupon.getScopeId()); + setCouponType(coupon.getCouponType()); + setStartTime(coupon.getStartTime()); + setEndTime(coupon.getEndTime()); + setGetType(coupon.getGetType()); + setStoreCommission(coupon.getStoreCommission()); + } + + public boolean canUse() { + return this.getMemberCouponStatus().equals(MemberCouponStatusEnum.NEW.name()); + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dos/Pintuan.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/Pintuan.java new file mode 100644 index 00000000..03661bf5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/Pintuan.java @@ -0,0 +1,45 @@ +package cn.lili.modules.promotion.entity.dos; + +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Min; + +/** + * 拼团活动实体类 + * + * @author Chopper + * @date 2020-03-19 10:44 上午 + */ +@Data +@Entity +@Table(name = "li_pintuan") +@TableName("li_pintuan") +@ApiModel(value = "拼团") +public class Pintuan extends BasePromotion { + + + private static final long serialVersionUID = -8465716592648602604L; + + + @Min(message = "成团人数必须为数字", value = 0) + @ApiModelProperty(value = "成团人数") + private Integer requiredNum; + + @Min(message = "限购数量必须为数字", value = 0) + @ApiModelProperty(value = "限购数量") + private Integer limitNum; + + @ApiModelProperty(value = "虚拟成团", required = true) + private Boolean fictitious; + + @ApiModelProperty(value = "拼团规则") + private String pintuanRule; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dos/PointsGoods.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/PointsGoods.java new file mode 100644 index 00000000..4fec53f1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/PointsGoods.java @@ -0,0 +1,49 @@ +package cn.lili.modules.promotion.entity.dos; + +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 积分商品实体类 + * + * @author paulG + * @date 2020-03-19 10:44 上午 + **/ +@Data +@Entity +@Table(name = "li_points_goods") +@TableName("li_points_goods") +@ApiModel(value = "积分商品") +@AllArgsConstructor +@NoArgsConstructor +public class PointsGoods extends BasePromotion { + + private static final long serialVersionUID = 1313207311581661571L; + + @ApiModelProperty(value = "商品编号") + private String skuId; + + @ApiModelProperty(value = "结算价格") + private Double settlementPrice; + + @ApiModelProperty(value = "积分商品分类编号") + private String pointsGoodsCategoryId; + + @ApiModelProperty(value = "分类名称") + private String pointsGoodsCategoryName; + + @ApiModelProperty(value = "活动库存数量") + private Long activeStock; + + @ApiModelProperty(value = "兑换积分") + private Long points; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dos/PointsGoodsCategory.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/PointsGoodsCategory.java new file mode 100644 index 00000000..d3693cb5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/PointsGoodsCategory.java @@ -0,0 +1,47 @@ +package cn.lili.modules.promotion.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; +import java.math.BigDecimal; + +/** + * 积分商品分类 + * + * @author paulG + * @date 2020-03-19 10:44 上午 + **/ +@Data +@Entity +@Table(name = "li_points_goods_category") +@TableName("li_points_goods_category") +@ApiModel(value = "积分商品分类") +@AllArgsConstructor +@NoArgsConstructor +public class PointsGoodsCategory extends BaseEntity { + + private static final long serialVersionUID = 4689246801280318515L; + + @NotEmpty(message = "分类名称不能为空") + @ApiModelProperty(value = "分类名称") + private String name; + + @ApiModelProperty(value = "父id, 根节点为0") + private String parentId; + + @ApiModelProperty(value = "层级, 从0开始") + private Integer level; + + @ApiModelProperty(value = "排序值") + private BigDecimal sortOrder; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dos/PromotionGoods.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/PromotionGoods.java new file mode 100644 index 00000000..efb517d9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/PromotionGoods.java @@ -0,0 +1,99 @@ +package cn.lili.modules.promotion.entity.dos; + +import cn.hutool.core.bean.BeanUtil; +import cn.lili.base.BaseEntity; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; + +/** + * 促销活动商品实体类 + * + * @author Chopper + * @date 2020-03-19 10:44 上午 + */ +@Data +@Entity +@Table(name = "li_promotion_goods") +@TableName("li_promotion_goods") +@ApiModel(value = "促销商品") +@NoArgsConstructor +public class PromotionGoods extends BaseEntity { + + private static final long serialVersionUID = 4150737500248136108L; + + @ApiModelProperty(value = "商家ID") + private String storeId; + + @ApiModelProperty(value = "商家名称") + private String storeName; + + @ApiModelProperty(value = "货品id") + private String skuId; + + @ApiModelProperty(value = "货品名称") + private String goodsName; + + @ApiModelProperty(value = "缩略图") + private String thumbnail; + + @ApiModelProperty(value = "活动开始时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @ApiModelProperty(value = "活动结束时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + @ApiModelProperty(value = "活动id") + private String promotionId; + + /** + * @see cn.lili.modules.promotion.entity.enums.PromotionTypeEnum + */ + @ApiModelProperty(value = "促销工具类型") + private String promotionType; + + @ApiModelProperty(value = "活动标题") + private String title; + + @ApiModelProperty(value = "卖出的商品数量") + private Integer num; + + @ApiModelProperty(value = "促销价格") + private Double price; + + @ApiModelProperty(value = "限购数量") + private Integer limitNum; + + @ApiModelProperty(value = "促销库存") + private Integer quantity; + + /** + * @see PromotionStatusEnum + */ + @ApiModelProperty(value = "状态") + private String promotionStatus; + + @ApiModelProperty(value = "分类path") + private String categoryPath; + + public PromotionGoods(GoodsSku sku) { + if (sku != null) { + String oldId = this.getId(); + BeanUtil.copyProperties(sku, this); + this.setSkuId(sku.getId()); + this.setId(oldId); + } + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dos/Seckill.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/Seckill.java new file mode 100644 index 00000000..55b154eb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/Seckill.java @@ -0,0 +1,47 @@ +package cn.lili.modules.promotion.entity.dos; + +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** + * 限时抢购实体类 + * + * @author Chopper + * @date 2020-03-19 10:44 上午 + */ +@Data +@Entity +@Table(name = "li_seckill") +@TableName("li_seckill") +@ApiModel(value = "限时抢购活动") +public class Seckill extends BasePromotion { + + private static final long serialVersionUID = -9116425737163730836L; + + @NotNull(message = "请填写报名截止时间") + @ApiModelProperty(value = "报名截至时间", required = true) + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date applyEndTime; + + @ApiModelProperty(value = "申请规则") + private String seckillRule; + + @ApiModelProperty(value = "开启几点场 例如:6,8,12") + @NotNull(message = "活动时间段不能为空") + private String hours; + + /** + * 已参与此活动的商家id集合 + */ + @ApiModelProperty(value = "商家id集合以逗号分隔") + private String storeIds; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dos/SeckillApply.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/SeckillApply.java new file mode 100644 index 00000000..e21b3505 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dos/SeckillApply.java @@ -0,0 +1,80 @@ +package cn.lili.modules.promotion.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 限时抢购申请实体类 + * + * @author Chopper + * @date 2020-03-19 10:44 上午 + */ +@Data +@Entity +@Table(name = "li_seckill_apply") +@TableName("li_seckill_apply") +@ApiModel(value = "限时抢购申请") +public class SeckillApply extends BaseEntity { + + private static final long serialVersionUID = 5440164641970820989L; + + @ApiModelProperty(value = "活动id", required = true) + @NotNull(message = "活动id参数不能为空") + @Min(value = 0, message = "活动id参数异常") + private String seckillId; + + @ApiModelProperty(value = "时刻") + @NotNull(message = "时刻参数不能为空") + private Integer timeLine; + + @ApiModelProperty(value = "skuID") + @NotNull(message = "skuId参数不能为空") + @Min(value = 0, message = "skuID参数异常") + private String skuId; + + @ApiModelProperty(value = "商品名称") + @NotEmpty(message = "商品名称参数不能为空") + private String goodsName; + + @ApiModelProperty(value = "商家id") + private String storeId; + + @ApiModelProperty(value = "商家名称") + private String storeName; + + @ApiModelProperty(value = "价格") + @NotNull(message = "价格参数不能为空") + @Min(value = 0, message = "价格参数不能小于0") + private Double price; + + @ApiModelProperty(value = "促销数量") + @NotNull(message = "促销数量参数不能为空") + @Min(value = 0, message = "促销数量数不能小于0") + private Integer quantity; + + /** + * @see cn.lili.modules.promotion.entity.enums.PromotionApplyStatusEnum + */ + @ApiModelProperty(value = "APPLY(\"申请\"), PASS(\"通过\"), REFUSE(\"拒绝\")") + private String promotionApplyStatus; + + @ApiModelProperty(value = "驳回原因") + private String failReason; + + @ApiModelProperty(value = "已售数量") + private Integer salesNum; + + @ApiModelProperty(value = "商品原始价格") + private Double originalPrice; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dto/BasePromotion.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/BasePromotion.java new file mode 100644 index 00000000..42351d0e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/BasePromotion.java @@ -0,0 +1,53 @@ +package cn.lili.modules.promotion.entity.dto; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.MappedSuperclass; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** + * 促销活动基础类 + * + * @author Chopper + * @date 2020-03-19 10:44 上午 + */ +@Data +@MappedSuperclass +public class BasePromotion extends BaseEntity { + + private static final long serialVersionUID = 7814832369110695758L; + + @ApiModelProperty(value = "商家名称,如果是平台,这个值为 platform") + private String storeName; + + @ApiModelProperty(value = "商家id,如果是平台,这个值为 platform") + private String storeId; + + @NotEmpty(message = "活动名称不能为空") + @ApiModelProperty(value = "活动名称", required = true) + private String promotionName; + + @Min(message = "活动开始时间不能为空", value = 0) + @ApiModelProperty(value = "活动开始时间", required = true) + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @Min(message = "活动结束时间不能为空", value = 0) + @ApiModelProperty(value = "活动结束时间", required = true) + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + /** + * @see PromotionStatusEnum + */ + @ApiModelProperty(value = "活动状态") + @NotNull(message = "活动状态不能为空") + private String promotionStatus; +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dto/GoodsSkuPromotionPriceDTO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/GoodsSkuPromotionPriceDTO.java new file mode 100644 index 00000000..a299ec8c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/GoodsSkuPromotionPriceDTO.java @@ -0,0 +1,108 @@ +package cn.lili.modules.promotion.entity.dto; + +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 促销活动商品价格数据传输对象 + * + * @author paulG + * @date 2020/8/20 + **/ +@Data +@NoArgsConstructor +public class GoodsSkuPromotionPriceDTO implements Serializable { + + private static final long serialVersionUID = 3510264801983456306L; + + @ApiModelProperty(value = "商品SkuId") + private String skuId; + + @ApiModelProperty(value = "店铺Id") + private String storeId; + + @ApiModelProperty(value = "分类path") + private String categoryPath; + + @ApiModelProperty(value = "店铺分类id") + private String storeCategoryPath; + + @ApiModelProperty(value = "数量") + private Integer number; + + @ApiModelProperty(value = "重量") + private Double weight; + + @ApiModelProperty(value = "总重量") + private Double totalWeight; + + @ApiModelProperty(value = "单个商品原价") + private Double originalPrice; + + @ApiModelProperty(value = "商品原价总价 = 商品原价 * 数量") + private Double totalOriginalPrice; + + @ApiModelProperty(value = "单个商品积分购买数量") + private Double points; + + @ApiModelProperty(value = "商品购买总数量 = 单个商品积分购买数量 * 数量") + private Double totalPoints; + + @ApiModelProperty(value = "单个优惠的所占总优惠金额比例") + private Double discountPriceRate; + + @ApiModelProperty(value = "单个优惠的金额") + private Double discountPrice; + + @ApiModelProperty(value = "优惠的总金额 = 单个优惠的金额 * 数量") + private Double totalDiscountPrice; + + @ApiModelProperty(value = "单个商品最终成交金额") + private Double finalePrice; + + @ApiModelProperty(value = "商品最终成交的总金额 = 单个商品最终成交金额 * 数量") + private Double totalFinalePrice; + + @ApiModelProperty(value = "分配到每个商品的优惠券金额") + private Double couponPrice; + + @ApiModelProperty(value = "促销活动ID") + private String promotionId; + + /** + * @see cn.lili.modules.promotion.entity.enums.PromotionTypeEnum + */ + @ApiModelProperty(value = "促销活动类型") + private String promotionType; + + /** + * 店铺商品促销信息集合 + */ + @ApiModelProperty(value = "参与的促销活动") + private List joinPromotion; + + public GoodsSkuPromotionPriceDTO(GoodsSku sku, Integer buyNum) { + this.setOriginalPrice(sku.getPrice()); + this.setSkuId(sku.getId()); + this.setNumber(buyNum); + this.setWeight(sku.getWeight()); + this.setTotalWeight(sku.getWeight() != null ? CurrencyUtil.mul(sku.getWeight(), buyNum) : 0); + this.setCategoryPath(sku.getCategoryPath()); + this.setStoreCategoryPath(sku.getStoreCategoryPath()); + this.setStoreId(sku.getStoreId()); + this.setDiscountPrice(0d); + this.setOriginalPrice(sku.getPrice()); + this.setCouponPrice(0D); + this.setPoints(0d); + this.setTotalPoints(0d); + this.setFinalePrice(sku.getPrice()); + this.setJoinPromotion(new ArrayList<>()); + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dto/PromotionGoodsDTO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/PromotionGoodsDTO.java new file mode 100644 index 00000000..de5ce85b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/PromotionGoodsDTO.java @@ -0,0 +1,37 @@ +package cn.lili.modules.promotion.entity.dto; + +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + + +/** + * 促销商品数据传输对象 + * + * @author paulG + * @date 2020/10/9 + **/ +@Data +@NoArgsConstructor +public class PromotionGoodsDTO extends PromotionGoods { + + private static final long serialVersionUID = 9206970681612883421L; + + @ApiModelProperty(value = "原价") + private Double originPrice; + + @ApiModelProperty(value = "商品id") + private String goodsId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品图片") + private String goodsImage; + + public PromotionGoodsDTO(GoodsSku sku) { + super(sku); + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dto/PromotionPriceDTO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/PromotionPriceDTO.java new file mode 100644 index 00000000..afb64321 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/PromotionPriceDTO.java @@ -0,0 +1,59 @@ +package cn.lili.modules.promotion.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 促销价格数据传输对象 + * + * @author paulG + * @date 2020/11/17 + **/ +@Data +public class PromotionPriceDTO { + + /** + * 实际成交价格合计 + */ + @ApiModelProperty(value = "商品原价格合计") + private Double totalOriginPrice; + + /** + * 总需支付积分合计 + */ + @ApiModelProperty(value = "总需支付积分合计") + private Double totalPoints; + + /** + * 总优惠价格合计 + */ + @ApiModelProperty(value = "总优惠价格合计") + private Double totalDiscountPrice; + + /** + * 优惠券合计 + */ + @ApiModelProperty(value = "优惠券合计") + private Double totalCouponPrice; + + /** + * 最终结算金额 = totalOriginPrice - totalDiscountPrice - totalCouponPrice + */ + @ApiModelProperty(value = "最终结算金额") + private Double totalFinalePrice; + + /** + * 店铺促销计算集合 + */ + @ApiModelProperty(value = "店铺促销计算集合") + private List storePromotionPriceList; + + /** + * 参与的促销活动 + */ + @ApiModelProperty(value = "参与的促销活动") + private List joinPromotion; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dto/PromotionPriceParamDTO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/PromotionPriceParamDTO.java new file mode 100644 index 00000000..42019a79 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/PromotionPriceParamDTO.java @@ -0,0 +1,23 @@ +package cn.lili.modules.promotion.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 促销价格参数数据传输对象 + * + * @author paulG + * @date 2020/11/17 + **/ +@Data +public class PromotionPriceParamDTO { + + @ApiModelProperty(value = "商品SkuId") + private String skuId; + + @ApiModelProperty(value = "购买数量") + private Integer num; + + @ApiModelProperty(value = "拼团id 如果是拼团购买 此值为拼团活动id,当pintuanId为空,则表示普通购买(或者拼团商品,单独购买)") + private String pintuanId; +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/dto/StorePromotionPriceDTO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/StorePromotionPriceDTO.java new file mode 100644 index 00000000..fc002ca8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/dto/StorePromotionPriceDTO.java @@ -0,0 +1,108 @@ +package cn.lili.modules.promotion.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 店铺促销计算数据传输对象 + * + * @author paulG + * @date 2020/11/17 + **/ +@Data +public class StorePromotionPriceDTO { + + /** + * 店铺ID + */ + @ApiModelProperty(value = "店铺ID") + private String storeId; + + /** + * 是否免运费 + */ + @ApiModelProperty(value = "是否免运费") + private Boolean isFreeFreight; + + /** + * 店铺商品数量合计 + */ + @ApiModelProperty(value = "店铺商品数量合计") + private Integer totalNum; + + /** + * 店铺商品重量合计 + */ + @ApiModelProperty(value = "店铺商品重量合计") + private Double totalWeight; + + /** + * 店铺商品原价格合计 + */ + @ApiModelProperty(value = "店铺商品原价格合计") + private Double totalOriginPrice; + + /** + * 店铺商品需支付积分合计 + */ + @ApiModelProperty(value = "店铺商品需支付积分合计") + private Double totalPoints; + + /** + * 店铺商品最终成交的总金额 + */ + @ApiModelProperty(value = "店铺商品最终成交的总金额") + private Double totalFinalePrice; + + /** + * 店铺参与促销商品价格合计 + */ + @ApiModelProperty(value = "店铺参与满优惠商品价格合计") + private Double totalJoinDiscountPrice; + + /** + * 店铺未参与促销商品价格合计 + */ + @ApiModelProperty(value = "店铺未参与满优惠商品价格合计") + private Double totalNotJoinDiscountPrice; + + /** + * 店铺商品优惠价格合计 + */ + @ApiModelProperty(value = "店铺商品优惠价格合计") + private Double totalDiscountPrice; + + /** + * 优惠券合计 + */ + @ApiModelProperty(value = "优惠券合计") + private Double totalCouponPrice; + + /** + * 店铺商品促销信息集合 + */ + @ApiModelProperty(value = "店铺商品促销信息集合") + private List goodsSkuPromotionPriceList; + + /** + * 参与的促销活动 + */ + @ApiModelProperty(value = "参与的促销活动") + private List joinPromotion; + + //=========distribution========== + + @ApiModelProperty(value = "1级单品分销返现支出") + private Double distributionCommission1; + + @ApiModelProperty(value = "2级单品分销返现支出") + private Double distributionCommission2; + + @ApiModelProperty(value = "平台收取交易佣金") + private Double platFormCommission; + + //=========end distribution========== + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/enums/CouponGetEnum.java b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/CouponGetEnum.java new file mode 100644 index 00000000..9b71ee7c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/CouponGetEnum.java @@ -0,0 +1,27 @@ +package cn.lili.modules.promotion.entity.enums; + +/** + * 优惠券获取方式枚举 + * + * @author Chopper + * @date 2020-03-19 9:36 上午 + */ +public enum CouponGetEnum { + + /** + * 枚举 + */ + FREE("免费获取"), ACTIVITY("活动获取"); + + private final String description; + + CouponGetEnum(String str) { + this.description = str; + } + + public String description() { + return description; + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/enums/CouponScopeTypeEnum.java b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/CouponScopeTypeEnum.java new file mode 100644 index 00000000..d099ef29 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/CouponScopeTypeEnum.java @@ -0,0 +1,28 @@ +package cn.lili.modules.promotion.entity.enums; + +/** + * 优惠券适用范围类型枚举 + * + * @author Chopper + * @date 2020-03-19 9:36 上午 + */ +public enum CouponScopeTypeEnum { + + /** + * 枚举 + */ + ALL("全品类"), + PORTION_GOODS_CATEGORY("部分商品分类"), + PORTION_SHOP_CATEGORY("部分店铺分类"), + PORTION_GOODS("指定商品"); + + private final String description; + + CouponScopeTypeEnum(String str) { + this.description = str; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/enums/CouponTypeEnum.java b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/CouponTypeEnum.java new file mode 100644 index 00000000..b7005f3a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/CouponTypeEnum.java @@ -0,0 +1,25 @@ +package cn.lili.modules.promotion.entity.enums; + +/** + * 优惠券折扣类型 + * + * @author Chopper + * @date 2020-03-19 9:36 上午 + */ +public enum CouponTypeEnum { + + /** + * 枚举 + */ + DISCOUNT("打折"), PRICE("减免现金"); + + private final String description; + + CouponTypeEnum(String str) { + this.description = str; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/enums/MemberCouponStatusEnum.java b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/MemberCouponStatusEnum.java new file mode 100644 index 00000000..8b98856c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/MemberCouponStatusEnum.java @@ -0,0 +1,24 @@ +package cn.lili.modules.promotion.entity.enums; + +/** + * 会员优惠券状态枚举 + * + * @author Chopper + * @date 2020-03-19 9:36 上午 + */ +public enum MemberCouponStatusEnum { + /** + * 枚举 + */ + NEW("领取"), USED("已使用"), EXPIRE("过期"), CLOSED("作废"); + + private final String description; + + MemberCouponStatusEnum(String str) { + this.description = str; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/enums/PromotionApplyStatusEnum.java b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/PromotionApplyStatusEnum.java new file mode 100644 index 00000000..cab25f91 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/PromotionApplyStatusEnum.java @@ -0,0 +1,25 @@ +package cn.lili.modules.promotion.entity.enums; + +/** + * 促销活动申请状态枚举 + * + * @author Chopper + * @date 2020-03-19 9:36 上午 + */ +public enum PromotionApplyStatusEnum { + + /** + * 枚举 + */ + APPLY("申请"), PASS("通过"), REFUSE("拒绝"); + + private final String description; + + PromotionApplyStatusEnum(String str) { + this.description = str; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/enums/PromotionStatusEnum.java b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/PromotionStatusEnum.java new file mode 100644 index 00000000..15affff3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/PromotionStatusEnum.java @@ -0,0 +1,22 @@ +package cn.lili.modules.promotion.entity.enums; + +/** + * 促销状态枚举 + * + * @author Chopper + * @date 2020-03-19 3:53 下午 + */ +public enum PromotionStatusEnum { + + NEW("新建"), START("开始/上架"), END("结束/下架"), CLOSE("紧急关闭/作废"); + + private final String description; + + PromotionStatusEnum(String str) { + this.description = str; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/enums/PromotionTypeEnum.java b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/PromotionTypeEnum.java new file mode 100644 index 00000000..2a346080 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/PromotionTypeEnum.java @@ -0,0 +1,43 @@ +package cn.lili.modules.promotion.entity.enums; + + +/** + * 促销分类枚举 + * + * @author Chopper + * @date 2021/2/1 19:32 + */ +public enum PromotionTypeEnum { + /** + * 促销枚举 + */ + PINTUAN("拼团"), SECKILL("秒杀"), COUPON("优惠券"), FULL_DISCOUNT("满减"), POINTS_GOODS("积分商品"); + + /** + * 拼团秒杀拥有独立库存,如果其他促销也有独立库存涉及库存扣减的,请添加在下方 + */ + static PromotionTypeEnum[] haveStockPromotion = new PromotionTypeEnum[]{PINTUAN, SECKILL}; + + private final String description; + + PromotionTypeEnum(String description) { + this.description = description; + } + + /** + * 是否拥有库存 + */ + public static boolean haveStock(String promotionType) { + for (PromotionTypeEnum promotionTypeEnum : haveStockPromotion) { + if (promotionTypeEnum.name().equals(promotionType)) { + return true; + } + } + return false; + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/enums/SeckillApplyStatusEnum.java b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/SeckillApplyStatusEnum.java new file mode 100644 index 00000000..d243df5a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/enums/SeckillApplyStatusEnum.java @@ -0,0 +1,26 @@ +package cn.lili.modules.promotion.entity.enums; + +/** + * 限时抢购状态枚举 + * + * @author paulG + * @date 2020/8/26 + **/ +public enum SeckillApplyStatusEnum { + + /** + * 当前店铺对当前限时抢购的申请状态 + */ + APPLIED("已经申请过"), NOT_APPLY("未报名"), EXPIRE("过期的"); + + private final String description; + + SeckillApplyStatusEnum(String str) { + this.description = str; + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/CouponSearchParams.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/CouponSearchParams.java new file mode 100644 index 00000000..56d44921 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/CouponSearchParams.java @@ -0,0 +1,214 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.modules.promotion.entity.enums.*; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Date; +import java.util.regex.Pattern; + +/** + * 优惠券查询通用类 + * + * @author paulG + * @date 2020/8/14 + **/ +@Data +public class CouponSearchParams implements Serializable { + + private static final long serialVersionUID = 4566880169478260409L; + private static final String PRICE_COLUMN = "price"; + + @ApiModelProperty(value = "店铺编号") + private String storeId; + + @ApiModelProperty(value = "会员id") + private String memberId; + + @ApiModelProperty(value = "优惠券名称") + private String couponName; + /** + * POINT("打折"), PRICE("减免现金"); + * + * @see cn.lili.modules.promotion.entity.enums.CouponTypeEnum + */ + @ApiModelProperty(value = "活动类型") + private String couponType; + /** + * @see cn.lili.modules.promotion.entity.enums.CouponScopeTypeEnum + */ + @ApiModelProperty(value = "关联范围类型") + private String scopeType; + @ApiModelProperty(value = "范围关联的id") + private String scopeId; + @ApiModelProperty(value = "面额,可以为范围,如10_1000") + private String price; + @ApiModelProperty(value = "发行数量,可以为范围,如10_1000") + private String publishNum; + @ApiModelProperty(value = "已被领取的数量,可以为范围,如10_1000") + private String receivedNum; + /** + * @see cn.lili.modules.promotion.entity.enums.CouponGetEnum + */ + @ApiModelProperty(value = "优惠券类型,分为免费领取和活动赠送") + private String getType; + + @ApiModelProperty(value = "活动开始时间") + private Long startTime; + + @ApiModelProperty(value = "活动结束时间") + private Long endTime; + /** + * @see MemberCouponStatusEnum + */ + @ApiModelProperty(value = "会员优惠券状态") + private String memberCouponStatus; + /** + * @see PromotionStatusEnum + */ + @ApiModelProperty(value = "活动状态") + private String promotionStatus; + + public QueryWrapper wrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (storeId != null) { + queryWrapper.in("store_id", Arrays.asList(storeId)); + } + if (CharSequenceUtil.isNotEmpty(couponName)) { + queryWrapper.like("coupon_name", couponName); + } + if (memberId != null) { + queryWrapper.eq("member_id", memberId); + } + if (CharSequenceUtil.isNotEmpty(couponType)) { + queryWrapper.eq("coupon_type", CouponTypeEnum.valueOf(couponType).name()); + } + if (CharSequenceUtil.isNotEmpty(scopeType)) { + queryWrapper.eq("scope_type", CouponScopeTypeEnum.valueOf(scopeType).name()); + } + if (CharSequenceUtil.isNotEmpty(scopeId)) { + queryWrapper.eq("scope_id", scopeId); + } + if (CharSequenceUtil.isNotEmpty(getType)) { + queryWrapper.eq("get_type", CouponGetEnum.valueOf(getType).name()); + } + if (startTime != null) { + queryWrapper.ge("start_time", new Date(startTime)); + } + if (endTime != null) { + queryWrapper.le("end_time", new Date(endTime)); + } + if (CharSequenceUtil.isNotEmpty(memberCouponStatus)) { + queryWrapper.eq("member_coupon_status", MemberCouponStatusEnum.valueOf(memberCouponStatus).name()); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + queryWrapper.eq("promotion_status", PromotionStatusEnum.valueOf(promotionStatus).name()); + } + this.betweenWrapper(queryWrapper); + queryWrapper.eq("delete_flag", false); + return queryWrapper; + } + + private void betweenWrapper(QueryWrapper queryWrapper) { + if (CharSequenceUtil.isNotEmpty(publishNum)) { + String[] s = publishNum.split("_"); + if (s.length > 1) { + queryWrapper.ge("publish_num", s[1]); + } else { + queryWrapper.le("publish_num", publishNum); + } + } + if (CharSequenceUtil.isNotEmpty(price)) { + String[] s = price.split("_"); + if (s.length > 1) { + queryWrapper.ge(PRICE_COLUMN, s[1]); + } else { + queryWrapper.le(PRICE_COLUMN, s[0]); + } + } + if (CharSequenceUtil.isNotEmpty(receivedNum)) { + String[] s = receivedNum.split("_"); + if (s.length > 1) { + queryWrapper.ge("received_num", s[1]); + } else { + queryWrapper.le("received_num", s[0]); + } + } + } + + public Query mongoQuery() { + Query query = new Query(); + if (storeId != null) { + query.addCriteria(Criteria.where("storeId").in(Arrays.asList(storeId))); + } + if (CharSequenceUtil.isNotEmpty(couponName)) { + Pattern pattern = Pattern.compile("^.*" + couponName + ".*$", Pattern.CASE_INSENSITIVE); + query.addCriteria(Criteria.where("couponName").regex(pattern)); + } + if (memberId != null) { + query.addCriteria(Criteria.where("memberId").is(memberId)); + } + if (CharSequenceUtil.isNotEmpty(couponType)) { + query.addCriteria(Criteria.where("couponType").is(CouponTypeEnum.valueOf(couponType).name())); + } + if (CharSequenceUtil.isNotEmpty(scopeType)) { + query.addCriteria(Criteria.where("scopeType").is(CouponScopeTypeEnum.valueOf(scopeType).name())); + } + if (CharSequenceUtil.isNotEmpty(scopeId)) { + query.addCriteria(Criteria.where("scopeId").is(scopeId)); + } + if (CharSequenceUtil.isNotEmpty(getType)) { + query.addCriteria(Criteria.where("getType").is(CouponGetEnum.valueOf(getType).name())); + } + if (startTime != null) { + query.addCriteria(Criteria.where("startTime").gte(new Date(startTime))); + } + if (endTime != null) { + query.addCriteria(Criteria.where("endTime").lte(new Date(endTime))); + } + if (CharSequenceUtil.isNotEmpty(memberCouponStatus)) { + query.addCriteria(Criteria.where("memberCouponStatus").is(MemberCouponStatusEnum.valueOf(memberCouponStatus).name())); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + query.addCriteria(Criteria.where("promotionStatus").is(PromotionStatusEnum.valueOf(promotionStatus).name())); + } + query.addCriteria(Criteria.where("deleteFlag").is(false)); + betweenQuery(query); + return query; + } + + private void betweenQuery(Query query) { + if (CharSequenceUtil.isNotEmpty(price)) { + String[] s = price.split("_"); + if (s.length > 1) { + query.addCriteria(Criteria.where(PRICE_COLUMN).gt(s[1])); + } else { + query.addCriteria(Criteria.where(PRICE_COLUMN).lt(s[0])); + } + } + if (CharSequenceUtil.isNotEmpty(publishNum)) { + String[] s = publishNum.split("_"); + if (s.length > 1) { + query.addCriteria(Criteria.where("publishNum").gt(s[1])); + } else { + query.addCriteria(Criteria.where("publishNum").lt(s[0])); + } + } + if (CharSequenceUtil.isNotEmpty(receivedNum)) { + String[] s = receivedNum.split("_"); + if (s.length > 1) { + query.addCriteria(Criteria.where("receivedNum").gt(s[1])); + } else { + query.addCriteria(Criteria.where("receivedNum").lt(s[0])); + } + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/CouponVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/CouponVO.java new file mode 100644 index 00000000..c27e2a52 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/CouponVO.java @@ -0,0 +1,30 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.lili.modules.promotion.entity.dos.Coupon; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 优惠券视图对象 + * + * @author Chopper + * @date 2020/8/14 + */ +@Data +@ApiModel(value = "优惠券") +public class CouponVO extends Coupon { + + private static final long serialVersionUID = 8372420376262437018L; + + /** + * 促销关联的商品 + */ + @ApiModelProperty(value = "优惠券关联商品集合") + private List promotionGoodsList; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/FullDiscountSearchParams.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/FullDiscountSearchParams.java new file mode 100644 index 00000000..f4709418 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/FullDiscountSearchParams.java @@ -0,0 +1,90 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Date; +import java.util.regex.Pattern; + +/** + * 满优惠查询通用类 + * + * @author paulG + * @date 2020/8/21 + **/ +@Data +public class FullDiscountSearchParams implements Serializable { + + private static final long serialVersionUID = -4052716630253333681L; + + + @ApiModelProperty(value = "活动名称") + private String promotionName; + + @ApiModelProperty(value = "店铺编号 如有多个','分割") + private String storeId; + + @ApiModelProperty(value = "活动开始时间", required = true) + private Long startTime; + + @ApiModelProperty(value = "活动结束时间", required = true) + private Long endTime; + + /** + * @see PromotionStatusEnum + */ + @ApiModelProperty(value = "活动状态") + private String promotionStatus; + + + public QueryWrapper wrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (CharSequenceUtil.isNotEmpty(promotionName)) { + queryWrapper.like("title", promotionName); + } + if (storeId != null) { + queryWrapper.in("store_id", Arrays.asList(storeId.split(","))); + } + if (startTime != null) { + queryWrapper.ge("start_time", new Date(startTime)); + } + if (endTime != null) { + queryWrapper.le("end_time", new Date(endTime)); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + queryWrapper.eq("promotion_status", PromotionStatusEnum.valueOf(promotionStatus).name()); + } + return queryWrapper; + } + + public Query mongoQuery() { + Query query = new Query(); + if (CharSequenceUtil.isNotEmpty(promotionName)) { + Pattern pattern = Pattern.compile("^.*" + promotionName + ".*$", Pattern.CASE_INSENSITIVE); + query.addCriteria(Criteria.where("promotionName").regex(pattern)); + } + if (storeId != null) { + query.addCriteria(Criteria.where("storeId").in(Arrays.asList(storeId.split(",")))); + } + if (startTime != null) { + query.addCriteria(Criteria.where("startTime").gte(new Date(startTime))); + } + if (endTime != null) { + query.addCriteria(Criteria.where("endTime").lte(new Date(endTime))); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + query.addCriteria(Criteria.where("promotionStatus").is(PromotionStatusEnum.valueOf(promotionStatus).name())); + } + query.addCriteria(Criteria.where("deleteFlag").is(false)); + return query; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanMemberVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanMemberVO.java new file mode 100644 index 00000000..94baea1e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanMemberVO.java @@ -0,0 +1,50 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.lili.modules.member.entity.dos.Member; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 拼图会员视图对象 + * + * @author paulG + * @date 2021/3/3 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PintuanMemberVO { + + @ApiModelProperty(value = "会员编号") + private String memberId; + + @ApiModelProperty(value = "会员用户名") + private String memberName; + + @ApiModelProperty(value = "会员头像") + private String face; + + @ApiModelProperty(value = "昵称") + private String nickName; + + @ApiModelProperty(value = "参团订单编号") + private String orderSn; + + @ApiModelProperty(value = "已参团人数") + private Integer groupedNum; + + @ApiModelProperty(value = "待参团人数") + private Integer toBeGroupedNum; + + @ApiModelProperty(value = "成团人数") + private Integer groupNum; + + public PintuanMemberVO(Member member) { + this.memberId = member.getId(); + this.memberName = member.getUsername(); + this.face = member.getFace(); + this.nickName = member.getNickName(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanSearchParams.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanSearchParams.java new file mode 100644 index 00000000..34a64425 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanSearchParams.java @@ -0,0 +1,99 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.Date; +import java.util.regex.Pattern; + +/** + * 拼团查询通用类 + * + * @author paulG + * @date 2020/10/9 + **/ +@Data +public class PintuanSearchParams { + + @ApiModelProperty(value = "商家id") + private String storeId; + + @ApiModelProperty(value = "商家名称,如果是平台,这个值为 platform") + private String storeName; + + @NotEmpty(message = "活动名称不能为空") + @ApiModelProperty(value = "活动名称", required = true) + private String promotionName; + + /** + * @see PromotionStatusEnum + */ + @ApiModelProperty(value = "活动状态") + @NotNull(message = "活动状态不能为空") + private String promotionStatus; + + @ApiModelProperty(value = "活动开始时间") + private Long startTime; + + @ApiModelProperty(value = "活动结束时间") + private Long endTime; + + public QueryWrapper wrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (CharSequenceUtil.isNotEmpty(promotionName)) { + queryWrapper.like("promotion_name", promotionName); + } + if (!StringUtils.isEmpty(storeName)) { + queryWrapper.like("store_name", storeName); + } + if (!StringUtils.isEmpty(storeId)) { + queryWrapper.eq("store_id", storeName); + } + if (startTime != null) { + queryWrapper.ge("start_time", new Date(startTime)); + } + if (endTime != null) { + queryWrapper.le("end_time", new Date(endTime)); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + queryWrapper.eq("promotion_status", PromotionStatusEnum.valueOf(promotionStatus).name()); + } + queryWrapper.eq("delete_flag", false); + return queryWrapper; + } + + public Query mongoQuery() { + Query query = new Query(); + if (CharSequenceUtil.isNotEmpty(promotionName)) { + Pattern pattern = Pattern.compile("^.*" + promotionName + ".*$", Pattern.CASE_INSENSITIVE); + query.addCriteria(Criteria.where("promotionName").regex(pattern)); + } + if (!StringUtils.isEmpty(storeName)) { + Pattern pattern = Pattern.compile("^.*" + storeName + ".*$", Pattern.CASE_INSENSITIVE); + query.addCriteria(Criteria.where("storeName").regex(pattern)); + } + if (!StringUtils.isEmpty(storeId)) { + query.addCriteria(Criteria.where("storeId").is(storeId)); + } + if (startTime != null) { + query.addCriteria(Criteria.where("startTime").gte(new Date(startTime))); + } + if (endTime != null) { + query.addCriteria(Criteria.where("endTime").lte(new Date(endTime))); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + query.addCriteria(Criteria.where("promotionStatus").is(PromotionStatusEnum.valueOf(promotionStatus).name())); + } + query.addCriteria(Criteria.where("deleteFlag").is(false)); + return query; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanShareVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanShareVO.java new file mode 100644 index 00000000..fa9a2ba0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanShareVO.java @@ -0,0 +1,21 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import lombok.Data; + +import java.util.List; + +/** + * 拼图会员分享对象 + * + * @author paulG + * @date 2021/3/24 + **/ +@Data +public class PintuanShareVO { + + private PromotionGoods promotionGoods; + + private List pintuanMemberVOS; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanVO.java new file mode 100644 index 00000000..3b134d0e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PintuanVO.java @@ -0,0 +1,23 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.lili.modules.promotion.entity.dos.Pintuan; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 拼团视图对象 + * + * @author paulG + * @date 2020/10/28 + **/ +@Data +public class PintuanVO extends Pintuan { + + private static final long serialVersionUID = 218582640653676201L; + + private List promotionGoodsList; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PointsGoodsCategoryVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PointsGoodsCategoryVO.java new file mode 100644 index 00000000..919dd989 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PointsGoodsCategoryVO.java @@ -0,0 +1,18 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.lili.modules.promotion.entity.dos.PointsGoodsCategory; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 积分商品分类视图对象 + * + * @author paulG + * @date 2021/1/15 + **/ +@Data +public class PointsGoodsCategoryVO extends PointsGoodsCategory { + + private static final long serialVersionUID = 5528833118735059182L; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PointsGoodsSearchParams.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PointsGoodsSearchParams.java new file mode 100644 index 00000000..1b37c5d3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PointsGoodsSearchParams.java @@ -0,0 +1,103 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; + +import java.util.regex.Pattern; + +/** + * 积分商品查询通用类 + * + * @author paulG + * @date 2021/1/13 + **/ +@Data +public class PointsGoodsSearchParams { + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品skuId") + private String skuId; + + @ApiModelProperty(value = "积分商品分类编号") + private String pointsGoodsCategoryId; + + @ApiModelProperty(value = "是否为推荐商品") + private Boolean recommend; + + @ApiModelProperty(value = "积分,可以为范围,如10_1000") + private String points; + + /** + * @see PromotionStatusEnum + */ + @ApiModelProperty(value = "活动状态") + private String promotionStatus; + + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (CharSequenceUtil.isNotEmpty(goodsName)) { + queryWrapper.eq("gs.goods_name", goodsName); + } + if (CharSequenceUtil.isNotEmpty(skuId)) { + queryWrapper.eq("pg.sku_id", skuId); + } + if (CharSequenceUtil.isNotEmpty(pointsGoodsCategoryId)) { + queryWrapper.eq("pg.points_goods_category_id", pointsGoodsCategoryId); + } + if (CharSequenceUtil.isNotEmpty(points)) { + String[] s = points.split("_"); + if (s.length > 1) { + queryWrapper.between("pg.points", s[0], s[1]); + } else { + queryWrapper.eq("pg.points", s[0]); + } + } + if (recommend != null) { + queryWrapper.eq("gs.recommend", recommend); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + queryWrapper.eq("pg.promotion_status", promotionStatus); + } + return queryWrapper; + } + + + public Query mongoQuery() { + Query query = new Query(); + if (CharSequenceUtil.isNotEmpty(goodsName)) { + Pattern pattern = Pattern.compile("^.*" + goodsName + ".*$", Pattern.CASE_INSENSITIVE); + query.addCriteria(Criteria.where("goodsSku.goodsName").regex(pattern)); + } + if (CharSequenceUtil.isNotEmpty(skuId)) { + query.addCriteria(Criteria.where("skuId").is(skuId)); + } + if (CharSequenceUtil.isNotEmpty(pointsGoodsCategoryId)) { + query.addCriteria(Criteria.where("pointsGoodsCategoryId").is(pointsGoodsCategoryId)); + } + if (CharSequenceUtil.isNotEmpty(points)) { + String[] s = points.split("_"); + if (s.length > 1) { + query.addCriteria(Criteria.where("points").gte(Integer.parseInt(s[0])).lte(Integer.parseInt(s[1]))); + } else { + query.addCriteria(Criteria.where("points").gte(Integer.parseInt(s[0]))); + } + } + if (recommend != null) { + query.addCriteria(Criteria.where("goodsSku.recommend").is(recommend)); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + query.addCriteria(Criteria.where("promotionStatus").is(promotionStatus)); + } + query.addCriteria(Criteria.where("deleteFlag").is(false)); + return query; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PointsGoodsVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PointsGoodsVO.java new file mode 100644 index 00000000..e649ffa6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PointsGoodsVO.java @@ -0,0 +1,24 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.promotion.entity.dos.PointsGoods; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 积分商品视图对象 + * + * @author paulG + * @date 2021/1/13 + **/ +@EqualsAndHashCode(callSuper = true) +@Data +public class PointsGoodsVO extends PointsGoods { + + private static final long serialVersionUID = -5163709626742905057L; + + @ApiModelProperty(value = "商品规格详细信息") + private GoodsSku goodsSku; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PromotionGoodsSearchParams.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PromotionGoodsSearchParams.java new file mode 100644 index 00000000..12951732 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/PromotionGoodsSearchParams.java @@ -0,0 +1,69 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 促销商品查询通用类 + * + * @author paulG + * @date 2021/2/21 + **/ +@Data +public class PromotionGoodsSearchParams { + + @ApiModelProperty(value = "促销活动id") + private String promotionId; + + @ApiModelProperty(value = "促销类型") + private String promotionType; + + @ApiModelProperty(value = "促销状态") + private String promotionStatus; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品分类路径") + private String categoryPath; + + @ApiModelProperty(value = "开始时间") + private Long startTime; + + @ApiModelProperty(value = "结束时间") + private Long endTime; + + + public LambdaQueryWrapper queryWrapper() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (CharSequenceUtil.isNotEmpty(promotionId)) { + queryWrapper.eq(PromotionGoods::getPromotionId, promotionId); + } + if (CharSequenceUtil.isNotEmpty(goodsName)) { + queryWrapper.like(PromotionGoods::getGoodsName, goodsName); + } + if (CharSequenceUtil.isNotEmpty(promotionType)) { + queryWrapper.eq(PromotionGoods::getPromotionType, promotionType); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + queryWrapper.eq(PromotionGoods::getPromotionStatus, promotionStatus); + } + if (CharSequenceUtil.isNotEmpty(categoryPath)) { + queryWrapper.like(PromotionGoods::getCategoryPath, categoryPath); + } + if (startTime != null) { + queryWrapper.ge(PromotionGoods::getStartTime, new Date(startTime)); + } + if (endTime != null) { + queryWrapper.ge(PromotionGoods::getEndTime, new Date(endTime)); + } + queryWrapper.eq(PromotionGoods::getDeleteFlag, false); + return queryWrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillApplyVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillApplyVO.java new file mode 100644 index 00000000..d351d4d2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillApplyVO.java @@ -0,0 +1,18 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.lili.modules.promotion.entity.dos.SeckillApply; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 限时抢购申请视图对象 + * + * @author paulG + * @date 2020/8/21 + **/ +@Data +public class SeckillApplyVO extends SeckillApply { + + private static final long serialVersionUID = 7076774723400062602L; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillGoodsVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillGoodsVO.java new file mode 100644 index 00000000..238c5cdb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillGoodsVO.java @@ -0,0 +1,55 @@ +package cn.lili.modules.promotion.entity.vos; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 限时抢购商品视图对象 + * + * @author paulG + * @date 2020/8/26 + **/ +@Data +public class SeckillGoodsVO implements Serializable { + + private static final long serialVersionUID = 5170316685407828228L; + + @ApiModelProperty(value = "活动id") + private String seckillId; + + @ApiModelProperty(value = "时刻") + private Integer timeLine; + + @ApiModelProperty(value = "商品id") + private String goodsId; + + @ApiModelProperty(value = "以积分渠道购买需要积分数量") + private Integer point; + + @ApiModelProperty(value = "skuID") + private String skuId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "商品图片") + private String goodsImage; + + @ApiModelProperty(value = "商家id") + private String storeId; + + @ApiModelProperty(value = "价格") + private Double price; + + @ApiModelProperty(value = "促销数量") + private Integer quantity; + + @ApiModelProperty(value = "已售数量") + private Integer salesNum; + + @ApiModelProperty(value = "商品原始价格") + private Double originalPrice; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillSearchParams.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillSearchParams.java new file mode 100644 index 00000000..04838e41 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillSearchParams.java @@ -0,0 +1,134 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.lili.modules.promotion.entity.enums.PromotionApplyStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Date; +import java.util.regex.Pattern; + +/** + * 限时抢购查询通用类 + * + * @author paulG + * @date 2020/8/21 + **/ +@Data +public class SeckillSearchParams implements Serializable { + + private static final long serialVersionUID = -4052716630253333681L; + + @ApiModelProperty(value = "限时抢购活动编号") + private String seckillId; + + @ApiModelProperty(value = "活动名称") + private String promotionName; + + @ApiModelProperty(value = "时刻") + private Integer timeLine; + + @ApiModelProperty(value = "商家id") + private String[] storeIds; + + @ApiModelProperty(value = "商家编号") + private String storeId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "活动开始时间", required = true) + private Long startTime; + + @ApiModelProperty(value = "活动结束时间", required = true) + private Long endTime; + + /** + * @see PromotionStatusEnum + */ + @ApiModelProperty(value = "活动状态") + private String promotionStatus; + + /** + * @see cn.lili.modules.promotion.entity.enums.PromotionApplyStatusEnum + */ + @ApiModelProperty(value = "APPLY(\"申请\"), PASS(\"通过\"), REFUSE(\"拒绝\")") + private String promotionApplyStatus; + + public QueryWrapper wrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (CharSequenceUtil.isNotEmpty(goodsName)) { + queryWrapper.like("goods_name", goodsName); + } + if (CharSequenceUtil.isNotEmpty(promotionName)) { + queryWrapper.like("promotion_name", promotionName); + } + if (CharSequenceUtil.isNotEmpty(seckillId)) { + queryWrapper.eq("seckill_id", seckillId); + } + if (storeIds != null) { + queryWrapper.in("store_id", Arrays.asList(storeIds)); + } + if (timeLine != null) { + queryWrapper.eq("time_line", timeLine); + } + if (startTime != null) { + queryWrapper.ge("start_time", new Date(startTime)); + } + if (endTime != null) { + queryWrapper.le("end_time", new Date(endTime)); + } + if (CharSequenceUtil.isNotEmpty(promotionApplyStatus)) { + queryWrapper.eq("promotion_apply_status", PromotionApplyStatusEnum.valueOf(promotionApplyStatus).name()); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + queryWrapper.eq("promotion_status", PromotionStatusEnum.valueOf(promotionStatus).name()); + } + return queryWrapper; + } + + public Query mongoQuery() { + Query query = new Query(); + if (CharSequenceUtil.isNotEmpty(goodsName)) { + Pattern pattern = Pattern.compile("^.*" + goodsName + ".*$", Pattern.CASE_INSENSITIVE); + query.addCriteria(Criteria.where("goodsName").regex(pattern)); + } + if (CharSequenceUtil.isNotEmpty(promotionName)) { + Pattern pattern = Pattern.compile("^.*" + promotionName + ".*$", Pattern.CASE_INSENSITIVE); + query.addCriteria(Criteria.where("promotionName").regex(pattern)); + } + if (CharSequenceUtil.isNotEmpty(seckillId)) { + query.addCriteria(Criteria.where("_id").is(seckillId)); + } + if (storeIds != null) { + Pattern pattern = Pattern.compile("^.*" + ArrayUtil.join(storeIds, ",") + ".*$", Pattern.CASE_INSENSITIVE); + query.addCriteria(Criteria.where("storeIds").regex(pattern)); + } + if (timeLine != null) { + query.addCriteria(Criteria.where("timeLine").is(timeLine)); + } + if (startTime != null) { + query.addCriteria(Criteria.where("startTime").gte(new Date(startTime))); + } + if (endTime != null) { + query.addCriteria(Criteria.where("endTime").lte(new Date(endTime))); + } + if (CharSequenceUtil.isNotEmpty(promotionApplyStatus)) { + query.addCriteria(Criteria.where("promotionApplyStatus").is(PromotionApplyStatusEnum.valueOf(promotionApplyStatus).name())); + } + if (CharSequenceUtil.isNotEmpty(promotionStatus)) { + query.addCriteria(Criteria.where("promotionStatus").is(PromotionStatusEnum.valueOf(promotionStatus).name())); + } + query.addCriteria(Criteria.where("deleteFlag").is(false)); + return query; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillTimelineVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillTimelineVO.java new file mode 100644 index 00000000..1c8a7ae1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillTimelineVO.java @@ -0,0 +1,32 @@ +package cn.lili.modules.promotion.entity.vos; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 限时抢购时刻视图对象 + * + * @author paulG + * @date 2020/8/27 + **/ +@Data +public class SeckillTimelineVO implements Serializable { + + private static final long serialVersionUID = -8171512491016990179L; + + @ApiModelProperty(value = "时刻") + private Integer timeLine; + + @ApiModelProperty(value = "秒杀开始时间,这个是时间戳") + private Long startTime; + + @ApiModelProperty(value = "距离本组活动开始的时间,秒为单位。如果活动的开始时间是10点,服务器时间为8点,距离开始还有多少时间") + private Long distanceStartTime; + + @ApiModelProperty(value = "本组活动内的限时抢购商品列表") + private List seckillGoodsList; + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillVO.java b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillVO.java new file mode 100644 index 00000000..6191c84d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/entity/vos/SeckillVO.java @@ -0,0 +1,89 @@ +package cn.lili.modules.promotion.entity.vos; + +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.DateUtil; +import cn.lili.modules.promotion.entity.dos.Seckill; +import cn.lili.modules.promotion.entity.dos.SeckillApply; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +/** + * 限时抢购视图对象 + * + * @author paulG + * @date 2020/8/20 + **/ +@Data +public class SeckillVO extends Seckill { + + private static final long serialVersionUID = 2891461638257152270L; + + /** + * @see cn.lili.modules.promotion.entity.enums.SeckillApplyStatusEnum + */ + @ApiModelProperty(value = "报名状态") + private String seckillApplyStatus; + + /** + * 当前限时抢购下所有的秒杀申请信息 + */ + private List seckillApplyList; + + /** + * 检查当前时间 + */ + public void checkTime() { + String[] timeRange = this.getHours().split(","); + int[] hoursSored = Arrays.stream(timeRange).mapToInt(Integer::parseInt).toArray(); + Arrays.sort(hoursSored); + int lastTime = 0; + for (int s : hoursSored) { + if (lastTime == s) { + throw new ServiceException("抢购区间的值不能重复"); + } else { + lastTime = s; + } + + if (s < 0 || s > 23) { + throw new ServiceException("抢购区间必须在0点到23点的整点时刻"); + } + } + + // 活动开始时间 + long startTime = this.getStartTime().getTime() / 1000; + // 报名截止时间 + long applyEndTime = this.getApplyEndTime().getTime() / 1000; + + int timeHour = hoursSored[0]; + int endTimeHour = hoursSored[hoursSored.length - 1]; + //获取活动开始当天0点的时间 + String startDate = DateUtil.toString(startTime, DateUtil.STANDARD_DATE_FORMAT) + " " + (timeHour > 10 ? timeHour : "0" + timeHour) + ":00:00"; + long startDayTime = cn.hutool.core.date.DateUtil.parse(startDate, DateUtil.STANDARD_FORMAT).getTime() / 1000; + // 结束时间 + String endDate = DateUtil.toString(startTime, DateUtil.STANDARD_DATE_FORMAT) + " " + (endTimeHour > 10 ? endTimeHour : "0" + endTimeHour) + ":59:00"; + long endDayTime = cn.hutool.core.date.DateUtil.parse(endDate, DateUtil.STANDARD_FORMAT).getTime() / 1000; + //活动时间小于当天开始时间 + if (startDayTime < DateUtil.startOfTodDay()) { + throw new ServiceException("活动时间不能小于当前时间"); + } + + //报名截止时间小于当前时间 + if (applyEndTime < DateUtil.getDateline()) { + throw new ServiceException("报名截止时间不能小于当前时间"); + } + + //报名截止时间大于活动开始当天的起始时间 + if (applyEndTime >= startDayTime) { + throw new ServiceException("报名截止时间不能大于等于活动开始时间"); + } + + this.setStartTime(new Date(startDayTime * 1000)); + this.setEndTime(new Date(endDayTime * 1000)); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/mapper/CouponMapper.java b/framework/src/main/java/cn/lili/modules/promotion/mapper/CouponMapper.java new file mode 100644 index 00000000..a3307375 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/mapper/CouponMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.promotion.mapper; + +import cn.lili.modules.promotion.entity.dos.Coupon; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 优惠券数据处理层 + * + * @author Chopper + * @date 2020/8/21 + */ +public interface CouponMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/mapper/FullDiscountMapper.java b/framework/src/main/java/cn/lili/modules/promotion/mapper/FullDiscountMapper.java new file mode 100644 index 00000000..d74297c3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/mapper/FullDiscountMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.promotion.mapper; + +import cn.lili.modules.promotion.entity.dos.FullDiscount; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 满优惠数据处理层 + * + * @author Chopper + * @date 2020/8/21 + */ +public interface FullDiscountMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/mapper/MemberCouponMapper.java b/framework/src/main/java/cn/lili/modules/promotion/mapper/MemberCouponMapper.java new file mode 100644 index 00000000..27d2cc4b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/mapper/MemberCouponMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.promotion.mapper; + +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 会员优惠券数据处理层 + * + * @author Chopper + * @date 2020/8/21 + */ +public interface MemberCouponMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/mapper/PintuanMapper.java b/framework/src/main/java/cn/lili/modules/promotion/mapper/PintuanMapper.java new file mode 100644 index 00000000..b6345bc4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/mapper/PintuanMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.promotion.mapper; + +import cn.lili.modules.promotion.entity.dos.Pintuan; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 拼团数据处理层 + * + * @author Chopper + * @date 2020/8/21 + */ +public interface PintuanMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/mapper/PointsGoodsCategoryMapper.java b/framework/src/main/java/cn/lili/modules/promotion/mapper/PointsGoodsCategoryMapper.java new file mode 100644 index 00000000..c706e299 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/mapper/PointsGoodsCategoryMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.promotion.mapper; + +import cn.lili.modules.promotion.entity.dos.PointsGoodsCategory; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 积分商品分类数据处理层 + * + * @author paulG + * @date 2020/8/21 + **/ +public interface PointsGoodsCategoryMapper extends BaseMapper { + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/mapper/PointsGoodsMapper.java b/framework/src/main/java/cn/lili/modules/promotion/mapper/PointsGoodsMapper.java new file mode 100644 index 00000000..d3e32af8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/mapper/PointsGoodsMapper.java @@ -0,0 +1,24 @@ +package cn.lili.modules.promotion.mapper; + +import cn.lili.modules.promotion.entity.dos.PointsGoods; +import cn.lili.modules.promotion.entity.vos.PointsGoodsVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 积分商品数据处理层 + * + * @author paulG + * @date 2020/8/21 + **/ +public interface PointsGoodsMapper extends BaseMapper { + + + @Select("select * from points_goods pg left join goods_sku gs on pg.sku_id = gs.id ${ew.customSqlSegment}") + IPage getPointsGoodsVO(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/mapper/PromotionGoodsMapper.java b/framework/src/main/java/cn/lili/modules/promotion/mapper/PromotionGoodsMapper.java new file mode 100644 index 00000000..bef0f0cb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/mapper/PromotionGoodsMapper.java @@ -0,0 +1,36 @@ +package cn.lili.modules.promotion.mapper; + +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.Date; + +/** + * 促销商品数据处理层 + * + * @author Chopper + * @date 2020/8/21 + */ +public interface PromotionGoodsMapper extends BaseMapper { + + + /** + * 查询参加活动促销商品是否同时参加指定类型的活动 + * + * @param promotionType 促销类型 + * @param skuId skuId + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 共参加了几种活动 + */ + @Select("select count(0) from li_promotion_goods where promotion_type = #{promotionType} and sku_id = #{skuId} and (" + + "( start_time < #{startTime} && end_time > #{startTime} ) || ( start_time < #{endTime} && end_time > #{endTime} ) || " + + "( start_time < #{startTime} && end_time > #{endTime} ) || ( start_time > #{startTime} && end_time < #{endTime} )" + + " || promotion_status = 'START' )") + Integer selectInnerOverlapPromotionGoods(@Param("promotionType") String promotionType, + @Param("skuId") String skuId, + @Param("startTime") Date startTime, + @Param("endTime") Date endTime); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/mapper/SeckillApplyMapper.java b/framework/src/main/java/cn/lili/modules/promotion/mapper/SeckillApplyMapper.java new file mode 100644 index 00000000..45eb1ce6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/mapper/SeckillApplyMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.promotion.mapper; + +import cn.lili.modules.promotion.entity.dos.SeckillApply; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 限时抢购申请数据处理层 + * + * @author Chopper + * @date 2020/8/21 + */ +public interface SeckillApplyMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/mapper/SeckillMapper.java b/framework/src/main/java/cn/lili/modules/promotion/mapper/SeckillMapper.java new file mode 100644 index 00000000..1ffae1f8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/mapper/SeckillMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.promotion.mapper; + +import cn.lili.modules.promotion.entity.dos.Seckill; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 限时抢购数据处理层 + * + * @author Chopper + * @date 2020/8/21 + */ +public interface SeckillMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/CouponService.java b/framework/src/main/java/cn/lili/modules/promotion/service/CouponService.java new file mode 100644 index 00000000..037f1193 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/CouponService.java @@ -0,0 +1,107 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.Coupon; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.vos.CouponSearchParams; +import cn.lili.modules.promotion.entity.vos.CouponVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 优惠券业务层 + * + * @author Chopper + * @date 2020/8/21 + */ +public interface CouponService extends IService { + + /** + * 添加优惠券 + * + * @param coupon 优惠券 + * @return 是否添加成功 + */ + CouponVO add(CouponVO coupon); + + /** + * 更新优惠卷 + * + * @param coupon 优惠卷信息 + * @return 是否更新成功 + */ + CouponVO updateCoupon(CouponVO coupon); + + /** + * 更新优惠卷状态 + * + * @param couponId 优惠券编号 + * @param promotionStatus 促销状态 + * @return 更新结果 + */ + boolean updateCouponStatus(List couponId, PromotionStatusEnum promotionStatus); + + /** + * 删除优惠券 + * + * @param id 优惠券id + * @return 是否删除成功 + */ + boolean deleteCoupon(String id); + + + /** + * 根据查询条件从mongo中获取优惠券信息列表 + * + * @param param 查询参数 + * @param page 分页参数 + * @return 优惠券信息列表 + */ + IPage getCouponsByPageFromMongo(CouponSearchParams param, PageVO page); + + /** + * 根据查询条件从mongo中获取优惠券信息列表 + * + * @param param 查询参数 + * @param page 分页参数 + * @return 优惠券信息列表 + */ + IPage getCanReceiveCoupons(CouponSearchParams param, PageVO page); + + /** + * 获取优惠券详情 + * + * @param id 优惠券id + * @return 优惠券详情 + */ + CouponVO getCouponDetailFromMongo(String id); + + /** + * 根据条件获取优惠券列表 + * + * @param param 条件参数 + * @param page 分页条件 + * @return 可领取优惠券集合 + */ + IPage getCouponsByPage(CouponSearchParams param, PageVO page); + + /** + * 领取优惠券 + * + * @param couponId 优惠券id + * @param receiveNum 领取数量 + */ + void receiveCoupon(String couponId, Integer receiveNum); + + /** + * 使用优惠券 + * + * @param couponId 优惠券id + * @param usedNum 使用数量 + */ + void usedCoupon(String couponId, Integer usedNum); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/FullDiscountService.java b/framework/src/main/java/cn/lili/modules/promotion/service/FullDiscountService.java new file mode 100644 index 00000000..03e3b3db --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/FullDiscountService.java @@ -0,0 +1,87 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.FullDiscount; +import cn.lili.modules.promotion.entity.vos.FullDiscountSearchParams; +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 满优惠业务层 + * + * @author Chopper + * @date 2020/8/21 + */ +public interface FullDiscountService extends IService { + + /** + * 当前满优惠活动 + * + * @param storeId 商家编号 + * @return 满优惠活动信息 + */ + FullDiscountVO currentPromotion(String storeId); + + /** + * 当前满优惠活动 + * + * @param storeId 商家编号 + * @return 满优惠活动信息 + */ + List currentPromotion(List storeId); + + /** + * 添加满优惠活动 + * + * @param fullDiscountVO 满优惠活动信息 + * @return 满优惠活动 + */ + FullDiscount addFullDiscount(FullDiscountVO fullDiscountVO); + + /** + * 从mysql中分页获取满优惠列表 + * + * @param searchParams 参数 + * @param page 分页参数 + * @return 满优惠列表 + */ + IPage getFullDiscountByPageFromMysql(FullDiscountSearchParams searchParams, PageVO page); + + /** + * 从mongo中分页获取满优惠列表 + * + * @param searchParams 搜索参数 + * @param page 分页参数 + * @return 满优惠列表 + */ + IPage getFullDiscountByPageFromMongo(FullDiscountSearchParams searchParams, PageVO page); + + + /** + * 修改满优惠活动 + * + * @param fullDiscountVO 满优惠活动信息 + * @return 满优惠活动 + */ + FullDiscountVO modifyFullDiscount(FullDiscountVO fullDiscountVO); + + /** + * 获取满优惠活动详情 + * + * @param id 满优惠KID + * @return 满优惠活动详情 + */ + FullDiscountVO getFullDiscount(String id); + + /** + * 删除满优惠获取 + * + * @param id 满优惠活动编号 + * @return 删除结果 + */ + boolean deleteFullDiscount(String id); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/MemberAddressService.java b/framework/src/main/java/cn/lili/modules/promotion/service/MemberAddressService.java new file mode 100644 index 00000000..cef85ea4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/MemberAddressService.java @@ -0,0 +1,63 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.MemberAddress; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 收货地址业务层 + * + * @author Chopper + * @date 2020/11/18 9:45 上午 + */ +public interface MemberAddressService extends IService { + + /** + * 根据会员获取会员地址分页列表 + * + * @param page 分页条件 + * @return 会员地址分页列表 + */ + IPage getAddressByMember(PageVO page, String memberId); + + /** + * 根据地址ID获取当前会员地址信息 + * + * @param id 地址ID + * @return 当前会员的地址信息 + */ + MemberAddress getMemberAddress(String id); + + /** + * 根据地址ID获取当前会员地址信息 + * + * @return 当前会员的地址信息 + */ + MemberAddress getDefaultMemberAddress(); + + /** + * 添加会员收货地址 + * + * @param memberAddress 收货地址 + * @return 操作状态 + */ + MemberAddress saveMemberAddress(MemberAddress memberAddress); + + /** + * 修改会员收货地址信息 + * + * @param memberAddress 收货地址 + * @return 操作状态 + */ + MemberAddress updateMemberAddress(MemberAddress memberAddress); + + /** + * 删除会员收货地址信息 + * + * @param id 收货地址ID + * @return 操作状态 + */ + boolean removeMemberAddress(String id); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/MemberCouponService.java b/framework/src/main/java/cn/lili/modules/promotion/service/MemberCouponService.java new file mode 100644 index 00000000..d2de9d52 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/MemberCouponService.java @@ -0,0 +1,105 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import cn.lili.modules.promotion.entity.enums.MemberCouponStatusEnum; +import cn.lili.modules.promotion.entity.vos.CouponSearchParams; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 会员优惠券业务层 + * + * @author Chopper + * @date 2020/11/18 9:45 上午 + */ +public interface MemberCouponService extends IService { + + /** + * 检查该会员领取优惠券的可领取数量 + * + * @param couponId 优惠券编号 + * @param memberId 会员 + */ + void checkCouponLimit(String couponId, String memberId); + + + /** + * 领取优惠券 + * + * @param couponId 优惠券编号 + * @param memberId 会员 + * @param memberName 会员名称 + */ + void receiveCoupon(String couponId, String memberId, String memberName); + + /** + * 获取会员优惠券列表 + * + * @param param 查询参数 + * @param pageVo 分页参数 + * @return 会员优惠券列表 + */ + IPage getMemberCoupons(CouponSearchParams param, PageVO pageVo); + + /** + * 获取会员优惠券列表 + * + * @param param 查询参数 + * @param pageVo 分页参数 + * @param totalPrice 当前商品总价 + * @return 会员优惠券列表 + */ + IPage getMemberCouponsByCanUse(CouponSearchParams param, Double totalPrice, PageVO pageVo); + + /** + * 获取当前会员当前商品可用的会员优惠券 + * + * @param memberId 会员Id + * @param couponIds 优惠券id列表 + * @param totalPrice 当前商品总价 + * @return 会员优惠券列表 + */ + List getCurrentGoodsCanUse(String memberId, List couponIds, Double totalPrice); + + /** + * 获取当前会员全品类优惠券 + * + * @param memberId 会员Id + * @param storeId 店铺id + * @return 会员优惠券列表 + */ + List getAllScopeMemberCoupon(String memberId, List storeId); + + /** + * 获取会员优惠券数量 + * + * @return 会员优惠券数量 + */ + Integer getMemberCouponsNum(); + + /** + * 更新会员优惠券状态 + * + * @param status 要变更的状态 + * @param id 会员优惠券id + */ + void updateMemberCouponStatus(MemberCouponStatusEnum status, String id); + + /** + * 使用优惠券 + * + * @param ids 会员优惠券id + */ + void used(List ids); + + /** + * 作废当前会员优惠券 + * + * @param id id + */ + void cancellation(String id); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/PintuanService.java b/framework/src/main/java/cn/lili/modules/promotion/service/PintuanService.java new file mode 100644 index 00000000..e3162511 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/PintuanService.java @@ -0,0 +1,125 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.Pintuan; +import cn.lili.modules.promotion.entity.vos.PintuanMemberVO; +import cn.lili.modules.promotion.entity.vos.PintuanSearchParams; +import cn.lili.modules.promotion.entity.vos.PintuanShareVO; +import cn.lili.modules.promotion.entity.vos.PintuanVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Date; +import java.util.List; + +/** + * 拼图活动业务层 + * + * @author Chopper + * @date 2020/11/18 9:45 上午 + */ +public interface PintuanService extends IService { + + /** + * 根据条件分页查询拼团活动列表 + * + * @param param 拼团活动查询参数 + * @param page 分页参数 + * @return 拼团活动列表 + */ + IPage getPintuanByPage(PintuanSearchParams param, PageVO page); + + /** + * 获取当前拼团的会员 + * + * @param pintuanId 拼图id + * @return 当前拼团的会员列表 + */ + List getPintuanMember(String pintuanId); + + /** + * 从mongo中根据条件分页查询拼团活动列表 + * + * @param param 拼团活动查询参数 + * @param page 分页参数 + * @return 拼团活动列表 + */ + IPage getPintuanByPageFromMongo(PintuanSearchParams param, PageVO page); + + /** + * 从mongo中查询拼团活动详情 + * + * @param id 拼团ID + * @return 拼团活动详情 + */ + PintuanVO getPintuanByIdFromMongo(String id); + + /** + * 从mysql中查询拼团活动详情 + * + * @param id 拼团活动id + * @return 拼团活动详情 + */ + Pintuan getPintuanById(String id); + + /** + * 从mongo中根据条件查询拼团活动总数 + * + * @param param 拼团活动查询参数 + * @return 总数 + */ + Long getPintuanByPageFromMongoCount(PintuanSearchParams param); + + /** + * 拼团新增业务处理 + * + * @param pintuan 拼团实体 + * @return 是否成功 + */ + boolean addPintuan(PintuanVO pintuan); + + /** + * 拼团修改 + * + * @param pintuan 拼团实体 + * @return 是否成功 + */ + boolean modifyPintuan(PintuanVO pintuan); + + /** + * 开启拼团 + * + * @param pintuanId 拼团活动编号 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 是否成功 + */ + boolean openPintuan(String pintuanId, Date startTime, Date endTime); + + /** + * 关闭拼团 + * + * @param pintuanId 拼团活动编号 + * @return 是否成功 + */ + boolean closePintuan(String pintuanId); + + /** + * 删除拼团 + * + * @param pintuanId 拼团活动编号 + * @return 是否成功 + */ + boolean deletePintuan(String pintuanId); + + /** + * 获取拼团分享信息 + * + * @param parentOrderSn 拼团团长订单sn + * @param skuId 商品skuId + * @return 拼团分享信息 + */ + PintuanShareVO getPintuanShareInfo(String parentOrderSn, String skuId); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/PointsGoodsCategoryService.java b/framework/src/main/java/cn/lili/modules/promotion/service/PointsGoodsCategoryService.java new file mode 100644 index 00000000..7f0d7a89 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/PointsGoodsCategoryService.java @@ -0,0 +1,57 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.PointsGoodsCategory; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 积分商品分类业务层 + * + * @author paulG + * @date 2020/11/18 9:45 上午 + **/ +public interface PointsGoodsCategoryService extends IService { + + /** + * 添加积分商品分类 + * + * @param pointsGoodsCategory 积分商品分类信息 + * @return 是否添加成功 + */ + boolean addCategory(PointsGoodsCategory pointsGoodsCategory); + + /** + * 更新积分商品分类 + * + * @param pointsGoodsCategory 积分商品分类信息 + * @return 是否更新成功 + */ + boolean updateCategory(PointsGoodsCategory pointsGoodsCategory); + + /** + * 删除积分商品类型 + * + * @param id 积分商品分类id + * @return 是否删除成功 + */ + boolean deleteCategory(String id); + + /** + * 分页获取积分商品类型 + * + * @param name 类型名称 + * @param page 分页参数 + * @return 积分商品类型分页数据 + */ + IPage getCategoryByPage(String name, PageVO page); + + /** + * 获取积分商品类型详情 + * + * @param id 积分商品类型id + * @return 积分商品类型详情 + */ + PointsGoodsCategory getCategoryDetail(String id); + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/PointsGoodsService.java b/framework/src/main/java/cn/lili/modules/promotion/service/PointsGoodsService.java new file mode 100644 index 00000000..122d6142 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/PointsGoodsService.java @@ -0,0 +1,78 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.PointsGoods; +import cn.lili.modules.promotion.entity.vos.PointsGoodsSearchParams; +import cn.lili.modules.promotion.entity.vos.PointsGoodsVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 积分商品业务层 + * + * @author paulG + * @date 2020/11/18 9:45 上午 + **/ +public interface PointsGoodsService extends IService { + + /** + * 批量添加积分商品 + * + * @param pointsGoodsList 积分商品列表 + * @return 是否添加成功 + */ + boolean addPointsGoods(List pointsGoodsList); + + /** + * 更新一个积分商品 + * + * @param pointsGoodsDTO 编辑的积分商品信息 + * @return 是否更新成功 + */ + boolean updatePointsGoods(PointsGoodsVO pointsGoodsDTO); + + /** + * 批量更新积分商品状态 + * + * @param ids 积分商品id集合 + * @param promotionStatus 更新的状态 + * @return 是否更新成功 + */ + boolean updatePointsGoodsPromotionStatus(List ids, String promotionStatus); + + /** + * 批量删除积分商品 + * + * @param ids 积分商品id集合 + * @return 是否删除成功 + */ + boolean deletePointsGoods(List ids); + + /** + * 根据ID获取积分详情 + * + * @param id 积分商品id + * @return 积分详情 + */ + PointsGoodsVO getPointsGoodsDetail(String id); + + /** + * 根据SkuID获取积分商品信息 + * + * @param skuId 商品skuId + * @return 积分详情 + */ + PointsGoods getPointsGoodsDetailBySkuId(String skuId); + + /** + * 根据条件查询积分商品 + * + * @param searchParams 积分商品查询参数 + * @param page 分页参数 + * @return 积分商品查询结果 + */ + IPage getPointsGoodsByPage(PointsGoodsSearchParams searchParams, PageVO page); + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/PromotionGoodsService.java b/framework/src/main/java/cn/lili/modules/promotion/service/PromotionGoodsService.java new file mode 100644 index 00000000..e4be4e76 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/PromotionGoodsService.java @@ -0,0 +1,155 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.dto.PromotionGoodsDTO; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.PromotionGoodsSearchParams; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Date; +import java.util.List; + +/** + * 促销商品业务层 + * + * @author Chopper + * @date 2020/11/18 9:45 上午 + */ +public interface PromotionGoodsService extends IService { + + + /** + * 缓存商品库存key + * + * @param typeEnum 促销分类枚举 + * @param promotionId 促销活动Id + * @param skuId skuId + * @return 缓存商品库存key + */ + static String getPromotionGoodsStockCacheKey(PromotionTypeEnum typeEnum, String promotionId, String skuId) { + return "{" + CachePrefix.PROMOTION_GOODS_STOCK.name() + "_" + typeEnum.name() + "}_" + promotionId + "_" + skuId; + } + + /** + * 根据活动获取商品 + * + * @param promotionId 促销活动id + * @param skuId 商品id + * @return 促销商品信息 + */ + PromotionGoods findByPromotion(String promotionId, String skuId); + + /** + * 删除指定促销类型的促销商品 + * + * @param promotionGoodsList 促销商品列表 + * @param promotionType 促销类型 + */ + void removePromotionGoods(List promotionGoodsList, PromotionTypeEnum promotionType); + + /** + * 更新促销活动 + * + * @param cartSkuVO 购物车中的产品 + */ + void updatePromotion(CartSkuVO cartSkuVO); + + /** + * 获取购物车商品的促销活动 + * + * @param cartSkuVO 购物车中的产品 + */ + void getCartSkuPromotion(CartSkuVO cartSkuVO); + + /** + * 获取某sku当日所有活动 + * + * @param skuId 商品skuId + * @return 促销商品集合 + */ + List findNowSkuPromotion(String skuId); + + /** + * 分页获取促销商品信息 + * + * @param goodsId 商品skuId + * @return 某商品的促销信息 + */ + List getPromotionGoods(String goodsId); + + /** + * 分页获取促销商品信息 + * + * @param searchParams 查询参数 + * @param pageVo 分页参数 + * @return 促销商品列表 + */ + IPage getPromotionGoods(PromotionGoodsSearchParams searchParams, PageVO pageVo); + + /** + * 分页获取当前进行中的促销活动的促销商品信息 + * + * @param promotionType 促销活动类型 + * @param pageVo 分页参数 + * @return 促销商品列表 + */ + IPage getCurrentPromotionGoods(String promotionType, PageVO pageVo); + + /** + * 查询参加活动促销商品是否同时参加指定类型的活动 + * + * @param promotionType 促销类型 + * @param skuId skuId + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 共参加了几种活动 + */ + Integer findInnerOverlapPromotionGoods(String promotionType, String skuId, Date startTime, Date endTime); + + + /** + * 获取促销活动商品库存 + * + * @param typeEnum 促销商品类型 + * @param promotionId 促销活动id + * @param skuId 商品skuId + * @return 促销活动商品库存 + */ + Integer getPromotionGoodsStock(PromotionTypeEnum typeEnum, String promotionId, String skuId); + + /** + * 根据条件获取促销活动商品详情 + * + * @param typeEnum 促销类型 + * @param promotionId 促销活动id + * @param skuId 商品skuId + * @return 促销活动商品详情 + */ + PromotionGoods getPromotionGoods(PromotionTypeEnum typeEnum, String promotionId, String skuId); + + /** + * 更新促销活动商品库存 + * + * @param typeEnum 促销商品类型 + * @param promotionId 促销活动id + * @param skuId 商品skuId + * @param quantity 更新后的库存数量 + */ + void updatePromotionGoodsStock(PromotionTypeEnum typeEnum, String promotionId, String skuId, Integer quantity); + + + /** + * 分页获取根据条件获取促销商品 + * + * @param goodsName 商品名称 + * @param categoryPath 商品分类 + * @param promotionType 促销类型 + * @param pageVo 分页参数 + * @return 促销商品信息 + */ + IPage getPromotionGoodsPage(String goodsName, String categoryPath, String promotionType, PageVO pageVo); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/PromotionPriceService.java b/framework/src/main/java/cn/lili/modules/promotion/service/PromotionPriceService.java new file mode 100644 index 00000000..0b934850 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/PromotionPriceService.java @@ -0,0 +1,26 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import cn.lili.modules.promotion.entity.dto.PromotionPriceDTO; +import cn.lili.modules.promotion.entity.dto.PromotionPriceParamDTO; + +import java.util.List; + +/** + * 促销计算 + * + * @author paulG + * @date 2020/8/21 + **/ +public interface PromotionPriceService { + + /** + * 计算商品当前所参与的促销活动的价格 + * + * @param tradeSkuList 促销计算参数 + * @param memberCouponList 使用的优惠券 + * @return 促销计算结果 + */ + PromotionPriceDTO calculationPromotionPrice(List tradeSkuList, List memberCouponList); + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/PromotionService.java b/framework/src/main/java/cn/lili/modules/promotion/service/PromotionService.java new file mode 100644 index 00000000..6b825e40 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/PromotionService.java @@ -0,0 +1,41 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.delayqueue.PromotionMessage; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; + +import java.util.Map; + +/** + * 促销业务层 + * + * @author Chopper + * @date 2020/11/18 9:45 上午 + */ +public interface PromotionService { + + + /** + * 更新促销活动状态 + * + * @param promotionMessage 促销变更信息 + * @return 是否更新成功 + */ + boolean updatePromotionStatus(PromotionMessage promotionMessage); + + + /** + * 获取当前进行的所有促销活动信息 + * + * @return 当前促销活动集合 + */ + Map getCurrentPromotion(); + + /** + * 根据商品索引获取当前商品索引的所有促销活动信息 + * + * @param index 商品索引 + * @return 当前促销活动集合 + */ + Map getGoodsCurrentPromotionMap(EsGoodsIndex index); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/SeckillApplyService.java b/framework/src/main/java/cn/lili/modules/promotion/service/SeckillApplyService.java new file mode 100644 index 00000000..aeff8ee7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/SeckillApplyService.java @@ -0,0 +1,103 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.SeckillApply; +import cn.lili.modules.promotion.entity.vos.SeckillApplyVO; +import cn.lili.modules.promotion.entity.vos.SeckillGoodsVO; +import cn.lili.modules.promotion.entity.vos.SeckillSearchParams; +import cn.lili.modules.promotion.entity.vos.SeckillTimelineVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; +import java.util.Map; + +/** + * 秒杀申请业务层 + * + * @author Chopper + * @date 2020/11/18 9:45 上午 + */ +public interface SeckillApplyService extends IService { + + + /** + * 获取当天限时抢购信息列表(时刻及对应时刻下的商品) + * + * @return 限时抢购信息列表 + */ + List getSeckillTimeline(); + + + /** + * 获取当天某个时刻的限时抢购商品列表 + * + * @param timeline 指定时刻 + * @return 限时抢购商品列表 + */ + List getSeckillGoods(Integer timeline); + + /** + * 审核一批申请 + * + * @param ids 限时抢购申请编号 + * @param seckillId 限时抢购编号 + * @param applyStatus 审批状态 + * @param failReason 驳回原因 + */ + void auditBatchApply(String[] ids, String seckillId, String applyStatus, String failReason); + + /** + * 分页查询限时请购申请列表 + * + * @param queryParam 限时抢购申请查询参数 + * @param pageVo 分页参数 + * @return 限时请购申请列表 + */ + IPage getSeckillApplyFromMysql(SeckillSearchParams queryParam, PageVO pageVo); + + /** + * 从mongo中分页查询限时请购申请列表 + * + * @param queryParam 限时抢购申请查询参数 + * @param pageVo 分页参数 + * @return 限时请购申请列表 + */ + IPage getSeckillApplyFromMongo(SeckillSearchParams queryParam, PageVO pageVo); + + /** + * 添加限时抢购申请 + * + * @param seckillId 限时抢购编号 + * @param storeId 商家id + * @param seckillApplyList 限时抢购申请列表 + */ + void addSeckillApply(String seckillId, String storeId, List seckillApplyList); + + /** + * 批量删除限时抢购申请 + * + * @param seckillId 限时抢购活动id + * @param ids 限时抢购申请id集合 + */ + void removeSeckillApplyByIds(String seckillId, List ids); + + /** + * 更新限时抢购库存数量 + * + * @param id 限时抢购申请(限时抢购商品)id + * @param num 数量 + * @return 是否成功 + */ + boolean updateSeckillStock(String id, Integer num); + + + /** + * 更新限时抢购库存数量 + * + * @param map key 为 限时抢购申请(限时抢购商品)id, value 为数量 + * @return 是否成功 + */ + boolean updateSeckillStock(Map map); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/service/SeckillService.java b/framework/src/main/java/cn/lili/modules/promotion/service/SeckillService.java new file mode 100644 index 00000000..bd8cfc90 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/service/SeckillService.java @@ -0,0 +1,95 @@ +package cn.lili.modules.promotion.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.Seckill; +import cn.lili.modules.promotion.entity.vos.SeckillSearchParams; +import cn.lili.modules.promotion.entity.vos.SeckillVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 秒杀业务层 + * + * @author Chopper + * @date 2020/11/18 9:45 上午 + */ +public interface SeckillService extends IService { + + + /** + * 从mysql中根据条件获取限时抢购分页列表 + * + * @param queryParam 查询参数 + * @param pageVo 分页参数 + * @return 限时抢购分页列表 + */ + IPage getSeckillByPageFromMysql(SeckillSearchParams queryParam, PageVO pageVo); + + /** + * 从mongo中根据条件获取限时抢购分页列表 + * + * @param queryParam 查询参数 + * @param pageVo 分页参数 + * @return 限时抢购分页列表 + */ + IPage getSeckillByPageFromMongo(SeckillSearchParams queryParam, PageVO pageVo); + + /** + * 从mongo中获取限时抢购信息 + * + * @param id 限时抢购id + * @return 限时抢购信息 + */ + SeckillVO getSeckillByIdFromMongo(String id); + + /** + * 保存限时抢购 + * + * @param seckill 限时抢购信息 + * @return 是否保存成功 + */ + boolean saveSeckill(SeckillVO seckill); + + /** + * 商家报名限时抢购活动 + * + * @param storeId 商家编号 + * @param seckillId 限时抢购编号 + */ + void storeApply(String storeId, String seckillId); + + /** + * 修改限时抢购 + * + * @param seckillVO 限时抢购信息 + * @return 是否修改成功 + */ + boolean modifySeckill(SeckillVO seckillVO); + + /** + * 删除限时抢购 + * + * @param id 限时抢购编号 + */ + void deleteSeckill(String id); + + /** + * 开启一个限时抢购 + * + * @param id 限时抢购编号 + */ + void openSeckill(String id); + + /** + * 关闭一个限时抢购 + * + * @param id 限时抢购编号 + */ + void closeSeckill(String id); + + /** + * 获取当前可参与的活动数量 + * @return 可参与活动数量 + */ + Integer getApplyNum(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/CouponServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/CouponServiceImpl.java new file mode 100644 index 00000000..b2f67ac3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/CouponServiceImpl.java @@ -0,0 +1,396 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.delayqueue.DelayQueueTools; +import cn.lili.common.delayqueue.DelayQueueType; +import cn.lili.common.delayqueue.PromotionMessage; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.trigger.interfaces.TimeTrigger; +import cn.lili.common.trigger.model.TimeExecuteConstant; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.promotion.entity.dos.Coupon; +import cn.lili.modules.promotion.entity.dos.FullDiscount; +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.enums.*; +import cn.lili.modules.promotion.entity.vos.CouponSearchParams; +import cn.lili.modules.promotion.entity.vos.CouponVO; +import cn.lili.modules.promotion.mapper.CouponMapper; +import cn.lili.modules.promotion.service.CouponService; +import cn.lili.modules.promotion.service.FullDiscountService; +import cn.lili.modules.promotion.service.MemberCouponService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import cn.lili.modules.promotion.tools.PromotionTools; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 优惠券活动业务层实现 + * + * @author Chopper + * @date 2020/8/21 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CouponServiceImpl extends ServiceImpl implements CouponService { + + //延时任务 + private final TimeTrigger timeTrigger; + //Mongo + private final MongoTemplate mongoTemplate; + //规格商品 + private GoodsSkuService goodsSkuService; + //Rocketmq + private RocketmqCustomProperties rocketmqCustomProperties; + //促销商品 + private PromotionGoodsService promotionGoodsService; + //会员优惠券 + private MemberCouponService memberCouponService; + //满额活动 + private FullDiscountService fullDiscountService; + + @Override + public CouponVO add(CouponVO coupon) { + // 检查参数 + this.checkParam(coupon); + coupon.setUsedNum(0); + coupon.setReceivedNum(0); + // 保存到MYSQL中 + this.save(coupon); + // 如果优惠券类型为部分商品则将促销活动商品更新 + this.updateScopePromotionGoods(coupon); + // 保存到MONGO中 + this.mongoTemplate.save(coupon); + PromotionMessage promotionMessage = new PromotionMessage(coupon.getId(), PromotionTypeEnum.COUPON.name(), PromotionStatusEnum.START.name(), coupon.getStartTime(), coupon.getEndTime()); + TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.PROMOTION_EXECUTOR, + coupon.getStartTime().getTime(), + promotionMessage, + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + rocketmqCustomProperties.getPromotionTopic()); + // 发送促销活动开始的延时任务 + this.timeTrigger.addDelay(timeTriggerMsg, DateUtil.getDelayTime(coupon.getStartTime().getTime())); + return coupon; + } + + @Override + public CouponVO updateCoupon(CouponVO couponVO) { + CouponVO coupon = checkStatus(couponVO.getId()); + // 检查参数 + this.checkParam(couponVO); + // 更新到MYSQL中 + this.updateById(couponVO); + // 如果优惠券类型为部分商品则将促销活动商品更新 + this.updateScopePromotionGoods(couponVO); + // 保存到MONGO中 + this.mongoTemplate.save(couponVO); + PromotionMessage promotionMessage = new PromotionMessage(couponVO.getId(), PromotionTypeEnum.COUPON.name(), PromotionStatusEnum.START.name(), coupon.getStartTime(), coupon.getEndTime()); + // 更新延时任务 + this.timeTrigger.edit(TimeExecuteConstant.PROMOTION_EXECUTOR, + promotionMessage, + coupon.getStartTime().getTime(), couponVO.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + DateUtil.getDelayTime(coupon.getStartTime().getTime()), + rocketmqCustomProperties.getPromotionTopic()); + return couponVO; + } + + @Override + public boolean updateCouponStatus(List couponId, PromotionStatusEnum promotionStatus) { + Query query = new Query(); + query.addCriteria(Criteria.where("id").in(couponId)); + List couponVOS = this.mongoTemplate.find(query, CouponVO.class); + couponVOS = couponVOS.parallelStream().filter(i -> Boolean.FALSE.equals(i.getDeleteFlag())).collect(Collectors.toList()); + if (couponVOS.isEmpty()) { + throw new ServiceException("优惠券不存在"); + } + for (CouponVO couponVO : couponVOS) { + if (promotionStatus.name().equals(PromotionStatusEnum.START.name())) { + this.checkParam(couponVO); + } + couponVO.setPromotionStatus(promotionStatus.name()); + this.updateById(couponVO); + this.mongoTemplate.save(couponVO); + if (promotionStatus.name().equals(PromotionStatusEnum.START.name())) { + PromotionMessage promotionMessage = new PromotionMessage(couponVO.getId(), PromotionTypeEnum.COUPON.name(), PromotionStatusEnum.START.name(), couponVO.getStartTime(), couponVO.getEndTime()); + // 更新延时任务 + this.timeTrigger.edit(TimeExecuteConstant.PROMOTION_EXECUTOR, + promotionMessage, + couponVO.getStartTime().getTime(), couponVO.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + DateUtil.getDelayTime(couponVO.getStartTime().getTime()), + rocketmqCustomProperties.getPromotionTopic()); + } + } + return true; + } + + @Override + public boolean deleteCoupon(String id) { + CouponVO couponVO = checkStatus(id); + LambdaUpdateWrapper couponUpdateWrapper = new LambdaUpdateWrapper().eq(Coupon::getId, id).set(Coupon::getPromotionStatus, PromotionStatusEnum.CLOSE.name()).set(Coupon::getDeleteFlag, true); + // 更新优惠券状态为关闭,标示删除标志 + boolean result = this.update(couponUpdateWrapper); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper().eq(PromotionGoods::getPromotionId, id).set(PromotionGoods::getPromotionStatus, PromotionStatusEnum.CLOSE.name()).set(PromotionGoods::getDeleteFlag, true); + // 更新促销商品记录信息为删除 + this.promotionGoodsService.update(updateWrapper); + LambdaUpdateWrapper memberCouponLambdaUpdateWrapper = new LambdaUpdateWrapper().eq(MemberCoupon::getCouponId, id).set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.CLOSED.name()); + memberCouponService.update(memberCouponLambdaUpdateWrapper); + this.mongoTemplate.remove(new Query().addCriteria(Criteria.where("id").is(id)), CouponVO.class); + this.timeTrigger.delete(TimeExecuteConstant.PROMOTION_EXECUTOR, + couponVO.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (PromotionTypeEnum.COUPON.name() + couponVO.getId())), + rocketmqCustomProperties.getPromotionTopic()); + return result; + } + + @Override + public IPage getCouponsByPageFromMongo(CouponSearchParams param, PageVO page) { + Query query = param.mongoQuery(); + IPage coupons = new Page<>(); + if (page != null) { + page.setNotConvert(true); + PromotionTools.mongoQueryPageParam(query, page); + coupons.setSize(page.getPageSize()); + coupons.setCurrent(page.getPageNumber()); + } + List couponVOList = mongoTemplate.find(query, CouponVO.class); + coupons.setRecords(couponVOList); + coupons.setTotal(mongoTemplate.count(query, CouponVO.class)); + return coupons; + } + + /** + * 根据查询条件从mongo中获取优惠券信息列表 + * + * @param param 查询参数 + * @param page 分页参数 + * @return 优惠券信息列表 + */ + @Override + public IPage getCanReceiveCoupons(CouponSearchParams param, PageVO page) { + Query query = param.mongoQuery(); + query.addCriteria(Criteria.where("promotionStatus").is(PromotionStatusEnum.START.name())); + query.addCriteria(Criteria.where("endTime").gte(new Date())); + IPage coupons = new Page<>(); + if (page != null) { + page.setNotConvert(true); + PromotionTools.mongoQueryPageParam(query, page); + coupons.setSize(page.getPageSize()); + coupons.setCurrent(page.getPageNumber()); + } + List couponVOList = mongoTemplate.find(query, CouponVO.class); + coupons.setRecords(couponVOList); + coupons.setTotal(mongoTemplate.count(query, CouponVO.class)); + return coupons; + } + + @Override + public CouponVO getCouponDetailFromMongo(String id) { + return mongoTemplate.findById(id, CouponVO.class); + } + + @Override + public IPage getCouponsByPage(CouponSearchParams param, PageVO page) { + QueryWrapper queryWrapper = param.wrapper(); + return page(PageUtil.initPage(page), queryWrapper); + } + + /** + * 领取优惠券 + * + * @param couponId 优惠券id + * @param receiveNum 领取数量 + */ + @Override + public void receiveCoupon(String couponId, Integer receiveNum) { + CouponVO couponVO = checkStatus(couponId); + couponVO.setReceivedNum(couponVO.getReceivedNum() + receiveNum); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Coupon::getId, couponId); + updateWrapper.set(Coupon::getReceivedNum, couponVO.getReceivedNum()); + this.update(updateWrapper); + this.mongoTemplate.save(couponVO); + } + + /** + * 使用优惠券 + * + * @param couponId 优惠券id + * @param usedNum 使用数量 + */ + @Override + public void usedCoupon(String couponId, Integer usedNum) { + CouponVO couponVO = checkStatus(couponId); + couponVO.setUsedNum(couponVO.getUsedNum() + usedNum); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Coupon::getId, couponId); + updateWrapper.set(Coupon::getUsedNum, couponVO.getUsedNum()); + this.update(updateWrapper); + this.mongoTemplate.save(couponVO); + } + + /** + * 检查优惠券信息是否合法 + * + * @param coupon 优惠券信息 + */ + private void checkParam(CouponVO coupon) { + + if (coupon.getCouponLimitNum() < 0) { + throw new ServiceException("领取限制数量不能为负数"); + } + + if (coupon.getCouponLimitNum() > coupon.getPublishNum()) { + throw new ServiceException("领取限制数量超出发行数量"); + } + + if (coupon.getCouponType().equals(CouponTypeEnum.PRICE.name()) && coupon.getPrice() > coupon.getConsumeThreshold()) { + throw new ServiceException("优惠券面额必须小于优惠券消费限额"); + } else if (coupon.getCouponType().equals(CouponTypeEnum.DISCOUNT.name()) && (coupon.getCouponDiscount() < 0 && coupon.getCouponDiscount() > 10)) { + throw new ServiceException("优惠券折扣必须小于10且大于0"); + } + + long nowTime = DateUtil.getDateline() * 1000; + if (coupon.getStartTime().getTime() < nowTime && coupon.getEndTime().getTime() > nowTime) { + throw new ServiceException("活动时间小于当前时间,不能进行编辑删除操作"); + } + + PromotionTools.checkPromotionTime(coupon.getStartTime().getTime(), coupon.getEndTime().getTime()); + + this.checkCouponScope(coupon); + //对状态的处理.如果未传递状态则需要 根据当前时间来确认优惠券状态 + this.promotionStatusEmpty(coupon); + } + + /** + * 检查优惠券范围 + * + * @param coupon 检查的优惠券对象 + */ + private void checkCouponScope(CouponVO coupon) { + if (coupon.getScopeType().equals(CouponScopeTypeEnum.PORTION_GOODS.name()) && (coupon.getPromotionGoodsList() == null || coupon.getPromotionGoodsList().isEmpty())) { + throw new ServiceException("当前关联范围类型为指定商品时,商品列表不能为空"); + } else if (coupon.getScopeType().equals(CouponScopeTypeEnum.PORTION_GOODS.name()) && CharSequenceUtil.isEmpty(coupon.getScopeId())) { + throw new ServiceException("当前关联范围类型为指定商品时,范围关联的id不能为空"); + } else if (coupon.getScopeType().equals(CouponScopeTypeEnum.PORTION_GOODS_CATEGORY.name()) && CharSequenceUtil.isEmpty(coupon.getScopeId())) { + throw new ServiceException("当前关联范围类型为部分商品分类时,范围关联的id不能为空"); + } else if (coupon.getScopeType().equals(CouponScopeTypeEnum.PORTION_SHOP_CATEGORY.name()) && CharSequenceUtil.isEmpty(coupon.getScopeId())) { + throw new ServiceException("当前关联范围类型为部分店铺分类时,范围关联的id不能为空"); + } + + if (coupon.getScopeType().equals(CouponScopeTypeEnum.PORTION_GOODS.name())) { + String[] split = coupon.getScopeId().split(","); + if (split.length <= 0) { + throw new ServiceException("指定商品范围关联id无效!"); + } + for (String id : split) { + GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(id); + if (goodsSku == null) { + throw new ServiceException("商品已下架"); + } + } + } + } + + /** + * 对状态的处理.如果未传递状态则需要 根据当前时间来确认优惠券状态 + * + * @param coupon 优惠券参数 + */ + private void promotionStatusEmpty(CouponVO coupon) { + if (StringUtils.isEmpty(coupon.getPromotionStatus())) { + //格式时间 + long startTme = coupon.getStartTime().getTime() / 1000; + long endTime = coupon.getEndTime().getTime() / 1000; + //校验时间确定当前优惠券有效期 + long currentTime = DateUtil.getDateline(); + //如果未到时间点则为新建 + if (startTme > currentTime) { + coupon.setPromotionStatus(PromotionStatusEnum.NEW.name()); + } + //如果超过结束时间则为结束 + if (endTime < currentTime) { + coupon.setPromotionStatus(PromotionStatusEnum.END.name()); + } + //如果在使用时间内 则是开始状态 + if (startTme <= currentTime && endTime > currentTime) { + coupon.setPromotionStatus(PromotionStatusEnum.START.name()); + } + } + } + + /** + * 检查优惠券状态是否可进行编辑删除 + * + * @param id 优惠券id + * @return 优惠券信息 + */ + private CouponVO checkStatus(String id) { + CouponVO coupon = this.mongoTemplate.findById(id, CouponVO.class); + if (coupon == null) { + throw new ServiceException("当前优惠券活动不存在"); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper().eq(FullDiscount::getIsCoupon, true).eq(FullDiscount::getCouponId, id); + FullDiscount fullDiscount = fullDiscountService.getOne(queryWrapper); + if (fullDiscount != null) { + throw new ServiceException("当前优惠券参与了促销活动 " + fullDiscount.getTitle() + " 不能进行编辑删除操作"); + } + return coupon; + } + + private void updateScopePromotionGoods(CouponVO couponVO) { + // 如果优惠券类型为部分商品则将促销活动更新至ES中 + if (CouponScopeTypeEnum.PORTION_GOODS.name().equals(couponVO.getScopeType()) && !couponVO.getPromotionGoodsList().isEmpty()) { + PromotionTools.promotionGoodsInit(couponVO.getPromotionGoodsList(), couponVO, PromotionTypeEnum.COUPON); + } + } + + @Autowired + public void setGoodsSkuService(GoodsSkuService goodsSkuService) { + this.goodsSkuService = goodsSkuService; + } + + @Autowired + public void setRocketmqCustomProperties(RocketmqCustomProperties rocketmqCustomProperties) { + this.rocketmqCustomProperties = rocketmqCustomProperties; + } + + @Autowired + public void setPromotionGoodsService(PromotionGoodsService promotionGoodsService) { + this.promotionGoodsService = promotionGoodsService; + } + + @Autowired + public void setMemberCouponService(MemberCouponService memberCouponService) { + this.memberCouponService = memberCouponService; + } + + @Autowired + public void setFullDiscountService(FullDiscountService fullDiscountService) { + this.fullDiscountService = fullDiscountService; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/FullDiscountServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/FullDiscountServiceImpl.java new file mode 100644 index 00000000..ad59314b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/FullDiscountServiceImpl.java @@ -0,0 +1,330 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.lili.common.delayqueue.DelayQueueTools; +import cn.lili.common.delayqueue.DelayQueueType; +import cn.lili.common.delayqueue.PromotionMessage; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.trigger.interfaces.TimeTrigger; +import cn.lili.common.trigger.model.TimeExecuteConstant; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import cn.lili.modules.promotion.entity.dos.Coupon; +import cn.lili.modules.promotion.entity.dos.FullDiscount; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.FullDiscountSearchParams; +import cn.lili.modules.promotion.mapper.FullDiscountMapper; +import cn.lili.modules.promotion.service.CouponService; +import cn.lili.modules.promotion.service.FullDiscountService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import cn.lili.modules.promotion.tools.PromotionTools; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; + +/** + * 满优惠业务层实现 + * + * @author Chopper + * @date 2020/8/21 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FullDiscountServiceImpl extends ServiceImpl implements FullDiscountService { + + private static final String SELLER_ID_COLUMN = "storeId"; + private static final String PROMOTION_STATUS_COLUMN = "promotionStatus"; + //延时任务 + private final TimeTrigger timeTrigger; + //Mongo + private final MongoTemplate mongoTemplate; + //满额活动 + private final FullDiscountMapper fullDiscountMapper; + //Rocketmq + private final RocketmqCustomProperties rocketmqCustomProperties; + //优惠券 + private CouponService couponService; + //促销商品 + private PromotionGoodsService promotionGoodsService; + + @Override + public FullDiscountVO currentPromotion(String storeId) { + Query query = this.getMongoQuery(); + query.addCriteria(Criteria.where(SELLER_ID_COLUMN).is(storeId)); + return mongoTemplate.findOne(query, FullDiscountVO.class); + } + + @Override + public List currentPromotion(List storeId) { + Query query = this.getMongoQuery(); + query.addCriteria(Criteria.where(SELLER_ID_COLUMN).in(storeId)); + return mongoTemplate.find(query, FullDiscountVO.class); + } + + @Override + public FullDiscount addFullDiscount(FullDiscountVO fullDiscountVO) { + // 验证是否是有效参数 + PromotionTools.paramValid(fullDiscountVO.getStartTime().getTime(), fullDiscountVO.getEndTime().getTime(), fullDiscountVO.getNumber(), fullDiscountVO.getPromotionGoodsList()); + // 当前时间段是否存在同类活动 + this.checkSameActiveExist(fullDiscountVO.getStartTime(), fullDiscountVO.getEndTime(), fullDiscountVO.getStoreId(), null); + // 检查满减参数 + this.checkFullDiscount(fullDiscountVO); + // 保存到MYSQL中 + this.save(fullDiscountVO); + if (fullDiscountVO.getPromotionGoodsList() != null) { + List promotionGoodsList = PromotionTools.promotionGoodsInit(fullDiscountVO.getPromotionGoodsList(), fullDiscountVO, PromotionTypeEnum.FULL_DISCOUNT); + // 促销活动商品更新 + this.promotionGoodsService.saveOrUpdateBatch(promotionGoodsList); + } + // 保存到MONGO中 + this.mongoTemplate.save(fullDiscountVO); + PromotionMessage promotionMessage = new PromotionMessage(fullDiscountVO.getId(), PromotionTypeEnum.FULL_DISCOUNT.name(), PromotionStatusEnum.START.name(), fullDiscountVO.getStartTime(), fullDiscountVO.getEndTime()); + TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.PROMOTION_EXECUTOR, + fullDiscountVO.getStartTime().getTime(), promotionMessage, + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + rocketmqCustomProperties.getPromotionTopic()); + // 发送促销活动开始的延时任务 + this.timeTrigger.addDelay(timeTriggerMsg, DateUtil.getDelayTime(fullDiscountVO.getStartTime().getTime())); + return fullDiscountVO; + } + + public IPage getFullDiscountByPageFromMysql(FullDiscountSearchParams searchParams, PageVO page) { + QueryWrapper queryWrapper = searchParams.wrapper(); + return this.page(PageUtil.initPage(page), queryWrapper); + } + + @Override + public IPage getFullDiscountByPageFromMongo(FullDiscountSearchParams searchParams, PageVO page) { + IPage fullDiscountIPage = new Page<>(); + Query query = searchParams.mongoQuery(); + if (page != null) { + PromotionTools.mongoQueryPageParam(query, page); + fullDiscountIPage.setCurrent(page.getPageNumber()); + fullDiscountIPage.setSize(page.getPageSize()); + } + List fullDiscountVOS = this.mongoTemplate.find(query, FullDiscountVO.class); + fullDiscountIPage.setRecords(fullDiscountVOS); + fullDiscountIPage.setTotal(this.mongoTemplate.count(query, FullDiscountVO.class)); + return fullDiscountIPage; + } + + @Override + public FullDiscountVO modifyFullDiscount(FullDiscountVO fullDiscountVO) { + // 检查满优惠活动是否存在 + FullDiscountVO fullDiscount = this.checkFullDiscountExist(fullDiscountVO.getId()); + if (!fullDiscount.getPromotionStatus().equals(PromotionStatusEnum.NEW.name())) { + throw new ServiceException("当前编辑的满优惠活动已经开始或者已经结束,无法修改"); + } + // 检查活动是否已经开始 + PromotionTools.checkPromotionTime(fullDiscountVO.getStartTime().getTime(), fullDiscountVO.getEndTime().getTime()); + // 检查满减参数 + this.checkFullDiscount(fullDiscountVO); + // 时间发生变化 + if (!fullDiscount.getStartTime().equals(fullDiscountVO.getStartTime()) && fullDiscount.getEndTime().equals(fullDiscountVO.getEndTime())) { + // 检查当前时间段是否存在同类活动 + this.checkSameActiveExist(fullDiscountVO.getStartTime(), fullDiscountVO.getEndTime(), fullDiscountVO.getStoreId(), fullDiscount.getId()); + + } + // 更新到MYSQL中 + this.updateById(fullDiscountVO); + if (fullDiscountVO.getPromotionGoodsList() != null) { + // 促销活动商品更新 + this.promotionGoodsService.updateBatchById(PromotionTools.promotionGoodsInit(fullDiscountVO.getPromotionGoodsList(), fullDiscountVO, PromotionTypeEnum.FULL_DISCOUNT)); + } + // 保存到MONGO中 + this.mongoTemplate.save(fullDiscountVO); + PromotionMessage promotionMessage = new PromotionMessage(fullDiscountVO.getId(), PromotionTypeEnum.FULL_DISCOUNT.name(), PromotionStatusEnum.START.name(), fullDiscountVO.getStartTime(), fullDiscountVO.getEndTime()); + // 发送更新延时任务 + this.timeTrigger.edit(TimeExecuteConstant.PROMOTION_EXECUTOR, promotionMessage, + fullDiscount.getStartTime().getTime(), fullDiscountVO.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + DateUtil.getDelayTime(fullDiscountVO.getStartTime().getTime()), + rocketmqCustomProperties.getPromotionTopic()); + return fullDiscountVO; + } + + /** + * 获取满优惠活动详情 + * + * @param id 满优惠KID + * @return 满优惠活动详情 + */ + @Override + public FullDiscountVO getFullDiscount(String id) { + return this.checkFullDiscountExist(id); + } + + @Override + public boolean deleteFullDiscount(String id) { + FullDiscountVO fullDiscount = this.checkFullDiscountExist(id); + // 检查活动是否已经开始 + boolean result = this.removeById(id); + this.mongoTemplate.remove(new Query().addCriteria(Criteria.where("id").is(id)), FullDiscountVO.class); + if (fullDiscount.getPromotionGoodsList() != null && !fullDiscount.getPromotionGoodsList().isEmpty()) { + this.promotionGoodsService.removePromotionGoods(fullDiscount.getPromotionGoodsList(), PromotionTypeEnum.FULL_DISCOUNT); + } + this.timeTrigger.delete(TimeExecuteConstant.PROMOTION_EXECUTOR, fullDiscount.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (PromotionTypeEnum.FULL_DISCOUNT.name() + fullDiscount.getId())), + rocketmqCustomProperties.getPromotionTopic()); + return result; + } + + /** + * 检查满优惠活动是否存在 + * + * @param id 满优惠活动id + * @return 满优惠活动 + */ + private FullDiscountVO checkFullDiscountExist(String id) { + FullDiscountVO fullDiscountVO = mongoTemplate.findById(id, FullDiscountVO.class); + if (fullDiscountVO == null) { + throw new ServiceException("当前要操作的满优惠活动不存在"); + } + return fullDiscountVO; + } + + /** + * 检查满减参数 + * + * @param fullDiscountVO 满减参数信息 + */ + private void checkFullDiscount(FullDiscountVO fullDiscountVO) { + if (fullDiscountVO.getIsFullMinus() == null && fullDiscountVO.getIsCoupon() == null && fullDiscountVO.getIsGift() == null && fullDiscountVO.getIsPoint() == null && fullDiscountVO.getIsFullRate() == null) { + throw new ServiceException("请选择一种优惠方式"); + } + // 如果优惠方式是满减 + if (Boolean.TRUE.equals(fullDiscountVO.getIsFullMinus())) { + this.checkFullMinus(fullDiscountVO.getFullMinus(), fullDiscountVO.getFullMoney()); + fullDiscountVO.setTitle("满" + fullDiscountVO.getFullMoney() + " 减" + fullDiscountVO.getFullMinus()); + } + // 如果优惠方式是赠品 + if (Boolean.TRUE.equals(fullDiscountVO.getIsGift())) { + // 是否没有选择赠品 + boolean noGiftSelected = fullDiscountVO.getGiftId() == null; + if (noGiftSelected) { + throw new ServiceException("请选择赠品"); + } + } + // 如果优惠方式是赠优惠券 + if (Boolean.TRUE.equals(fullDiscountVO.getIsCoupon())) { + this.checkCoupon(fullDiscountVO.getCouponId(), fullDiscountVO.getEndTime().getTime()); + } + // 如果优惠方式是折扣 + if (Boolean.TRUE.equals(fullDiscountVO.getIsFullRate())) { + this.checkFullRate(fullDiscountVO.getFullRate()); + fullDiscountVO.setTitle("满" + fullDiscountVO.getFullMoney() + " 打" + fullDiscountVO.getFullRate() + "折"); + } + + } + + /** + * 检查同一时间段内不能存在相同的活动数量 + * + * @param statTime 开始时间 + * @param endTime 结束时间 + * @param storeId 店铺id + * @param id 满优惠活动ID + */ + private void checkSameActiveExist(Date statTime, Date endTime, String storeId, String id) { + // 同一时间段内相同的活动 + QueryWrapper queryWrapper = PromotionTools.checkActiveTime(statTime, endTime, PromotionTypeEnum.FULL_DISCOUNT, storeId, id); + Integer sameNum = this.fullDiscountMapper.selectCount(queryWrapper); + if (sameNum > 0) { + throw new ServiceException("当前时间内已存在同类活动"); + } + } + + /** + * 检查优惠券信息 + * + * @param couponId 优惠券编号 + * @param endTime 活动结束时间 + */ + private void checkCoupon(String couponId, long endTime) { + // 是否没有选择优惠券 + boolean noCouponSelected = couponId == null; + if (noCouponSelected) { + throw new ServiceException("请选择优惠券"); + } + Coupon coupon = this.couponService.getById(couponId); + if (coupon.getEndTime().getTime() < endTime) { + throw new ServiceException("赠送的优惠券有效时间必须大于活动时间"); + } + } + + /** + * 检查满减信息 + * + * @param fullMinus 满减金额 + * @param fullMoney 优惠门槛 + */ + private void checkFullMinus(Double fullMinus, Double fullMoney) { + // 是否没有填写满减金额 + boolean noFullMinusInput = fullMinus == null || fullMinus == 0; + if (noFullMinusInput) { + throw new ServiceException("请填写满减金额"); + } + if (fullMinus > fullMoney) { + throw new ServiceException("满减金额不能大于优惠门槛"); + } + } + + /** + * 检查打折信息 + * + * @param fullRate 打折数值 + */ + private void checkFullRate(Double fullRate) { + // 是否没有填写打折数值 + boolean noFullRateInput = fullRate == null || fullRate == 0; + if (noFullRateInput) { + throw new ServiceException("请填写打折数值"); + } + int rateLimit = 10; + if (fullRate >= rateLimit || fullRate <= 0) { + throw new ServiceException("请填写打折数值"); + } + } + + /** + * 通用有效的满优惠活动mongo查询 + * + * @return mongo查询对象 + */ + private Query getMongoQuery() { + Query query = new Query(); + Date now = new Date(); + query.addCriteria(Criteria.where(PROMOTION_STATUS_COLUMN).is(PromotionStatusEnum.START.name())); + query.addCriteria(Criteria.where("startTime").lt(now)); + query.addCriteria(Criteria.where("endTime").gt(now)); + return query; + } + + @Autowired + public void setPromotionGoodsService(PromotionGoodsService promotionGoodsService) { + this.promotionGoodsService = promotionGoodsService; + } + + @Autowired + public void setCouponService(CouponService couponService) { + this.couponService = couponService; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/MemberAddressServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/MemberAddressServiceImpl.java new file mode 100644 index 00000000..3cb12353 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/MemberAddressServiceImpl.java @@ -0,0 +1,109 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.member.entity.dos.MemberAddress; +import cn.lili.modules.member.mapper.MemberAddressMapper; +import cn.lili.modules.promotion.service.MemberAddressService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 收货地址业务层实现 + * + * @author Chopper + * @date 2020/11/18 9:44 上午 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberAddressServiceImpl extends ServiceImpl implements MemberAddressService { + + @Override + public IPage getAddressByMember(PageVO page,String memberId) { + return this.page(PageUtil.initPage(page), + new QueryWrapper() + .eq("member_id", memberId)); + + } + + @Override + public MemberAddress getMemberAddress(String id) { + return this.getOne( + new QueryWrapper() + .eq("member_id", UserContext.getCurrentUser().getId()) + .eq("id", id)); + } + + /** + * 根据地址ID获取当前会员地址信息 + * + * @return 当前会员的地址信息 + */ + @Override + public MemberAddress getDefaultMemberAddress() { + return this.getOne( + new QueryWrapper() + .eq("member_id", UserContext.getCurrentUser().getId()) + .eq("is_default", true)); + } + + @Override + public MemberAddress saveMemberAddress(MemberAddress memberAddress) { + //判断当前地址是否为默认地址,如果为默认需要将其他的地址修改为非默认 + updateDefaultShippingAddress(memberAddress); + //添加会员地址 + this.save(memberAddress); + + return memberAddress; + } + + @Override + public MemberAddress updateMemberAddress(MemberAddress memberAddress) { + //判断当前地址是否为默认地址,如果为默认需要将其他的地址修改为非默认 + updateDefaultShippingAddress(memberAddress); + //修改会员地址 + this.update(memberAddress, + new QueryWrapper() + .eq("id", memberAddress.getId())); + return memberAddress; + } + + @Override + public boolean removeMemberAddress(String id) { + return this.remove(new QueryWrapper() + .eq("id", id)); + } + + /** + * 修改会员默认收件地址 + * + * @param shippingAddress 收件地址 + */ + private void updateDefaultShippingAddress(MemberAddress shippingAddress) { + //校验此地址是否为第一个会员地址 如果是默认是会员默认地址 + List list = this.baseMapper.selectList(new QueryWrapper().eq("member_id", shippingAddress.getMemberId())); + if (list.size() == 1) { + shippingAddress.setIsDefault(true); + } + //如果不是默认地址不需要处理 + if (shippingAddress.getIsDefault()) { + //将会员的地址修改为非默认地址 + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.set(MemberAddress::getIsDefault, false); + lambdaUpdateWrapper.eq(MemberAddress::getMemberId, shippingAddress.getMemberId()); + this.update(lambdaUpdateWrapper); + } + + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/MemberCouponServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/MemberCouponServiceImpl.java new file mode 100644 index 00000000..9c634c59 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/MemberCouponServiceImpl.java @@ -0,0 +1,217 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.Coupon; +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import cn.lili.modules.promotion.entity.enums.CouponScopeTypeEnum; +import cn.lili.modules.promotion.entity.enums.MemberCouponStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.vos.CouponSearchParams; +import cn.lili.modules.promotion.mapper.MemberCouponMapper; +import cn.lili.modules.promotion.service.CouponService; +import cn.lili.modules.promotion.service.MemberCouponService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +/** + * 会员优惠券业务层实现 + * + * @author Chopper + * @date 2020/8/21 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberCouponServiceImpl extends ServiceImpl implements MemberCouponService { + + /** + * 优惠券 + */ + private CouponService couponService; + + @Override + public void checkCouponLimit(String couponId, String memberId) { + Coupon coupon = couponService.getById(couponId); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(MemberCoupon::getCouponId, couponId) + .eq(MemberCoupon::getMemberId, memberId); + int haveCoupons = this.count(queryWrapper); + if (!PromotionStatusEnum.START.name().equals(coupon.getPromotionStatus())) { + throw new ServiceException("当前优惠券状态不可领取"); + } + if (coupon.getReceivedNum() >= coupon.getPublishNum()) { + throw new ServiceException("优惠券剩余领取数量不足"); + } + if (haveCoupons >= coupon.getCouponLimitNum()) { + throw new ServiceException("此优惠券最多领取" + coupon.getCouponLimitNum()+"张"); + } + } + + @Override + public void receiveCoupon(String couponId, String memberId, String memberName) { + Coupon coupon = couponService.getCouponDetailFromMongo(couponId); + if (coupon != null) { + this.checkCouponLimit(couponId, memberId); + MemberCoupon memberCoupon = new MemberCoupon(coupon); + memberCoupon.setMemberId(memberId); + memberCoupon.setMemberName(memberName); + memberCoupon.setMemberCouponStatus(MemberCouponStatusEnum.NEW.name()); + memberCoupon.setIsPlatform(coupon.getStoreId().equals("platform")); + this.save(memberCoupon); + couponService.receiveCoupon(couponId, 1); + } else { + throw new ServiceException("当前优惠券不存在"); + } + } + + @Override + public IPage getMemberCoupons(CouponSearchParams param, PageVO pageVo) { + QueryWrapper queryWrapper = param.wrapper(); + return this.page(PageUtil.initPage(pageVo), queryWrapper); + } + + /** + * 获取会员优惠券列表 + * + * @param param 查询参数 + * @param pageVo 分页参数 + * @return 会员优惠券列表 + */ + @Override + public IPage getMemberCouponsByCanUse(CouponSearchParams param, Double totalPrice, PageVO pageVo) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(MemberCoupon::getStoreId, Arrays.asList(param.getStoreId(), "platform")); + queryWrapper.eq(MemberCoupon::getMemberId, param.getMemberId()); + queryWrapper.and( + i -> i.like(MemberCoupon::getScopeId, param.getScopeId()) + .or(j -> j.eq(MemberCoupon::getScopeType, CouponScopeTypeEnum.ALL.name()))); + queryWrapper.eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name()); + queryWrapper.le(MemberCoupon::getConsumeThreshold, totalPrice); + queryWrapper.gt(MemberCoupon::getEndTime, new Date()); + return this.page(PageUtil.initPage(pageVo), queryWrapper); + } + + /** + * 获取当前会员当前商品可用的会员优惠券 + * + * @param memberId 会员Id + * @param couponIds 优惠券id列表 + * @return 会员优惠券列表 + */ + @Override + public List getCurrentGoodsCanUse(String memberId, List couponIds, Double totalPrice) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(MemberCoupon::getMemberId, memberId); + queryWrapper.eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name()); + queryWrapper.in(MemberCoupon::getCouponId, couponIds); + queryWrapper.ne(MemberCoupon::getScopeType, CouponScopeTypeEnum.ALL.name()); + queryWrapper.le(MemberCoupon::getConsumeThreshold, totalPrice); + queryWrapper.gt(MemberCoupon::getEndTime, new Date()); + return this.list(queryWrapper); + } + + /** + * 获取当前会员全品类优惠券 + * + * @param memberId 会员Id + * @param storeId 店铺id + * @return 会员优惠券列表 + */ + @Override + public List getAllScopeMemberCoupon(String memberId, List storeId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(MemberCoupon::getMemberId, memberId); + queryWrapper.eq(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.NEW.name()); + queryWrapper.eq(MemberCoupon::getScopeType, CouponScopeTypeEnum.ALL.name()); + queryWrapper.gt(MemberCoupon::getEndTime, new Date()).and(i -> i.in(MemberCoupon::getStoreId, storeId).or(j -> j.eq(MemberCoupon::getIsPlatform, true))); + return this.list(queryWrapper); + } + + @Override + public Integer getMemberCouponsNum() { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq("member_id", UserContext.getCurrentUser().getId()); + queryWrapper.eq("member_coupon_status", MemberCouponStatusEnum.NEW.name()); + queryWrapper.eq("delete_flag", false); + return this.count(queryWrapper); + } + + /** + * 更新会员优惠券状态 + * + * @param status 要变更的状态 + * @param id 会员优惠券id + */ + @Override + public void updateMemberCouponStatus(MemberCouponStatusEnum status, String id) { + MemberCoupon memberCoupon = this.getById(id); + if (memberCoupon == null) { + throw new ServiceException("没有当前会员优惠券!"); + } + String memberCouponStatus = memberCoupon.getMemberCouponStatus(); + if (memberCouponStatus.equals(MemberCouponStatusEnum.NEW.name()) || memberCouponStatus.equals(MemberCouponStatusEnum.USED.name())) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(MemberCoupon::getId, id).set(MemberCoupon::getMemberCouponStatus, status.name()); + this.update(updateWrapper); + } else { + throw new ServiceException("当前会员优惠券已过期/作废无法变更状态!"); + } + } + + + @Override + public void used(List ids) { + if (ids != null && !ids.isEmpty()) { + List memberCoupons = this.listByIds(ids); + + //如果查出来的优惠券数量不一致 + if (memberCoupons.size() != ids.size()) { + throw new ServiceException(ResultCode.COUPON_EXPIRED); + } + //循环处理 + memberCoupons.forEach(item -> { + if (!item.getMemberCouponStatus().equals(MemberCouponStatusEnum.NEW.name())) { + throw new ServiceException(ResultCode.COUPON_EXPIRED); + } + item.setMemberCouponStatus(MemberCouponStatusEnum.USED.name()); + item.setConsumptionTime(new Date()); + }); + + this.updateBatchById(memberCoupons); + } + } + + /** + * 作废当前会员优惠券 + * + * @param id id + */ + @Override + public void cancellation(String id) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(MemberCoupon::getId, id); + updateWrapper.set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.CLOSED.name()); + this.update(updateWrapper); + } + + @Autowired + public void setCouponService(CouponService couponService) { + this.couponService = couponService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PintuanServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PintuanServiceImpl.java new file mode 100644 index 00000000..01c71d54 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PintuanServiceImpl.java @@ -0,0 +1,521 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.hutool.core.bean.BeanUtil; +import cn.lili.common.delayqueue.DelayQueueTools; +import cn.lili.common.delayqueue.DelayQueueType; +import cn.lili.common.delayqueue.PromotionMessage; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.trigger.interfaces.TimeTrigger; +import cn.lili.common.trigger.model.TimeExecuteConstant; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.entity.enums.OrderTypeEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.promotion.entity.dos.Pintuan; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.PintuanMemberVO; +import cn.lili.modules.promotion.entity.vos.PintuanSearchParams; +import cn.lili.modules.promotion.entity.vos.PintuanShareVO; +import cn.lili.modules.promotion.entity.vos.PintuanVO; +import cn.lili.modules.promotion.mapper.PintuanMapper; +import cn.lili.modules.promotion.service.PintuanService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import cn.lili.modules.promotion.tools.PromotionTools; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 拼团业务层实现 + * + * @author Chopper + * @date 2020/8/21 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PintuanServiceImpl extends ServiceImpl implements PintuanService { + + //延时任务 + private final TimeTrigger timeTrigger; + //Mongo + private final MongoTemplate mongoTemplate; + //促销商品 + private final PromotionGoodsService promotionGoodsService; + //规格商品 + private final GoodsSkuService goodsSkuService; + //会员 + private final MemberService memberService; + //Rocketmq + private final RocketmqCustomProperties rocketmqCustomProperties; + //订单 + private OrderService orderService; + + @Override + public IPage getPintuanByPage(PintuanSearchParams param, PageVO page) { + QueryWrapper queryWrapper = param.wrapper(); + return page(PageUtil.initPage(page), queryWrapper); + } + + /** + * 获取当前拼团的会员 + * + * @param pintuanId 拼图id + * @return 当前拼团的会员列表 + */ + @Override + public List getPintuanMember(String pintuanId) { + List members = new ArrayList<>(); + PintuanVO pintuan = this.getPintuanByIdFromMongo(pintuanId); + if (pintuan == null) { + log.error("拼团活动为" + pintuanId + "的拼团活动不存在!"); + return new ArrayList<>(); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Order::getPromotionId, pintuanId).eq(Order::getOrderType, OrderTypeEnum.PINTUAN.name()) + .eq(Order::getOrderStatus, OrderStatusEnum.PAID.name()).eq(Order::getParentOrderSn, ""); + List orders = orderService.list(queryWrapper); + // 遍历订单状态为已支付,为团长的拼团订单 + for (Order order : orders) { + Member member = memberService.getById(order.getMemberId()); + PintuanMemberVO memberVO = new PintuanMemberVO(member); + LambdaQueryWrapper countQueryWrapper = new LambdaQueryWrapper<>(); + countQueryWrapper.eq(Order::getOrderStatus, OrderStatusEnum.PAID.name()); + countQueryWrapper.and(i -> i.eq(Order::getSn, order.getSn()).or(j -> j.eq(Order::getParentOrderSn, order.getSn()))); + // 获取已参团人数 + int count = orderService.count(countQueryWrapper); + // 获取待参团人数 + int toBoGrouped = pintuan.getRequiredNum() - count; + memberVO.setGroupNum(pintuan.getRequiredNum()); + memberVO.setGroupedNum(count); + memberVO.setToBeGroupedNum(toBoGrouped); + memberVO.setOrderSn(order.getSn()); + members.add(memberVO); + } + return members; + } + + @Override + public IPage getPintuanByPageFromMongo(PintuanSearchParams param, PageVO page) { + IPage pintuanPage = new Page<>(); + Query query = param.mongoQuery(); + if (page != null) { + page.setNotConvert(true); + PromotionTools.mongoQueryPageParam(query, page); + pintuanPage.setCurrent(page.getPageNumber()); + pintuanPage.setSize(page.getPageSize()); + } + List pintuanVOS = mongoTemplate.find(query, PintuanVO.class); + pintuanPage.setRecords(pintuanVOS); + pintuanPage.setTotal(this.getPintuanByPageFromMongoCount(param)); + return pintuanPage; + } + + /** + * 从mongo中查询拼团活动详情 + * + * @param id 拼团ID + * @return 拼团活动详情 + */ + @Override + public PintuanVO getPintuanByIdFromMongo(String id) { + PintuanVO pintuanVO = mongoTemplate.findById(id, PintuanVO.class); + if (pintuanVO == null) { + log.error("拼团活动id[" + id + "]的拼团活动不存在!"); + throw new ServiceException("网络出现异常!"); + } + return pintuanVO; + } + + /** + * 从mysql中查询拼团活动详情 + * + * @param id 拼团活动id + * @return 拼团活动详情 + */ + @Override + public Pintuan getPintuanById(String id) { + Pintuan pintuan = this.getById(id); + if (pintuan == null) { + log.error("拼团活动id[" + id + "]的拼团活动不存在!"); + throw new ServiceException("网络出现异常!"); + } + return pintuan; + } + + /** + * 从mongo中根据条件查询拼团活动总数 + * + * @param param 拼团活动查询参数 + * @return 总数 + */ + @Override + public Long getPintuanByPageFromMongoCount(PintuanSearchParams param) { + Query query = param.mongoQuery(); + return mongoTemplate.count(query, PintuanVO.class); + } + + @Override + public boolean addPintuan(PintuanVO pintuan) { + PromotionTools.checkPromotionTime(pintuan.getStartTime().getTime(), pintuan.getEndTime().getTime()); + this.checkSamePromotion(pintuan.getStartTime(), pintuan.getEndTime(), pintuan.getStoreId(), null); + pintuan.setPromotionStatus(PromotionStatusEnum.NEW.name()); + // 保存到MYSQL中 + boolean result = this.save(pintuan); + this.updatePintuanPromotionGoods(pintuan); + this.mongoTemplate.save(pintuan); + this.addPintuanStartTask(pintuan); + return result; + } + + @Override + public boolean modifyPintuan(PintuanVO pintuan) { + PintuanVO pintuanVO = this.checkExist(pintuan.getId()); + if (!pintuan.getPromotionStatus().equals(PromotionStatusEnum.NEW.name())) { + throw new ServiceException("只有活动状态为新活动时(活动未开始)才可编辑!"); + } + // 检查促销时间 + PromotionTools.checkPromotionTime(pintuan.getStartTime().getTime(), pintuan.getEndTime().getTime()); + // 检查同一时间,同一店铺,同一类型的促销活动 + this.checkSamePromotion(pintuan.getStartTime(), pintuan.getEndTime(), pintuan.getStoreId(), pintuan.getId()); + boolean result = this.updateById(pintuan); + if (pintuan.getPromotionGoodsList() != null) { + this.updatePintuanPromotionGoods(pintuan); + } + this.mongoTemplate.save(pintuan); + // 时间发生变化 + if (pintuan.getStartTime().getTime() != pintuanVO.getStartTime().getTime()) { + PromotionMessage promotionMessage = new PromotionMessage(pintuan.getId(), PromotionTypeEnum.PINTUAN.name(), PromotionStatusEnum.START.name(), pintuan.getStartTime(), pintuan.getEndTime()); + // 更新延时任务 + this.timeTrigger.edit(TimeExecuteConstant.PROMOTION_EXECUTOR, + promotionMessage, + pintuanVO.getStartTime().getTime(), + pintuan.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + DateUtil.getDelayTime(pintuan.getStartTime().getTime()), + rocketmqCustomProperties.getPromotionTopic()); + } + + return result; + } + + @Override + public boolean openPintuan(String pintuanId, Date startTime, Date endTime) { + PintuanVO pintuan = checkExist(pintuanId); + pintuan.setStartTime(startTime); + pintuan.setEndTime(endTime); + boolean result; + + long endTimeLong = endTime.getTime() / 1000; + //如果还在活动时间内 + if (endTimeLong > DateUtil.getDateline()) { + pintuan.setPromotionStatus(PromotionStatusEnum.NEW.name()); + updatePintuanPromotionGoods(pintuan); + this.addPintuanStartTask(pintuan); + } else { + //活动时间范围外,修改状态为已结束 + pintuan.setPromotionStatus(PromotionStatusEnum.END.name()); + } + + pintuan.setPromotionGoodsList(new ArrayList<>()); + result = this.updateById(pintuan); + this.mongoTemplate.save(pintuan); + return result; + } + + @Override + public boolean closePintuan(String pintuanId) { + PintuanVO pintuan = checkExist(pintuanId); + + long endTime = pintuan.getEndTime().getTime() / 1000; + //如果还在活动时间内 + if (endTime > DateUtil.getDateline()) { + //表示可以再次开启,则不处理未成团订单,因为可以开启 + pintuan.setPromotionStatus(PromotionStatusEnum.CLOSE.name()); + } else { + pintuan.setPromotionStatus(PromotionStatusEnum.END.name()); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Order::getOrderType, OrderTypeEnum.PINTUAN.name()); + queryWrapper.eq(Order::getPromotionId, pintuanId); + queryWrapper.nested(i -> i.eq(Order::getPayStatus, PayStatusEnum.PAID.name()).or().eq(Order::getOrderStatus, OrderStatusEnum.PAID.name())); + // 过滤父级拼团订单,根据父级拼团订单分组 + Map> collect = orderService.list(queryWrapper).stream().filter(i -> !i.getParentOrderSn().equals("")).collect(Collectors.groupingBy(Order::getParentOrderSn)); + this.isOpenFictitiousPintuan(pintuan, collect); + + } + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Pintuan::getId, pintuanId).set(Pintuan::getPromotionStatus, PromotionStatusEnum.CLOSE.name()); + boolean result = this.update(updateWrapper); + if (pintuan.getPromotionGoodsList() != null && !pintuan.getPromotionGoodsList().isEmpty()) { + LambdaQueryWrapper deleteWrapper = new LambdaQueryWrapper<>(); + deleteWrapper.eq(PromotionGoods::getPromotionId, pintuanId); + promotionGoodsService.remove(deleteWrapper); + pintuan.setPromotionGoodsList(new ArrayList<>()); + } + this.removePintuanGoodsFromEs(pintuanId, pintuan.getStartTime().getTime()); + this.mongoTemplate.save(pintuan); + return result; + } + + /** + * 删除拼团 + * + * @param pintuanId 拼团活动编号 + * @return 是否成功 + */ + @Override + public boolean deletePintuan(String pintuanId) { + PintuanVO pintuanVO = this.checkExist(pintuanId); + pintuanVO.setDeleteFlag(true); + if (pintuanVO.getPromotionGoodsList() != null && !pintuanVO.getPromotionGoodsList().isEmpty()) { + LambdaQueryWrapper deleteWrapper = new LambdaQueryWrapper<>(); + deleteWrapper.eq(PromotionGoods::getPromotionId, pintuanId); + promotionGoodsService.remove(deleteWrapper); + pintuanVO.setPromotionGoodsList(new ArrayList<>()); + } + boolean result = this.updateById(pintuanVO); + this.mongoTemplate.save(pintuanVO); + this.removePintuanGoodsFromEs(pintuanId, pintuanVO.getStartTime().getTime()); + return result; + } + + /** + * 获取拼团分享信息 + * + * @param parentOrderSn 拼团团长订单sn + * @param skuId 商品skuId + * @return 拼团分享信息 + */ + @Override + public PintuanShareVO getPintuanShareInfo(String parentOrderSn, String skuId) { + PintuanShareVO pintuanShareVO = new PintuanShareVO(); + pintuanShareVO.setPintuanMemberVOS(new ArrayList<>()); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // 查找团长订单和已和当前拼团订单拼团的订单 + queryWrapper.eq(Order::getOrderType, OrderTypeEnum.PINTUAN.name()) + .eq(Order::getPayStatus, OrderStatusEnum.PAID.name()) + .and(i -> i.eq(Order::getParentOrderSn, parentOrderSn).or(j -> j.eq(Order::getSn, parentOrderSn))); + List orders = orderService.list(queryWrapper); + this.setPintuanOrderInfo(orders, pintuanShareVO, skuId); + // 如果为根据团员订单sn查询拼团订单信息时,找到团长订单sn,然后找到所有参与到同一拼团的订单信息 + if (!orders.isEmpty() && pintuanShareVO.getPromotionGoods() == null) { + LambdaQueryWrapper orderLambdaQueryWrapper = new LambdaQueryWrapper<>(); + // 查找团长订单和已和当前拼团订单拼团的订单 + orderLambdaQueryWrapper.eq(Order::getOrderType, OrderTypeEnum.PINTUAN.name()) + .eq(Order::getPayStatus, OrderStatusEnum.PAID.name()) + .ne(Order::getSn, parentOrderSn) + .and(i -> i.eq(Order::getParentOrderSn, orders.get(0).getParentOrderSn()).or(j -> j.eq(Order::getSn, orders.get(0).getParentOrderSn()))); + List parentOrders = orderService.list(orderLambdaQueryWrapper); + this.setPintuanOrderInfo(parentOrders, pintuanShareVO, skuId); + + } + return pintuanShareVO; + } + + /** + * 根据订单信息,从中提取出拼团信息,设置拼团信息 + * + * @param orders 订单列表 + * @param pintuanShareVO 拼团信息 + * @param skuId 商品skuId(用于获取拼团商品信息) + */ + private void setPintuanOrderInfo(List orders, PintuanShareVO pintuanShareVO, String skuId) { + for (Order order : orders) { + Member member = memberService.getById(order.getMemberId()); + PintuanMemberVO memberVO = new PintuanMemberVO(member); + if (order.getParentOrderSn().equals("")) { + memberVO.setOrderSn(""); + PromotionGoods promotionGoods = promotionGoodsService.getPromotionGoods(PromotionTypeEnum.PINTUAN, order.getPromotionId(), skuId); + if (promotionGoods == null) { + throw new ServiceException("当前拼团商品不存在!"); + } + pintuanShareVO.setPromotionGoods(promotionGoods); + Pintuan pintuanById = this.getPintuanById(order.getPromotionId()); + LambdaQueryWrapper countQueryWrapper = new LambdaQueryWrapper<>(); + countQueryWrapper.eq(Order::getPayStatus, PayStatusEnum.PAID.name()); + countQueryWrapper.and(i -> i.eq(Order::getSn, order.getSn()).or(j -> j.eq(Order::getParentOrderSn, order.getSn()))); + // 获取已参团人数 + int count = orderService.count(countQueryWrapper); + // 获取待参团人数 + int toBoGrouped = pintuanById.getRequiredNum() - count; + memberVO.setGroupNum(pintuanById.getRequiredNum()); + memberVO.setGroupedNum(count); + memberVO.setToBeGroupedNum(toBoGrouped); + } + pintuanShareVO.getPintuanMemberVOS().add(memberVO); + } + } + + private void checkSamePromotion(Date startTime, Date endTime, String storeId, String pintuanId) { + QueryWrapper queryWrapper = PromotionTools.checkActiveTime(startTime, endTime, PromotionTypeEnum.PINTUAN, storeId, pintuanId); + List list = this.list(queryWrapper); + if (!list.isEmpty()) { + throw new ServiceException("当前时间段已存在相同活动!"); + } + } + + private void addPintuanStartTask(PintuanVO pintuan) { + PromotionMessage promotionMessage = new PromotionMessage(pintuan.getId(), PromotionTypeEnum.PINTUAN.name(), PromotionStatusEnum.START.name(), pintuan.getStartTime(), pintuan.getEndTime()); + TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.PROMOTION_EXECUTOR, + pintuan.getStartTime().getTime(), + promotionMessage, + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + rocketmqCustomProperties.getPromotionTopic()); + // 发送促销活动开始的延时任务 + this.timeTrigger.addDelay(timeTriggerMsg, DateUtil.getDelayTime(pintuan.getStartTime().getTime())); + } + + /** + * 从es商品索引中中移除拼团活动 + * + * @param id 拼团活动ID + * @param originStartTime 活动开始时间 + */ + private void removePintuanGoodsFromEs(String id, Long originStartTime) { + this.timeTrigger.delete(TimeExecuteConstant.PROMOTION_EXECUTOR, + originStartTime, + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (PromotionTypeEnum.PINTUAN.name() + id)), + rocketmqCustomProperties.getPromotionTopic()); + } + + /** + * 从指定订单列表中检查是否开始虚拟成团 + * + * @param pintuan 拼团活动信息 + * @param collect 检查的订单列表 + */ + private void isOpenFictitiousPintuan(PintuanVO pintuan, Map> collect) { + // 成团人数 + Integer requiredNum = pintuan.getRequiredNum(); + + for (Map.Entry> entry : collect.entrySet()) { + // 是否开启虚拟成团 + if (Boolean.FALSE.equals(pintuan.getFictitious()) && entry.getValue().size() < requiredNum) { + // 如果未开启虚拟成团且已参团人数小于成团人数,则自动取消订单 + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(Order::getOrderType, OrderTypeEnum.PINTUAN.name()); + updateWrapper.eq(Order::getPromotionId, pintuan.getId()); + updateWrapper.eq(Order::getParentOrderSn, entry.getKey()); + updateWrapper.set(Order::getOrderStatus, OrderStatusEnum.CANCELLED.name()); + updateWrapper.set(Order::getCancelReason, "拼团活动结束订单未付款,系统自动取消订单"); + orderService.update(updateWrapper); + } else if (Boolean.TRUE.equals(pintuan.getFictitious())) { + this.fictitiousPintuan(entry, requiredNum); + } + } + } + + /** + * 虚拟成团 + * + * @param entry 订单列表 + * @param requiredNum 必须参团人数 + */ + private void fictitiousPintuan(Map.Entry> entry, Integer requiredNum) { + Map> listMap = entry.getValue().stream().collect(Collectors.groupingBy(Order::getPayStatus)); + // 未付款订单 + List unpaidOrders = listMap.get(PayStatusEnum.UNPAID.name()); + // 未付款订单自动取消 + if (unpaidOrders != null && !unpaidOrders.isEmpty()) { + for (Order unpaidOrder : unpaidOrders) { + unpaidOrder.setOrderStatus(OrderStatusEnum.CANCELLED.name()); + unpaidOrder.setCancelReason("拼团活动结束订单未付款,系统自动取消订单"); + } + orderService.updateBatchById(unpaidOrders); + } + List paidOrders = listMap.get(PayStatusEnum.PAID.name()); + // 如待参团人数大于0,并已开启虚拟成团 + if (!paidOrders.isEmpty()) { + // 待参团人数 + int waitNum = requiredNum - paidOrders.size(); + // 添加虚拟成团 + for (int i = 0; i < waitNum; i++) { + Order order = new Order(); + BeanUtil.copyProperties(paidOrders.get(0), order); + order.setMemberId("-1"); + order.setMemberName("参团人员"); + orderService.save(order); + paidOrders.add(order); + } + for (Order paidOrder : paidOrders) { + paidOrder.setOrderStatus(OrderStatusEnum.UNDELIVERED.name()); + } + orderService.updateBatchById(paidOrders); + } + } + + /** + * 检查当前拼团活动是否存在 + * + * @param pintuanId 拼团id + * @return 拼团活动 + */ + private PintuanVO checkExist(String pintuanId) { + PintuanVO pintuan = mongoTemplate.findById(pintuanId, PintuanVO.class); + if (pintuan == null) { + throw new ServiceException("当前拼团活动不存在!"); + } + return pintuan; + } + + /** + * 更新记录的促销商品信息 + * + * @param pintuan 拼团信息 + */ + private void updatePintuanPromotionGoods(PintuanVO pintuan) { + + if (pintuan.getPromotionGoodsList() != null && !pintuan.getPromotionGoodsList().isEmpty()) { + List promotionGoods = PromotionTools.promotionGoodsInit(pintuan.getPromotionGoodsList(), pintuan, PromotionTypeEnum.PINTUAN); + for (PromotionGoods promotionGood : promotionGoods) { + if (goodsSkuService.getGoodsSkuByIdFromCache(promotionGood.getSkuId()) == null) { + log.error("商品[" + promotionGood.getGoodsName() + "]不存在或处于不可售卖状态!"); + throw new ServiceException(); + } + // 查询是否在同一时间段参与了拼团活动 + Integer count = promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.SECKILL.name(), promotionGood.getSkuId(), pintuan.getStartTime(), pintuan.getEndTime()); + // 查询是否在同一时间段参与了限时抢购活动 + count += promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.PINTUAN.name(), promotionGood.getSkuId(), pintuan.getStartTime(), pintuan.getEndTime()); + if (count > 0) { + log.error("商品[" + promotionGood.getGoodsName() + "]已经在重叠的时间段参加了限时抢购或拼团活动,不能参加拼团活动"); + throw new ServiceException("商品[" + promotionGood.getGoodsName() + "]已经在重叠的时间段参加了限时抢购或拼团活动,不能参加拼团活动"); + } + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PromotionGoods::getPromotionId, pintuan.getId()).eq(PromotionGoods::getPromotionType, PromotionTypeEnum.PINTUAN.name()); + promotionGoodsService.remove(queryWrapper); + promotionGoodsService.saveOrUpdateBatch(promotionGoods); + } + } + + @Autowired + public void setOrderService(OrderService orderService) { + this.orderService = orderService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PointsGoodsCategoryServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PointsGoodsCategoryServiceImpl.java new file mode 100644 index 00000000..a2ac3b08 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PointsGoodsCategoryServiceImpl.java @@ -0,0 +1,121 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.PointsGoodsCategory; +import cn.lili.modules.promotion.mapper.PointsGoodsCategoryMapper; +import cn.lili.modules.promotion.service.PointsGoodsCategoryService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 积分商品分类业务层实现 + * + * @author paulG + * @date 2020/8/21 + **/ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PointsGoodsCategoryServiceImpl extends ServiceImpl implements PointsGoodsCategoryService { + + /** + * 添加积分商品分类 + * + * @param pointsGoodsCategory 积分商品分类信息 + * @return 是否添加成功 + */ + @Override + public boolean addCategory(PointsGoodsCategory pointsGoodsCategory) { + this.checkNameDuplicate(pointsGoodsCategory.getName(), null); + return this.save(pointsGoodsCategory); + } + + /** + * 更新积分商品分类 + * + * @param pointsGoodsCategory 积分商品分类信息 + * @return 是否更新成功 + */ + @Override + public boolean updateCategory(PointsGoodsCategory pointsGoodsCategory) { + this.checkExist(pointsGoodsCategory.getId()); + this.checkNameDuplicate(pointsGoodsCategory.getName(), pointsGoodsCategory.getId()); + return this.updateById(pointsGoodsCategory); + } + + /** + * 删除积分商品类型 + * + * @param id 积分商品分类id + * @return 是否删除成功 + */ + @Override + public boolean deleteCategory(String id) { + return this.removeById(id); + } + + /** + * 分页获取积分商品类型 + * + * @param name 类型名称 + * @param page 分页参数 + * @return 积分商品类型分页数据 + */ + @Override + public IPage getCategoryByPage(String name, PageVO page) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (StrUtil.isNotEmpty(name)) { + queryWrapper.like(PointsGoodsCategory::getName, name); + } + page.setOrder("ASC"); + page.setSort("sort_order"); + return this.page(PageUtil.initPage(page), queryWrapper); + } + + /** + * 获取积分商品类型详情 + * + * @param id 积分商品类型id + * @return 积分商品类型详情 + */ + @Override + public PointsGoodsCategory getCategoryDetail(String id) { + return this.checkExist(id); + } + + private void checkNameDuplicate(String name, String id) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PointsGoodsCategory::getName, name); + if (id != null) { + queryWrapper.ne(PointsGoodsCategory::getId, id); + } + if (this.getOne(queryWrapper) != null) { + log.error("当前积分商品分类名称" + name + "已存在!"); + throw new ServiceException(); + } + } + + /** + * 根据ID检查积分商品分类是否存在,如存在则范围积分商品分类 + * + * @param id 积分商品分类ID + * @return 积分商品分类 + */ + private PointsGoodsCategory checkExist(String id) { + PointsGoodsCategory category = this.getById(id); + if (category == null) { + log.error("积分商品分类id为" + id + "的分类不存在"); + throw new ServiceException(); + } + return category; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PointsGoodsServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PointsGoodsServiceImpl.java new file mode 100644 index 00000000..281a6d1b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PointsGoodsServiceImpl.java @@ -0,0 +1,306 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.delayqueue.DelayQueueTools; +import cn.lili.common.delayqueue.DelayQueueType; +import cn.lili.common.delayqueue.PromotionMessage; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.trigger.interfaces.TimeTrigger; +import cn.lili.common.trigger.model.TimeExecuteConstant; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.promotion.entity.dos.PointsGoods; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.PointsGoodsSearchParams; +import cn.lili.modules.promotion.entity.vos.PointsGoodsVO; +import cn.lili.modules.promotion.mapper.PointsGoodsMapper; +import cn.lili.modules.promotion.service.PointsGoodsService; +import cn.lili.modules.promotion.tools.PromotionTools; +import cn.lili.modules.search.service.EsGoodsIndexService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 积分商品业务层实现 + * + * @author paulG + * @date 2020/8/21 + **/ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PointsGoodsServiceImpl extends ServiceImpl implements PointsGoodsService { + + //延时任务 + private final TimeTrigger timeTrigger; + //Mongo + private final MongoTemplate mongoTemplate; + //Rocketmq + private final RocketmqCustomProperties rocketmqCustomProperties; + //规格商品 + private final GoodsSkuService goodsSkuService; + //Es商品 + @Autowired + private EsGoodsIndexService goodsIndexService; + + /** + * 批量添加积分商品 + * + * @param pointsGoodsList 积分商品列表 + * @return 是否添加成功 + */ + @Override + public boolean addPointsGoods(List pointsGoodsList) { + List pointsGoodsList1 = new ArrayList<>(); + for (PointsGoodsVO pointsGoods : pointsGoodsList) { + GoodsSku goodsSku = this.checkSkuExist(pointsGoods.getSkuId()); + this.checkParam(pointsGoods, goodsSku); + pointsGoods.setGoodsSku(goodsSku); + PromotionTools.checkPromotionTime(pointsGoods.getStartTime().getTime(), pointsGoods.getEndTime().getTime()); + if (this.checkSkuDuplicate(pointsGoods.getSkuId(), null) == null) { + pointsGoods.setPromotionStatus(PromotionStatusEnum.NEW.name()); + pointsGoods.setPromotionName("积分商品活动"); + pointsGoodsList1.add(pointsGoods); + } else { + throw new ServiceException("商品id为" + pointsGoods.getSkuId() + "的商品已参加积分商品活动!"); + } + } + this.saveBatch(pointsGoodsList1); + for (PointsGoodsVO pointsGoods : pointsGoodsList) { + this.mongoTemplate.save(pointsGoods); + this.addPointsGoodsPromotionTask(pointsGoods); + } + return true; + } + + /** + * 更新一个积分商品 + * + * @param pointsGoods 编辑的积分商品信息 + * @return 是否更新成功 + */ + @Override + public boolean updatePointsGoods(PointsGoodsVO pointsGoods) { + boolean result = false; + PointsGoodsVO pointsGoodsVO = this.checkExist(pointsGoods.getId()); + GoodsSku goodsSku = this.checkSkuExist(pointsGoods.getSkuId()); + this.checkParam(pointsGoods, goodsSku); + pointsGoods.setGoodsSku(goodsSku); + if (this.checkSkuDuplicate(pointsGoods.getSkuId(), pointsGoods.getId()) == null) { + if (PromotionStatusEnum.START.name().equals(pointsGoods.getPromotionStatus()) || PromotionStatusEnum.END.name().equals(pointsGoods.getPromotionStatus())) { + throw new ServiceException("当前活动已开始/结束,无法编辑!"); + } + PromotionTools.checkPromotionTime(pointsGoods.getStartTime().getTime(), pointsGoods.getEndTime().getTime()); + result = this.updateById(pointsGoods); + this.mongoTemplate.save(pointsGoods); + if (pointsGoods.getStartTime().getTime() != pointsGoodsVO.getStartTime().getTime()) { + PromotionMessage promotionMessage = new PromotionMessage(pointsGoods.getId(), PromotionTypeEnum.POINTS_GOODS.name(), PromotionStatusEnum.START.name(), pointsGoods.getStartTime(), pointsGoods.getEndTime()); + // 更新延时任务 + this.timeTrigger.edit(TimeExecuteConstant.PROMOTION_EXECUTOR, + promotionMessage, + pointsGoodsVO.getStartTime().getTime(), + pointsGoods.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + DateUtil.getDelayTime(pointsGoods.getStartTime().getTime()), + rocketmqCustomProperties.getPromotionTopic()); + } + + } + return result; + } + + /** + * 批量更新积分商品状态 + * + * @param ids 积分商品id集合 + * @param promotionStatus 更新的状态 + * @return 是否更新成功 + */ + @Override + public boolean updatePointsGoodsPromotionStatus(List ids, String promotionStatus) { + for (String id : ids) { + PointsGoodsVO pointsGoodsVO = this.checkExist(id); + pointsGoodsVO.setPromotionStatus(PromotionStatusEnum.valueOf(promotionStatus).name()); + this.updateById(pointsGoodsVO); + this.mongoTemplate.save(pointsGoodsVO); + if (promotionStatus.equals(PromotionStatusEnum.START.name())) { + this.addPointsGoodsPromotionTask(pointsGoodsVO); + } else { + this.goodsIndexService.deleteEsGoodsPromotionIndexByList(Collections.singletonList(pointsGoodsVO.getSkuId()), PromotionTypeEnum.POINTS_GOODS); + this.timeTrigger.delete(TimeExecuteConstant.PROMOTION_EXECUTOR, + pointsGoodsVO.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (PromotionTypeEnum.POINTS_GOODS.name() + pointsGoodsVO.getId())), + rocketmqCustomProperties.getPromotionTopic()); + } + } + return true; + } + + /** + * 批量删除积分商品 + * + * @param ids 积分商品id集合 + * @return 是否删除成功 + */ + @Override + public boolean deletePointsGoods(List ids) { + List skuIds = new ArrayList<>(); + for (String id : ids) { + PointsGoodsVO pointsGoodsVO = this.checkExist(id); + this.timeTrigger.delete(TimeExecuteConstant.PROMOTION_EXECUTOR, + pointsGoodsVO.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (PromotionTypeEnum.POINTS_GOODS.name() + pointsGoodsVO.getId())), + rocketmqCustomProperties.getPromotionTopic()); + skuIds.add(pointsGoodsVO.getSkuId()); + } + boolean result = this.removeByIds(ids); + this.goodsIndexService.deleteEsGoodsPromotionIndexByList(skuIds, PromotionTypeEnum.POINTS_GOODS); + Query query = new Query(); + query.addCriteria(new Criteria("id").in(ids)); + this.mongoTemplate.remove(query, PointsGoodsVO.class); + return result; + } + + /** + * 根据ID获取积分详情 + * + * @param id 积分商品id + * @return 积分详情 + */ + @Override + public PointsGoodsVO getPointsGoodsDetail(String id) { + return this.checkExist(id); + } + + /** + * 根据SkuID获取积分详情 + * + * @param skuId@return 积分详情 + */ + @Override + public PointsGoods getPointsGoodsDetailBySkuId(String skuId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PointsGoods::getSkuId, skuId).eq(PointsGoods::getPromotionStatus, PromotionStatusEnum.START.name()); + List list = this.list(queryWrapper); + if (list.size() == 1) { + return list.get(0); + } + return null; + } + + /** + * 根据条件查询积分商品 + * + * @param searchParams 积分商品查询参数 + * @param page 分页参数 + * @return 积分商品查询结果 + */ + @Override + public IPage getPointsGoodsByPage(PointsGoodsSearchParams searchParams, PageVO page) { + IPage pointsGoodsPage = new Page<>(); + Query query = searchParams.mongoQuery(); + if (page != null) { + PromotionTools.mongoQueryPageParam(query, page); + pointsGoodsPage.setSize(page.getPageSize()); + pointsGoodsPage.setCurrent(page.getPageNumber()); + } + List pointsGoodsVOS = this.mongoTemplate.find(query, PointsGoodsVO.class); + pointsGoodsPage.setRecords(pointsGoodsVOS); + pointsGoodsPage.setTotal(this.mongoTemplate.count(searchParams.mongoQuery(), PointsGoodsVO.class)); + return pointsGoodsPage; + } + + + /** + * 添加积分商品mq任务 + * + * @param pointsGoods 积分商品信息 + */ + private void addPointsGoodsPromotionTask(PointsGoodsVO pointsGoods) { + PromotionMessage promotionMessage = new PromotionMessage(pointsGoods.getId(), PromotionTypeEnum.POINTS_GOODS.name(), PromotionStatusEnum.START.name(), pointsGoods.getStartTime(), pointsGoods.getEndTime()); + TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.PROMOTION_EXECUTOR, + promotionMessage.getStartTime().getTime(), + promotionMessage, + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + rocketmqCustomProperties.getPromotionTopic()); + // 发送促销活动开始的延时任务 + this.timeTrigger.addDelay(timeTriggerMsg, DateUtil.getDelayTime(promotionMessage.getStartTime().getTime())); + } + + /** + * 检查mongo中积分商品存在 + * + * @param id 积分商品id + * @return 积分商品信息 + */ + private PointsGoodsVO checkExist(String id) { + PointsGoodsVO pointsGoodsVO = this.mongoTemplate.findById(id, PointsGoodsVO.class); + if (pointsGoodsVO == null) { + log.error("id为" + id + "的积分商品不存在!"); + throw new ServiceException(); + } + return pointsGoodsVO; + } + + /** + * 检查积分商品是否重复存在 + * + * @param skuId 商品SkuId + * @param id 积分商品I(可选) + * @return 积分商品信息 + */ + private PointsGoods checkSkuDuplicate(String skuId, String id) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PointsGoods::getSkuId, skuId); + if (StrUtil.isNotEmpty(id)) { + queryWrapper.ne(PointsGoods::getId, id); + } + queryWrapper.ne(PointsGoods::getPromotionStatus, PromotionStatusEnum.END.name()); + return this.getOne(queryWrapper); + } + + /** + * 检查商品Sku是否存 + * + * @param skuId skuId + * @return 商品sku + */ + private GoodsSku checkSkuExist(String skuId) { + GoodsSku goodsSku = this.goodsSkuService.getGoodsSkuByIdFromCache(skuId); + if (goodsSku == null) { + log.error("商品ID为" + skuId + "的商品不存在!"); + throw new ServiceException(); + } + return goodsSku; + } + + /** + * 检查参与积分商品参数 + * + * @param pointsGoods 积分商品信息 + * @param goodsSku 商品sku信息 + */ + private void checkParam(PointsGoods pointsGoods, GoodsSku goodsSku) { + if (pointsGoods.getActiveStock() > goodsSku.getQuantity()) { + throw new ServiceException("活动库存数量不能高于商品库存"); + } + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PromotionGoodsServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PromotionGoodsServiceImpl.java new file mode 100644 index 00000000..22522cbf --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PromotionGoodsServiceImpl.java @@ -0,0 +1,450 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.distribution.entity.dos.DistributionGoods; +import cn.lili.modules.distribution.service.DistributionGoodsService; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.dto.GoodsSearchParams; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.order.cart.entity.vo.CartSkuVO; +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import cn.lili.modules.promotion.entity.dos.PointsGoods; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.dos.SeckillApply; +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import cn.lili.modules.promotion.entity.dto.PromotionGoodsDTO; +import cn.lili.modules.promotion.entity.enums.CouponScopeTypeEnum; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.CouponVO; +import cn.lili.modules.promotion.entity.vos.PromotionGoodsSearchParams; +import cn.lili.modules.promotion.entity.vos.SeckillVO; +import cn.lili.modules.promotion.mapper.PromotionGoodsMapper; +import cn.lili.modules.promotion.service.PointsGoodsService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import cn.lili.modules.promotion.service.SeckillApplyService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 促销商品业务层实现 + * + * @author Chopper + * @date 2021/3/18 9:22 上午 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PromotionGoodsServiceImpl extends ServiceImpl implements PromotionGoodsService { + + //Mongo + private final MongoTemplate mongoTemplate; + //Redis + private final StringRedisTemplate stringRedisTemplate; + //限时抢购申请 + private SeckillApplyService seckillApplyService; + //规格商品 + private GoodsSkuService goodsSkuService; + //积分商品 + private PointsGoodsService pointsGoodsService; + //分销商品 + @Autowired + private DistributionGoodsService distributionGoodsService; + + @Override + public PromotionGoods findByPromotion(String promotionId, String skuId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PromotionGoods::getPromotionId, promotionId).eq(PromotionGoods::getSkuId, skuId); + return new PromotionGoods(); + } + + @Override + public void removePromotionGoods(List promotionGoodsList, PromotionTypeEnum promotionType) { + for (PromotionGoods promotionGoods : promotionGoodsList) { + promotionGoods.setDeleteFlag(true); + } + updateBatchById(promotionGoodsList); + } + + @Override + public List findNowSkuPromotion(String skuId) { + return this.list(new LambdaQueryWrapper() + .eq(PromotionGoods::getSkuId, skuId) + .eq(PromotionGoods::getPromotionStatus, PromotionStatusEnum.START.name())); + } + + @Override + public void updatePromotion(CartSkuVO cartSkuVO) { + Date date = DateUtil.getCurrentDayEndTime(); + //如果商品的促销更新时间在当前时间之前,则更新促销 + if (cartSkuVO.getUpdatePromotionTime().before(date)) { + List promotionGoods = this.findNowSkuPromotion(cartSkuVO.getGoodsSku().getId()); + cartSkuVO.setPromotions(promotionGoods); + //下一次更新时间 + cartSkuVO.setUpdatePromotionTime(date); + } + PointsGoods pointsGoods = pointsGoodsService.getPointsGoodsDetailBySkuId(cartSkuVO.getGoodsSku().getId()); + if (pointsGoods != null) { + cartSkuVO.setPoint(pointsGoods.getPoints().intValue()); + } + DistributionGoods distributionGoods=distributionGoodsService.distributionGoodsVOBySkuId(cartSkuVO.getGoodsSku().getId()); + if (distributionGoods != null) { + cartSkuVO.setDistributionGoods(distributionGoods); + } + } + + /** + * 获取购物车商品的促销活动 + * + * @param cartSkuVO 购物车中的产品 + */ + @Override + public void getCartSkuPromotion(CartSkuVO cartSkuVO) { + Date date = DateUtil.getCurrentDayEndTime(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PromotionGoods::getSkuId, cartSkuVO.getGoodsSku().getId()).eq(PromotionGoods::getPromotionStatus, PromotionStatusEnum.START.name()); + queryWrapper.le(PromotionGoods::getStartTime, date); + // 获取有效的促销活动 + List promotionGoods = this.list(queryWrapper); + // 同步查询缓存中的促销活动商品的库存 + for (PromotionGoods promotionGood : promotionGoods) { + Integer goodsStock = this.getPromotionGoodsStock(PromotionTypeEnum.valueOf(promotionGood.getPromotionType()), promotionGood.getPromotionId(), promotionGood.getSkuId()); + promotionGood.setQuantity(goodsStock); + } + // 单独检查,添加适用于全品类的满优惠活动 + Query query = new Query(); + query.addCriteria(Criteria.where("promotionStatus").is(PromotionStatusEnum.START.name())); + query.addCriteria(Criteria.where("startTime").lte(date)); + List fullDiscountVOS = mongoTemplate.find(query, FullDiscountVO.class); + for (FullDiscountVO fullDiscountVO : fullDiscountVOS) { + if (fullDiscountVO.getPromotionGoodsList() == null && fullDiscountVO.getNumber() == -1 && + cartSkuVO.getStoreId().equals(fullDiscountVO.getStoreId())) { + PromotionGoods p = new PromotionGoods(cartSkuVO.getGoodsSku()); + p.setPromotionId(fullDiscountVO.getId()); + p.setPromotionStatus(fullDiscountVO.getPromotionStatus()); + p.setPromotionType(PromotionTypeEnum.FULL_DISCOUNT.name()); + p.setStartTime(fullDiscountVO.getStartTime()); + p.setEndTime(fullDiscountVO.getEndTime()); + promotionGoods.add(p); + } + } + // 单独检查,添加适用于全品类的全平台或属于当前店铺的满优惠活动 + List couponVOS = mongoTemplate.find(query, CouponVO.class); + for (CouponVO couponVO : couponVOS) { + if (couponVO.getPromotionGoodsList() == null && couponVO.getScopeType().equals(CouponScopeTypeEnum.ALL.name()) && + (couponVO.getStoreId().equals("0") || cartSkuVO.getStoreId().equals(couponVO.getStoreId()))) { + PromotionGoods p = new PromotionGoods(cartSkuVO.getGoodsSku()); + p.setPromotionId(couponVO.getId()); + p.setPromotionStatus(couponVO.getPromotionStatus()); + p.setPromotionType(PromotionTypeEnum.COUPON.name()); + p.setStartTime(couponVO.getStartTime()); + p.setEndTime(couponVO.getEndTime()); + promotionGoods.add(p); + } + } + cartSkuVO.setPromotions(promotionGoods); + //下一次更新时间 + cartSkuVO.setUpdatePromotionTime(date); + } + + @Override + public List getPromotionGoods(String skuId) { + long currTime = DateUtil.getDateline(); + String currDate = DateUtil.toString(currTime, DateUtil.STANDARD_DATE_NO_UNDERLINE_FORMAT); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(PromotionGoods::getSkuId, skuId) + .le(PromotionGoods::getStartTime, currTime) + .ge(PromotionGoods::getEndTime, currDate) + .ne(PromotionGoods::getPromotionType, PromotionTypeEnum.PINTUAN.name()) + .ne(PromotionGoods::getPromotionType, PromotionTypeEnum.SECKILL.name()); + return this.baseMapper.selectList(queryWrapper); + } + + @Override + public IPage getPromotionGoods(PromotionGoodsSearchParams searchParams, PageVO pageVo) { + IPage promotionGoodsPage = new Page<>(); + LambdaQueryWrapper queryChainWrapper = searchParams.queryWrapper(); + List promotionGoodsList = new ArrayList<>(); + Page page = this.page(PageUtil.initPage(pageVo), queryChainWrapper); + promotionGoodsPage.setSize(page.getSize()); + promotionGoodsPage.setTotal(page.getTotal()); + promotionGoodsPage.setPages(page.getPages()); + for (PromotionGoods record : page.getRecords()) { + PromotionGoodsDTO promotionGoodsDTO = this.wrapperPromotionGoodsDTO(record); + promotionGoodsList.add(promotionGoodsDTO); + } + promotionGoodsPage.setRecords(promotionGoodsList); + return promotionGoodsPage; + } + + @Override + public IPage getCurrentPromotionGoods(String promotionType, PageVO pageVo) { + IPage promotionGoodsPage = new Page<>(); + promotionGoodsPage.setSize(pageVo.getPageSize()); + promotionGoodsPage.setCurrent(pageVo.getPageNumber()); + Date now = new Date(); + Query query = new Query(); + query.addCriteria(Criteria.where("startTime").lt(now)); + query.addCriteria(Criteria.where("endTime").gt(now)); + List promotionGoodsDTOList = new ArrayList<>(); + int total = 0; + // 根据促销活动类型的不同,将满足当前促销活动类型且正在进行的促销商品返回出去 + switch (PromotionTypeEnum.valueOf(promotionType)) { + case FULL_DISCOUNT: + List fullDiscountVOS = this.mongoTemplate.find(query, FullDiscountVO.class); + this.setFullDiscountPromotionGoods(promotionGoodsPage, fullDiscountVOS, pageVo); + break; + case COUPON: + List couponVOS = this.mongoTemplate.find(query, CouponVO.class); + for (CouponVO couponVO : couponVOS) { + if (couponVO != null && couponVO.getPromotionGoodsList() == null) { + IPage page = this.getAllGoodsSkuToPromotionGoodsByPage(couponVO.getStoreId(), couponVO, pageVo); + promotionGoodsDTOList.addAll(page.getRecords()); + total += page.getTotal(); + } + } + promotionGoodsPage.setRecords(promotionGoodsDTOList.subList(0, pageVo.getPageSize())); + promotionGoodsPage.setTotal(total); + break; + case SECKILL: + case POINTS_GOODS: + return promotionGoodsPage; + default: + break; + } + if (promotionGoodsPage.getRecords() == null || promotionGoodsPage.getRecords().isEmpty()) { + promotionGoodsPage = this.getGoodsSkuToPromotionGoodsByPage(promotionType, pageVo); + } + return promotionGoodsPage; + } + + + @Override + public Integer findInnerOverlapPromotionGoods(String promotionType, String skuId, Date startTime, Date endTime) { + return this.baseMapper.selectInnerOverlapPromotionGoods(promotionType, skuId, startTime, endTime); + } + + /** + * 获取促销活动商品库存 + * + * @param typeEnum 促销商品类型 + * @param promotionId 促销活动id + * @param skuId 商品skuId + * @return 促销活动商品库存 + */ + @Override + public Integer getPromotionGoodsStock(PromotionTypeEnum typeEnum, String promotionId, String skuId) { + String promotionStockKey = PromotionGoodsService.getPromotionGoodsStockCacheKey(typeEnum, promotionId, skuId); + String promotionGoodsStock = stringRedisTemplate.opsForValue().get(promotionStockKey); + + PromotionGoods promotionGoods = this.getPromotionGoods(typeEnum, promotionId, skuId); + if (promotionGoods == null) { + throw new ServiceException("当前促销商品不存在!"); + } + if (promotionGoodsStock != null && CharSequenceUtil.isNotEmpty(promotionGoodsStock) && promotionGoods.getQuantity() == Integer.parseInt(promotionGoodsStock)) { + return Integer.parseInt(promotionGoodsStock); + } else { + stringRedisTemplate.opsForValue().set(promotionStockKey, promotionGoods.getQuantity().toString()); + return promotionGoods.getQuantity(); + } + } + + /** + * 根据条件获取促销活动商品详情 + * + * @param typeEnum 促销类型 + * @param promotionId 促销活动id + * @param skuId 商品skuId + * @return 促销活动商品详情 + */ + @Override + public PromotionGoods getPromotionGoods(PromotionTypeEnum typeEnum, String promotionId, String skuId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(PromotionGoods::getPromotionType, typeEnum.name()).eq(PromotionGoods::getPromotionId, promotionId).eq(PromotionGoods::getSkuId, skuId); + return this.getOne(queryWrapper); + } + + /** + * 更新促销活动商品库存 + * + * @param typeEnum 促销商品类型 + * @param promotionId 促销活动id + * @param skuId 商品skuId + * @param quantity 更新后的库存数量 + */ + @Override + public void updatePromotionGoodsStock(PromotionTypeEnum typeEnum, String promotionId, String skuId, Integer quantity) { + String promotionStockKey = PromotionGoodsService.getPromotionGoodsStockCacheKey(typeEnum, promotionId, skuId); + if (typeEnum.equals(PromotionTypeEnum.SECKILL)) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SeckillApply::getSeckillId, promotionId).eq(SeckillApply::getSkuId, skuId); + SeckillApply seckillApply = seckillApplyService.getOne(queryWrapper); + if (seckillApply == null) { + throw new ServiceException("当前限时抢购商品不存在!"); + } + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SeckillApply::getSeckillId, promotionId).eq(SeckillApply::getSkuId, skuId); + updateWrapper.set(SeckillApply::getQuantity, quantity); + seckillApplyService.update(updateWrapper); + SeckillVO seckillVO = mongoTemplate.findById(promotionId, SeckillVO.class); + if (seckillVO != null && seckillApply.getPromotionApplyStatus() != null) { + for (SeckillApply apply : seckillVO.getSeckillApplyList()) { + if (apply.getSkuId().equals(skuId)) { + apply.setQuantity(quantity); + } + } + this.mongoTemplate.save(seckillVO); + } + } else { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(PromotionGoods::getPromotionType, typeEnum.name()).eq(PromotionGoods::getPromotionId, promotionId).eq(PromotionGoods::getSkuId, skuId); + updateWrapper.set(PromotionGoods::getQuantity, quantity); + this.update(updateWrapper); + } + + stringRedisTemplate.opsForValue().set(promotionStockKey, quantity.toString()); + } + + /** + * 分页获取根据条件获取促销商品 + * + * @param goodsName 商品名称 + * @param categoryPath 商品分类 + * @param promotionType 促销类型 + * @param pageVo 分页参数 + * @return 促销商品信息 + */ + @Override + public IPage getPromotionGoodsPage(String goodsName, String categoryPath, String promotionType, PageVO pageVo) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (CharSequenceUtil.isNotEmpty(goodsName)) { + queryWrapper.like(PromotionGoods::getGoodsName, goodsName); + } + if (CharSequenceUtil.isNotEmpty(categoryPath)) { + queryWrapper.like(PromotionGoods::getCategoryPath, categoryPath); + } + if (CharSequenceUtil.isNotEmpty(promotionType)) { + queryWrapper.eq(PromotionGoods::getPromotionType, promotionType); + } + return this.page(PageUtil.initPage(pageVo), queryWrapper); + } + + private void setFullDiscountPromotionGoods(IPage promotionGoodsPage, List fullDiscountVOS, PageVO pageVo) { + List promotionGoodsDTOList = new ArrayList<>(); + int total = 0; + for (FullDiscountVO fullDiscountVO : fullDiscountVOS) { + if (fullDiscountVO != null && fullDiscountVO.getPromotionGoodsList() == null) { + IPage page = this.getAllGoodsSkuToPromotionGoodsByPage(fullDiscountVO.getStoreId(), fullDiscountVO, pageVo); + promotionGoodsDTOList.addAll(page.getRecords()); + total += page.getTotal(); + } + } + if (!fullDiscountVOS.isEmpty() && !promotionGoodsDTOList.isEmpty()) { + promotionGoodsPage.setRecords(promotionGoodsDTOList.subList(0, pageVo.getPageSize())); + promotionGoodsPage.setTotal(total); + } + } + + private IPage getGoodsSkuToPromotionGoodsByPage(String promotionType, PageVO pageVo) { + Date date = new Date(); + IPage promotionGoodsPage = new Page<>(); + LambdaQueryWrapper queryChainWrapper = new LambdaQueryWrapper() + .eq(PromotionGoods::getPromotionType, PromotionTypeEnum.valueOf(promotionType).name()) + .le(PromotionGoods::getStartTime, date).ge(PromotionGoods::getEndTime, date); + List promotionGoodsList = new ArrayList<>(); + Page page = this.page(PageUtil.initPage(pageVo), queryChainWrapper); + promotionGoodsPage.setTotal(page.getTotal()); + promotionGoodsPage.setPages(page.getPages()); + List records = page.getRecords(); + for (PromotionGoods record : records) { + PromotionGoodsDTO promotionGoodsDTO = this.wrapperPromotionGoodsDTO(record); + promotionGoodsList.add(promotionGoodsDTO); + } + promotionGoodsPage.setRecords(promotionGoodsList); + return promotionGoodsPage; + } + + private PromotionGoodsDTO wrapperPromotionGoodsDTO(PromotionGoods promotionGoods) { + PromotionGoodsDTO promotionGoodsDTO = new PromotionGoodsDTO(); + GoodsSku goodsSku = goodsSkuService.getById(promotionGoods.getSkuId()); + BeanUtil.copyProperties(promotionGoods, promotionGoodsDTO); + if (goodsSku != null) { + promotionGoodsDTO.setGoodsId(goodsSku.getGoodsId()); + promotionGoodsDTO.setGoodsImage(goodsSku.getThumbnail()); + promotionGoodsDTO.setGoodsName(goodsSku.getGoodsName()); + promotionGoodsDTO.setOriginPrice(goodsSku.getPrice()); + } + return promotionGoodsDTO; + } + + private IPage getAllGoodsSkuToPromotionGoodsByPage(String storeId, BasePromotion promotion, PageVO pageVo) { + IPage promotionGoodsPage = new Page<>(); + List promotionGoodsList = new ArrayList<>(); + GoodsSearchParams searchParams = new GoodsSearchParams(); + searchParams.setStoreId(storeId); + searchParams.setMarketEnable(GoodsStatusEnum.UPPER.name()); + searchParams.setIsAuth(GoodsAuthEnum.PASS.name()); + searchParams.setPageNumber(pageVo.getPageNumber()); + searchParams.setPageSize(pageVo.getPageSize()); + searchParams.setSort(pageVo.getSort()); + searchParams.setOrder(pageVo.getOrder()); + IPage goodsSkuByPage = goodsSkuService.getGoodsSkuByPage(searchParams); + // 将查询到的商品sku转换为促销商品 + for (GoodsSku record : goodsSkuByPage.getRecords()) { + PromotionGoodsDTO promotionGoods = new PromotionGoodsDTO(record); + promotionGoods.setGoodsImage(record.getThumbnail()); + promotionGoods.setStartTime(promotion.getStartTime()); + promotionGoods.setEndTime(promotion.getEndTime()); + promotionGoods.setTitle(promotion.getPromotionName()); + promotionGoodsList.add(promotionGoods); + } + promotionGoodsPage.setSize(goodsSkuByPage.getSize()); + promotionGoodsPage.setTotal(goodsSkuByPage.getTotal()); + promotionGoodsPage.setPages(goodsSkuByPage.getPages()); + promotionGoodsPage.setRecords(promotionGoodsList); + return promotionGoodsPage; + } + + @Autowired + public void setGoodsSkuService(GoodsSkuService goodsSkuService) { + this.goodsSkuService = goodsSkuService; + } + + @Autowired + public void setPointsGoodsService(PointsGoodsService pointsGoodsService) { + this.pointsGoodsService = pointsGoodsService; + } + + @Autowired + public void setSeckillApplyService(SeckillApplyService seckillApplyService) { + this.seckillApplyService = seckillApplyService; + } + + @Autowired + public void setDistributionGoodsService(DistributionGoodsService distributionGoodsService) { + this.distributionGoodsService = distributionGoodsService; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PromotionPriceServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PromotionPriceServiceImpl.java new file mode 100644 index 00000000..335fc184 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PromotionPriceServiceImpl.java @@ -0,0 +1,513 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.promotion.entity.dos.*; +import cn.lili.modules.promotion.entity.dto.*; +import cn.lili.modules.promotion.entity.enums.*; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import cn.lili.modules.promotion.service.PromotionPriceService; +import cn.lili.modules.promotion.service.SeckillApplyService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.service.EsGoodsSearchService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 促销价格计算业务层实现 + * + * @author paulG + * @date 2020/8/21 + **/ +@Service +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PromotionPriceServiceImpl implements PromotionPriceService { + + //ES商品 + private final EsGoodsSearchService goodsSearchService; + //限时抢购申请 + private final SeckillApplyService seckillApplyService; + //促销商品 + private final PromotionGoodsService promotionGoodsService; + //规格商品 + private final GoodsSkuService goodsSkuService; + + @Override + public PromotionPriceDTO calculationPromotionPrice(List tradeSkuList, List memberCouponList) { + PromotionPriceDTO promotionPrice = new PromotionPriceDTO(); + if (!tradeSkuList.isEmpty()) { + // 拆分出SkuId列表 + List skuIds = tradeSkuList.parallelStream().map(PromotionPriceParamDTO::getSkuId).collect(Collectors.toList()); + // 参与计算的ES商品SKU及其促销信息列表 + List esGoodsSkus = goodsSearchService.getEsGoodsBySkuIds(skuIds); + // 参与计算的缓存中的商品SKU列表 + List goodsSkus = goodsSkuService.getGoodsSkuByIdFromCache(skuIds); + // 计算价格 + promotionPrice = this.calculationPromotionPrice(tradeSkuList, memberCouponList, esGoodsSkus, goodsSkus); + } + return promotionPrice; + } + + /** + * 促销计算 + * + * @param tradeSkuList 促销计算参数列表 + * @param memberCouponList 参与促销计算的优惠券列表 + * @param esGoodsSkus 参与计算的ES商品SKU及其促销信息列表 + * @param goodsSkus 参与计算的缓存中的商品SKU列表 + * @return 促销计算结果 + */ + private PromotionPriceDTO calculationPromotionPrice(List tradeSkuList, List memberCouponList, List esGoodsSkus, List goodsSkus) { + PromotionPriceDTO promotionPrice = new PromotionPriceDTO(); + + // 单品商品SKU促销计算结果列表 + List priceDTOList = this.packageGoodsSkuPromotionPrice(tradeSkuList, esGoodsSkus, goodsSkus); + + // 将使用的优惠券根据店铺分类 + Map> couponCollect = memberCouponList.parallelStream().collect(Collectors.groupingBy(MemberCoupon::getStoreId)); + + double couponTotalPrice = 0; + + // 根据卖家分组商品信息 + Map> storeCollect = priceDTOList.parallelStream().collect(Collectors.groupingBy(GoodsSkuPromotionPriceDTO::getStoreId)); + List storePromotionPriceList = new ArrayList<>(); + for (Map.Entry> entry : storeCollect.entrySet()) { + StorePromotionPriceDTO storePromotionPrice = new StorePromotionPriceDTO(); + storePromotionPrice.setStoreId(entry.getKey()); + storePromotionPrice.setGoodsSkuPromotionPriceList(entry.getValue()); + Optional firstFullDiscount = entry.getValue().parallelStream().filter(i -> i.getPromotionId() != null && i.getPromotionType().equals(PromotionTypeEnum.FULL_DISCOUNT.name())).findFirst(); + if (firstFullDiscount.isPresent()) { + String promotionId = firstFullDiscount.get().getPromotionId(); + // 是否存在满优惠促销活动 + String esPromotionFullDiscountKey = PromotionTypeEnum.FULL_DISCOUNT + "-" + promotionId; + if (CharSequenceUtil.isNotEmpty(promotionId)) { + FullDiscount fullDiscount = null; + for (EsGoodsIndex skus : esGoodsSkus) { + // 检查是否为正在进行的满优惠活动 + if (skus.getPromotionMap() != null && skus.getPromotionMap().containsKey(esPromotionFullDiscountKey)) { + fullDiscount = (FullDiscount) skus.getPromotionMap().get(esPromotionFullDiscountKey); + break; + } + } + if (fullDiscount != null) { + // 计算满优惠活动 + this.calculationFullDiscount(fullDiscount, storePromotionPrice); + } + } + } + + // 获取店铺优惠券 + List storeCoupons = couponCollect.get(entry.getKey()); + if (storeCoupons != null && !storeCoupons.isEmpty()) { + // 计算店铺优惠券活动 + couponTotalPrice = this.calculationCoupon(storeCoupons, entry.getValue()); + storePromotionPrice.setTotalCouponPrice(couponTotalPrice); + storePromotionPrice.setTotalFinalePrice(storePromotionPrice.getGoodsSkuPromotionPriceList().parallelStream().mapToDouble(GoodsSkuPromotionPriceDTO::getTotalFinalePrice).sum()); + } + + // 累加除商品促销信息之外的信息 + this.accumulationGoodsSkuPromotionOther(entry.getValue(), storePromotionPrice); + + storePromotionPriceList.add(storePromotionPrice); + } + // 获取平台优惠券 + List platformCoupons = couponCollect.get("platform"); + if (platformCoupons != null && !platformCoupons.isEmpty()) { + // 计算平台优惠券活动 + couponTotalPrice = CurrencyUtil.add(couponTotalPrice, this.calculationCoupon(platformCoupons, priceDTOList)); + } + promotionPrice.setStorePromotionPriceList(storePromotionPriceList); + promotionPrice.setTotalCouponPrice(couponTotalPrice); + promotionPrice.setTotalOriginPrice(storePromotionPriceList.parallelStream().mapToDouble(StorePromotionPriceDTO::getTotalOriginPrice).sum()); + promotionPrice.setTotalPoints(storePromotionPriceList.parallelStream().mapToDouble(StorePromotionPriceDTO::getTotalPoints).sum()); + promotionPrice.setTotalDiscountPrice(storePromotionPriceList.parallelStream().mapToDouble(StorePromotionPriceDTO::getTotalDiscountPrice).sum()); + // 最终结算金额 = 商品原价格合计 - 总优惠价格合计 - 优惠券合计 + double totalFinalePrice = CurrencyUtil.sub(CurrencyUtil.sub(promotionPrice.getTotalOriginPrice(), promotionPrice.getTotalDiscountPrice()), promotionPrice.getTotalCouponPrice()); + promotionPrice.setTotalFinalePrice(totalFinalePrice); + return promotionPrice; + } + + + /** + * 单品SKU的促销计算 + * + * @param tradeSkuList 交易商品信息列表 + * @param esGoodsSkus 参与计算的ES商品SKU及其促销信息列表 + * @param goodsSkus 参与计算的缓存中的商品SKU列表 + * @return 单品SKU促销计算结果列表 + */ + private List packageGoodsSkuPromotionPrice(List tradeSkuList, List esGoodsSkus, List goodsSkus) { + List priceDTOList = new ArrayList<>(); + for (GoodsSku skus : goodsSkus) { + List collect = esGoodsSkus.parallelStream().filter(i -> i != null && i.getId().equals(skus.getId())).collect(Collectors.toList()); + if (!collect.isEmpty()) { + EsGoodsIndex esGoodsIndex = collect.get(0); + // 找出当前商品相应的结算参数 + PromotionPriceParamDTO tradeSku = tradeSkuList.parallelStream().filter(i -> i.getSkuId().equals(skus.getId())).findFirst().orElse(new PromotionPriceParamDTO()); + GoodsSkuPromotionPriceDTO goodsSkuPromotionPrice = new GoodsSkuPromotionPriceDTO(skus, tradeSku.getNum()); + // 商品原价总价 = 商品原价 * 数量 + goodsSkuPromotionPrice.setTotalOriginalPrice(CurrencyUtil.mul(goodsSkuPromotionPrice.getOriginalPrice(), tradeSku.getNum())); + // 获取当前商品所有参加的促销活动 + Map promotionMap = esGoodsIndex.getPromotionMap(); + // 是否计算拼团促销 + String pintuanId = tradeSku.getPintuanId() != null ? tradeSku.getPintuanId() : null; + // 如果商品促销列表存在促销活动 + this.calculationPromotionMap(promotionMap, goodsSkuPromotionPrice, esGoodsIndex, pintuanId); + + goodsSkuPromotionPrice.setTotalOriginalPrice(CurrencyUtil.mul(goodsSkuPromotionPrice.getOriginalPrice(), goodsSkuPromotionPrice.getNumber())); + goodsSkuPromotionPrice.setTotalPoints(CurrencyUtil.mul(goodsSkuPromotionPrice.getPoints(), goodsSkuPromotionPrice.getNumber())); + goodsSkuPromotionPrice.setTotalDiscountPrice(CurrencyUtil.mul(goodsSkuPromotionPrice.getDiscountPrice(), goodsSkuPromotionPrice.getNumber())); + goodsSkuPromotionPrice.setTotalFinalePrice(CurrencyUtil.mul(goodsSkuPromotionPrice.getFinalePrice(), goodsSkuPromotionPrice.getNumber())); + priceDTOList.add(goodsSkuPromotionPrice); + } + } + return priceDTOList; + } + + /** + * 促销计算(限时抢购,拼团) + * + * @param promotionMap 当前商品所有参加的促销活动 + * @param goodsSkuPromotionPrice 商品SKU促销计算信息 + * @param esGoodsIndex ES商品信息 + * @param pintuanId 拼团id,标示是否计算拼团活动 + */ + private void calculationPromotionMap(Map promotionMap, GoodsSkuPromotionPriceDTO goodsSkuPromotionPrice, EsGoodsIndex esGoodsIndex, String pintuanId) { + if (promotionMap != null && !promotionMap.isEmpty()) { + // 检查当前商品是否存在限时抢购活动 + Optional existSeckill = promotionMap.keySet().parallelStream().filter(i -> i.contains(PromotionTypeEnum.SECKILL.name())).findFirst(); + if (existSeckill.isPresent()) { + Seckill seckill = (Seckill) promotionMap.get(existSeckill.get()); + // 计算限时抢购促销 + this.calculationSeckill(seckill, goodsSkuPromotionPrice); + seckill.setPromotionName(PromotionTypeEnum.SECKILL.name()); + goodsSkuPromotionPrice.getJoinPromotion().add(seckill); + } + + // 检查当前商品是否存在拼团活动 + Optional existPintuan = promotionMap.keySet().parallelStream().filter(i -> i.contains(PromotionTypeEnum.PINTUAN.name())).findFirst(); + if (existPintuan.isPresent() && pintuanId != null) { + Pintuan pintuan = (Pintuan) promotionMap.get(existPintuan.get()); + // 优惠的总价格 = 原商品总价 - 优惠后的商品总价 + double discountPrice = CurrencyUtil.sub(goodsSkuPromotionPrice.getOriginalPrice(), esGoodsIndex.getPromotionPrice()); + goodsSkuPromotionPrice.setDiscountPrice(discountPrice); + goodsSkuPromotionPrice.setFinalePrice(esGoodsIndex.getPromotionPrice()); + pintuan.setPromotionName(PromotionTypeEnum.PINTUAN.name()); + goodsSkuPromotionPrice.getJoinPromotion().add(pintuan); + } + + // 检查当前商品是否存在满优惠活动 + Optional existFullDiscount = promotionMap.keySet().parallelStream().filter(i -> i.contains(PromotionTypeEnum.FULL_DISCOUNT.name())).findFirst(); + if (existFullDiscount.isPresent()) { + FullDiscount discount = (FullDiscount) promotionMap.get(existFullDiscount.get()); + goodsSkuPromotionPrice.setPromotionId(discount.getId()); + goodsSkuPromotionPrice.setPromotionType(PromotionTypeEnum.FULL_DISCOUNT.name()); + } + + + Optional existPointsGoods = promotionMap.keySet().parallelStream().filter(i -> i.contains(PromotionTypeEnum.POINTS_GOODS.name())).findFirst(); + if (existPointsGoods.isPresent()) { + PointsGoods pointsGoods = (PointsGoods) promotionMap.get(existPointsGoods.get()); + goodsSkuPromotionPrice.setPoints(pointsGoods.getPoints().doubleValue()); + } + } + } + + /** + * 优惠券计算 + * + * @param coupons 使用的优惠券列表 + * @param list 商品促销价格信息列表 + * @return 优惠券总金额 + */ + private double calculationCoupon(List coupons, List list) { + double couponTotalPrice = 0; + for (MemberCoupon coupon : coupons) { + if (CouponScopeTypeEnum.PORTION_GOODS.name().equals(coupon.getScopeType())) { + String[] scopeSkuIds = coupon.getScopeId().split(","); + List conformCollect = list.parallelStream().filter(i -> Arrays.asList(scopeSkuIds).contains(i.getSkuId())).collect(Collectors.toList()); + // 单个优惠券计算 + couponTotalPrice = this.calculationSingleCoupon(coupon, conformCollect); + } else if (CouponScopeTypeEnum.ALL.name().equals(coupon.getScopeType())) { + // 单个优惠券计算 + couponTotalPrice = this.calculationSingleCoupon(coupon, list); + } else if (CouponScopeTypeEnum.PORTION_GOODS_CATEGORY.name().equals(coupon.getScopeType()) || CouponScopeTypeEnum.PORTION_SHOP_CATEGORY.name().equals(coupon.getScopeType())) { + List collect = this.filterCoupon(coupon, list); + // 单个优惠券计算 + couponTotalPrice = this.calculationSingleCoupon(coupon, collect); + } + } + return couponTotalPrice; + } + + /** + * 过滤优惠券范围内分类 商品sku促销价格信息列表 + * + * @param coupon 会员优惠券信息 + * @param list 过滤的列表 + * @return 过滤后的商品价格信息列表 + */ + private List filterCoupon(MemberCoupon coupon, List list) { + String[] scopeCategories = coupon.getScopeId().split(","); + + // 过滤优惠券范围内分类 商品sku促销价格信息列表 + return list.parallelStream().filter(i -> { + String[] split; + if (CouponScopeTypeEnum.PORTION_GOODS_CATEGORY.name().equals(coupon.getScopeType())) { + split = i.getCategoryPath().split("\\|"); + } else { + split = i.getStoreCategoryPath().split("\\|"); + } + for (String s : split) { + if (Arrays.asList(scopeCategories).contains(s)) { + return true; + } + } + return false; + }).collect(Collectors.toList()); + } + + /** + * 单个优惠券计算 + * + * @param coupon 使用的优惠券信息 + * @param conformCollect 商品促销价格信息列表 + * @return 优惠券总金额 + */ + private double calculationSingleCoupon(MemberCoupon coupon, List conformCollect) { + double couponTotalPrice = 0; + // 合计优惠券范围内的所有商品的原价 + double totalPrice = conformCollect.parallelStream().mapToDouble(GoodsSkuPromotionPriceDTO::getOriginalPrice).sum(); + + double discountPrice = 0; + // 根据优惠券优惠类型,判断是否满足条件 + if (CouponTypeEnum.PRICE.name().equals(coupon.getCouponType()) && coupon.getConsumeThreshold() <= totalPrice) { + couponTotalPrice = CurrencyUtil.add(couponTotalPrice, coupon.getPrice()); + discountPrice = coupon.getPrice(); + } else if (CouponTypeEnum.DISCOUNT.name().equals(coupon.getCouponType())) { + double fullRate = coupon.getDiscount() >= 10 ? coupon.getDiscount() / 100 : coupon.getDiscount() / 10; + double discountRatePrice = CurrencyUtil.sub(totalPrice, CurrencyUtil.mul(totalPrice, fullRate)); + + couponTotalPrice = CurrencyUtil.add(couponTotalPrice, discountRatePrice); + discountPrice = discountRatePrice; + // 消费限额判断 +// if (coupon.getConsumeThreshold() >= discountRatePrice) { +// couponTotalPrice = CurrencyUtil.add(couponTotalPrice, discountRatePrice); +// discountPrice = discountRatePrice; +// } else { +// couponTotalPrice = CurrencyUtil.add(couponTotalPrice, coupon.getConsumeThreshold()); +// discountPrice = coupon.getConsumeThreshold(); +// } + } + + // 分配到每个商品的优惠券金额 + double distributePrice = CurrencyUtil.div(discountPrice, conformCollect.size()); + for (GoodsSkuPromotionPriceDTO goodsSkuPromotionPriceDTO : conformCollect) { + if (goodsSkuPromotionPriceDTO.getFinalePrice() != null) { + goodsSkuPromotionPriceDTO.setFinalePrice(CurrencyUtil.sub(goodsSkuPromotionPriceDTO.getFinalePrice(), distributePrice)); + } else { + goodsSkuPromotionPriceDTO.setFinalePrice(CurrencyUtil.sub(goodsSkuPromotionPriceDTO.getOriginalPrice(), distributePrice)); + } + goodsSkuPromotionPriceDTO.setCouponPrice(distributePrice); + goodsSkuPromotionPriceDTO.setTotalFinalePrice(CurrencyUtil.mul(goodsSkuPromotionPriceDTO.getFinalePrice(), goodsSkuPromotionPriceDTO.getNumber())); + BasePromotion basePromotion = new BasePromotion(); + basePromotion.setId(coupon.getId()); + if (coupon.getIsPlatform() != null && Boolean.TRUE.equals(coupon.getIsPlatform())) { + basePromotion.setPromotionName("platformCoupon"); + } else { + basePromotion.setPromotionName("storeCoupon"); + } + basePromotion.setStartTime(coupon.getStartTime()); + basePromotion.setEndTime(coupon.getEndTime()); + goodsSkuPromotionPriceDTO.getJoinPromotion().add(basePromotion); + } + return couponTotalPrice; + } + + /** + * 计算限时抢购 + * + * @param seckill 限时抢购信息 + * @param promotionPrice 商品SKU的计算促销信息 + */ + private void calculationSeckill(Seckill seckill, GoodsSkuPromotionPriceDTO promotionPrice) { + // 检查 活动有效时间 及 活动有效状态 + if (checkPromotionValidTime(seckill.getStartTime(), seckill.getEndTime(), PromotionTypeEnum.SECKILL.name(), seckill.getId()) && seckill.getPromotionStatus().equals(PromotionStatusEnum.START.name())) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SeckillApply::getSkuId, promotionPrice.getSkuId()).eq(SeckillApply::getSeckillId, seckill.getId()); + SeckillApply seckillApply = seckillApplyService.getOne(queryWrapper); + if (seckillApply.getPromotionApplyStatus().equals(PromotionApplyStatusEnum.PASS.name())) { + Integer quantity = promotionGoodsService.getPromotionGoodsStock(PromotionTypeEnum.SECKILL, seckill.getId(), seckillApply.getSkuId()); + int seckillTotalSaleNum = seckillApply.getSalesNum() + promotionPrice.getNumber(); + if (quantity >= seckillTotalSaleNum) { + // 优惠的价格 = 原商品价 - 优惠后的商品价 + double discountPrice = CurrencyUtil.sub(promotionPrice.getOriginalPrice(), seckillApply.getPrice()); + promotionPrice.setDiscountPrice(discountPrice); + promotionPrice.setFinalePrice(seckillApply.getPrice()); + } else { + log.error("购买数量超出限时抢购剩余数量"); + } + } + } + } + + /** + * 计算满优惠促销 + * + * @param fullDiscount 满优惠信息 + * @param storePromotionPriceDTO 店铺促销计算信息 + */ + private void calculationFullDiscount(FullDiscount fullDiscount, StorePromotionPriceDTO storePromotionPriceDTO) { + // 检查 活动有效时间 及 活动有效状态 + if (checkPromotionValidTime(fullDiscount.getStartTime(), fullDiscount.getEndTime(), PromotionTypeEnum.FULL_DISCOUNT.name(), fullDiscount.getId()) && fullDiscount.getPromotionStatus().equals(PromotionStatusEnum.START.name())) { + // 是否免运费 + if (Boolean.TRUE.equals(fullDiscount.getIsFreeFreight())) { + storePromotionPriceDTO.setIsFreeFreight(true); + } + List goodsSkuPromotionPriceList = storePromotionPriceDTO.getGoodsSkuPromotionPriceList(); + + // 参与活动的商品总数量 + this.accumulationGoodsSkuPromotionPrice(goodsSkuPromotionPriceList, storePromotionPriceDTO); + + // 参与优惠商品的成交金额是否满足满优惠条件 + if (fullDiscount.getFullMoney() <= storePromotionPriceDTO.getTotalJoinDiscountPrice()) { + // 判断是否为满减,反之则为满折扣 + if (Boolean.TRUE.equals(fullDiscount.getIsFullMinus())) { + // 优惠总金额 = 原优惠总金额 + 满优惠减免的金额 + storePromotionPriceDTO.setTotalDiscountPrice(CurrencyUtil.add(storePromotionPriceDTO.getTotalDiscountPrice(), fullDiscount.getFullMinus())); + } else if (Boolean.TRUE.equals(fullDiscount.getIsFullRate())) { + double fullRate = fullDiscount.getFullRate() >= 10 ? fullDiscount.getFullRate() / 100 : fullDiscount.getFullRate() / 10; + // 满优惠减免的金额 = 原参与优惠的总金额 * 满优惠折扣(百分比) + double discountPrice = CurrencyUtil.sub(storePromotionPriceDTO.getTotalJoinDiscountPrice(), CurrencyUtil.mul(storePromotionPriceDTO.getTotalJoinDiscountPrice(), fullRate)); + // 优惠总金额 = 原优惠总金额 + 满优惠减免的金额 + storePromotionPriceDTO.setTotalDiscountPrice(CurrencyUtil.add(storePromotionPriceDTO.getTotalDiscountPrice(), discountPrice)); + } + } + + // 将计算完的促销价格,平均分配到参与促销的每个商品上去 + this.distributeStoreFullDiscountPromotionPrice(storePromotionPriceDTO, fullDiscount); + } + } + + + /** + * 计算累加促销活动商品价格 + * + * @param goodsSkuPromotionPriceList 商品促销价格信息列表 + * @param storePromotionPriceDTO 店铺促销计算信息 + */ + private void accumulationGoodsSkuPromotionPrice(List goodsSkuPromotionPriceList, StorePromotionPriceDTO storePromotionPriceDTO) { + // 数据累加 + double totalNotJoinDiscountPrice = 0; + double totalJoinDiscountPrice = 0; + double totalDiscountPrice = 0; + + for (GoodsSkuPromotionPriceDTO goodsSkuPromotionPrice : goodsSkuPromotionPriceList) { + if (goodsSkuPromotionPrice.getPromotionId() != null && goodsSkuPromotionPrice.getPromotionType().equals(PromotionTypeEnum.FULL_DISCOUNT.name())) { + totalJoinDiscountPrice = CurrencyUtil.add(totalJoinDiscountPrice, goodsSkuPromotionPrice.getTotalFinalePrice()); + totalDiscountPrice = CurrencyUtil.add(totalDiscountPrice, goodsSkuPromotionPrice.getDiscountPrice()); + } else { + totalNotJoinDiscountPrice = CurrencyUtil.add(totalNotJoinDiscountPrice, goodsSkuPromotionPrice.getFinalePrice()); + } + } + storePromotionPriceDTO.setTotalDiscountPrice(totalDiscountPrice); + storePromotionPriceDTO.setTotalJoinDiscountPrice(totalJoinDiscountPrice); + storePromotionPriceDTO.setTotalNotJoinDiscountPrice(totalNotJoinDiscountPrice); + + for (GoodsSkuPromotionPriceDTO goodsSkuPromotionPriceDTO : goodsSkuPromotionPriceList) { + double fullDiscountRate = CurrencyUtil.div(goodsSkuPromotionPriceDTO.getTotalFinalePrice(), totalJoinDiscountPrice); + goodsSkuPromotionPriceDTO.setDiscountPriceRate(fullDiscountRate); + } + + } + + /** + * 计算累加促销活动商品价格其他信息 + * + * @param goodsSkuPromotionPriceList 商品促销价格信息列表 + * @param storePromotionPriceDTO 店铺促销计算信息 + */ + private void accumulationGoodsSkuPromotionOther(List goodsSkuPromotionPriceList, StorePromotionPriceDTO storePromotionPriceDTO) { + double totalWeight = 0; + double totalNum = 0D; + double totalOriginPrice = 0; + double totalDiscountPrice = 0; + double totalPoints = 0; + for (GoodsSkuPromotionPriceDTO goodsSkuPromotionPrice : goodsSkuPromotionPriceList) { + totalWeight = CurrencyUtil.add(totalWeight, goodsSkuPromotionPrice.getWeight()); + totalNum = CurrencyUtil.add(totalNum, goodsSkuPromotionPrice.getNumber()); + totalPoints = CurrencyUtil.add(totalPoints, goodsSkuPromotionPrice.getTotalPoints()); + totalOriginPrice = CurrencyUtil.add(totalOriginPrice, (goodsSkuPromotionPrice.getOriginalPrice() * goodsSkuPromotionPrice.getNumber())); + if (goodsSkuPromotionPrice.getTotalDiscountPrice() != null && goodsSkuPromotionPrice.getTotalDiscountPrice() > 0) { + totalDiscountPrice = CurrencyUtil.add(totalDiscountPrice, goodsSkuPromotionPrice.getTotalDiscountPrice()); + } + } + if (storePromotionPriceDTO.getTotalDiscountPrice() == null && totalDiscountPrice > 0) { + storePromotionPriceDTO.setTotalDiscountPrice(totalDiscountPrice); + } else if (storePromotionPriceDTO.getTotalDiscountPrice() == null) { + storePromotionPriceDTO.setTotalDiscountPrice(0d); + } + storePromotionPriceDTO.setTotalPoints(totalPoints); + storePromotionPriceDTO.setTotalNum((int) totalNum); + storePromotionPriceDTO.setTotalWeight(totalWeight); + storePromotionPriceDTO.setTotalOriginPrice(totalOriginPrice); + } + + + /** + * 将相应促销活动的店铺促销计算信息中的促销价格和最终成交金额平均分配到每个商品促销信息中去 + * + * @param storePromotionPriceDTO 店铺促销计算信息 + */ + private void distributeStoreFullDiscountPromotionPrice(StorePromotionPriceDTO storePromotionPriceDTO, FullDiscount fullDiscount) { + // 分配的促销总金额 = 促销总金额 / 分配的数量 + for (GoodsSkuPromotionPriceDTO goodsSkuPromotionPrice : storePromotionPriceDTO.getGoodsSkuPromotionPriceList()) { + // 属于相应的促销活动的商品金额均分 + if (goodsSkuPromotionPrice.getDiscountPriceRate() != null + && goodsSkuPromotionPrice.getPromotionType() != null + && goodsSkuPromotionPrice.getPromotionType().equals(PromotionTypeEnum.FULL_DISCOUNT.name())) { + double distributeDiscountTotalPrice = CurrencyUtil.mul(storePromotionPriceDTO.getTotalDiscountPrice(), goodsSkuPromotionPrice.getDiscountPriceRate()); + goodsSkuPromotionPrice.setDiscountPrice(distributeDiscountTotalPrice); + goodsSkuPromotionPrice.setTotalDiscountPrice(distributeDiscountTotalPrice); + // 单品成交价 + double finalPrice = CurrencyUtil.sub(goodsSkuPromotionPrice.getTotalOriginalPrice(), distributeDiscountTotalPrice); + goodsSkuPromotionPrice.setFinalePrice(finalPrice); + goodsSkuPromotionPrice.setTotalFinalePrice(CurrencyUtil.mul(finalPrice, goodsSkuPromotionPrice.getNumber())); + fullDiscount.setPromotionName(PromotionTypeEnum.FULL_DISCOUNT.name()); + goodsSkuPromotionPrice.getJoinPromotion().add(fullDiscount); + + } + } + } + + /** + * 检查活动有效时间 + * + * @param startTime 活动开始时间 + * @param endTime 活动结束时间 + * @param promotionType 活动类型 + * @param promotionId 活动ID + * @return 是否有效 + */ + private boolean checkPromotionValidTime(Date startTime, Date endTime, String promotionType, String promotionId) { + long now = System.currentTimeMillis(); + if (startTime.getTime() > now) { + log.error("商品ID为{}的{}活动开始时间小于当时时间,活动未开始!", promotionId, promotionType); + return false; + } + if (endTime.getTime() < now) { + log.error("活动ID为{}的{}活动结束时间大于当时时间,活动已结束!", promotionId, promotionType); + return false; + } + return true; + } +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PromotionServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PromotionServiceImpl.java new file mode 100644 index 00000000..3ba9289a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/PromotionServiceImpl.java @@ -0,0 +1,399 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.delayqueue.PromotionMessage; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.DateUtil; +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import cn.lili.modules.promotion.entity.dos.*; +import cn.lili.modules.promotion.entity.enums.*; +import cn.lili.modules.promotion.entity.vos.CouponVO; +import cn.lili.modules.promotion.entity.vos.PintuanVO; +import cn.lili.modules.promotion.entity.vos.PointsGoodsVO; +import cn.lili.modules.promotion.entity.vos.SeckillVO; +import cn.lili.modules.promotion.service.*; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.service.EsGoodsIndexService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +/** + * 促销业务层实现 + * + * @author Chopper + * @date 2020/8/21 + */ +@Slf4j +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PromotionServiceImpl implements PromotionService { + //会员优惠券 + private MemberCouponService memberCouponService; + //秒杀 + private SeckillService seckillService; + //秒杀申请 + private SeckillApplyService seckillApplyService; + //满额活动 + private FullDiscountService fullDiscountService; + //拼团 + private PintuanService pintuanService; + //优惠券 + private CouponService couponService; + //促销商品 + private PromotionGoodsService promotionGoodsService; + //积分商品 + private PointsGoodsService pointsGoodsService; + //ES商品 + private EsGoodsIndexService goodsIndexService; + //Mongo + private MongoTemplate mongoTemplate; + + + @Override + public boolean updatePromotionStatus(PromotionMessage promotionMessage) { + PromotionTypeEnum promotionTypeEnum = PromotionTypeEnum.valueOf(promotionMessage.getPromotionType()); + String esPromotionKey = promotionTypeEnum + "-" + promotionMessage.getPromotionId(); + log.info("更新促销活动状态:{}", promotionMessage); + boolean result = false; + switch (promotionTypeEnum) { + case FULL_DISCOUNT: + result = this.updateFullDiscount(promotionMessage, esPromotionKey, promotionTypeEnum); + break; + case SECKILL: + SeckillVO seckill = this.mongoTemplate.findById(promotionMessage.getPromotionId(), SeckillVO.class); + if (seckill == null) { + this.throwPromotionException(promotionTypeEnum, promotionMessage.getPromotionId(), promotionMessage.getPromotionStatus()); + } + seckill.setPromotionStatus(promotionMessage.getPromotionStatus()); + result = this.seckillService.update(promotionMessage.updateWrapper()); + for (SeckillApply seckillApply : seckill.getSeckillApplyList()) { + if (seckillApply.getPromotionApplyStatus().equals(PromotionApplyStatusEnum.PASS.name())) { + // 下一个时间,默认为当天结束时间 + int nextHour = 23; + String[] split = seckill.getHours().split(","); + int[] hoursSored = Arrays.stream(split).mapToInt(Integer::parseInt).toArray(); + // 排序时间段 + Arrays.sort(hoursSored); + for (int i : hoursSored) { + // 如果当前时间段大于排序后的时间段的某个,当前时间段的下个时间段即为排序后的时间段的某个 + if (seckillApply.getTimeLine() < i) { + nextHour = i; + break; + } + } + Seckill seckill1 = JSONUtil.toBean(JSONUtil.toJsonStr(seckill), Seckill.class); + String format = cn.hutool.core.date.DateUtil.format(seckill.getStartTime(), DateUtil.STANDARD_DATE_FORMAT); + DateTime parseStartTime = cn.hutool.core.date.DateUtil.parse((format + " " + seckillApply.getTimeLine()), "yyyy-MM-dd HH"); + DateTime parseEndTime = cn.hutool.core.date.DateUtil.parse((format + " " + nextHour), "yyyy-MM-dd HH"); + // 如果是当天最后的时间段则设置到当天结束时间的59分59秒 + if (nextHour == seckillApply.getTimeLine()) { + parseEndTime = cn.hutool.core.date.DateUtil.parse((format + " " + nextHour + ":59:59"), DateUtil.STANDARD_FORMAT); + } + seckill1.setStartTime(parseStartTime); + // 当时商品的限时抢购活动结束时间为下个时间段的开始 + seckill1.setEndTime(parseEndTime); + this.goodsIndexService.updateEsGoodsIndex(seckillApply.getSkuId(), seckill1, promotionTypeEnum.name() + "-" + seckillApply.getTimeLine(), seckillApply.getPrice()); + } + } + this.mongoTemplate.save(seckill); + break; + case PINTUAN: + PintuanVO pintuanVO = this.mongoTemplate.findById(promotionMessage.getPromotionId(), PintuanVO.class); + if (pintuanVO == null) { + this.throwPromotionException(promotionTypeEnum, promotionMessage.getPromotionId(), promotionMessage.getPromotionStatus()); + } + pintuanVO.setPromotionStatus(promotionMessage.getPromotionStatus()); + result = this.pintuanService.update(promotionMessage.updateWrapper()); + this.promotionGoodsService.updateBatchById(pintuanVO.getPromotionGoodsList()); + List promotionGoodsList = pintuanVO.getPromotionGoodsList(); + // 更新促销商品索引 + for (PromotionGoods promotionGoods : promotionGoodsList) { + Pintuan pintuan1 = JSONUtil.toBean(JSONUtil.toJsonStr(pintuanVO), Pintuan.class); + this.goodsIndexService.updateEsGoodsIndex(promotionGoods.getSkuId(), pintuan1, esPromotionKey, promotionGoods.getPrice()); + } + this.mongoTemplate.save(pintuanVO); + break; + case COUPON: + result = this.updateCoupon(promotionMessage, esPromotionKey, promotionTypeEnum); + break; + case POINTS_GOODS: + PointsGoodsVO pointsGoodsVO = this.mongoTemplate.findById(promotionMessage.getPromotionId(), PointsGoodsVO.class); + if (pointsGoodsVO == null) { + this.throwPromotionException(promotionTypeEnum, promotionMessage.getPromotionId(), promotionMessage.getPromotionStatus()); + } + pointsGoodsVO.setPromotionStatus(promotionMessage.getPromotionStatus()); + result = this.pointsGoodsService.update(promotionMessage.updateWrapper()); + PointsGoods pointsGoods = JSONUtil.toBean(JSONUtil.toJsonStr(pointsGoodsVO), PointsGoods.class); + this.goodsIndexService.updateEsGoodsIndex(pointsGoodsVO.getSkuId(), pointsGoods, esPromotionKey, null); + this.mongoTemplate.save(pointsGoodsVO); + break; + default: + break; + } + this.updatePromotionGoods(promotionMessage.getPromotionId(), promotionMessage.getPromotionStatus()); + return result; + } + + /** + * 获取当前进行的所有促销活动信息 + * + * @return 当前促销活动集合 + */ + @Override + public Map getCurrentPromotion() { + Map resultMap = new HashMap<>(); + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("promotion_status", PromotionStatusEnum.START.name()); + queryWrapper.gt("start_time", new Date()); + queryWrapper.lt("end_time", new Date()); + // 获取当前进行的限时抢购活动 + List seckillList = seckillService.list(queryWrapper); + if (seckillList != null && !seckillList.isEmpty()) { + for (Seckill seckill : seckillList) { + resultMap.put(PromotionTypeEnum.SECKILL.name(), seckill); + } + } + // 获取当前进行的满优惠活动 + List fullDiscountList = fullDiscountService.list(queryWrapper); + if (fullDiscountList != null && !fullDiscountList.isEmpty()) { + for (FullDiscount fullDiscount : fullDiscountList) { + resultMap.put(PromotionTypeEnum.FULL_DISCOUNT.name(), fullDiscount); + } + } + // 获取当前进行的拼团活动 + List pintuanList = pintuanService.list(queryWrapper); + if (pintuanList != null && !pintuanList.isEmpty()) { + for (Pintuan pintuan : pintuanList) { + resultMap.put(PromotionTypeEnum.PINTUAN.name(), pintuan); + } + } + return resultMap; + } + + /** + * 根据商品索引获取当前商品索引的所有促销活动信息 + * + * @param index 商品索引 + * @return 当前促销活动集合 + */ + @Override + public Map getGoodsCurrentPromotionMap(EsGoodsIndex index) { + Map promotionMap = new HashMap<>(); + Query query = new Query(); + query.addCriteria(Criteria.where("deleteFlag").is(false)); + query.addCriteria(Criteria.where("promotionStatus").is(PromotionStatusEnum.START.name())); + query.addCriteria(Criteria.where("endTime").gt(new Date())); + List fullDiscountVOS = mongoTemplate.find(query, FullDiscountVO.class); + for (FullDiscountVO fullDiscountVO : fullDiscountVOS) { + if (fullDiscountVO.getPromotionGoodsList() == null && fullDiscountVO.getNumber() == -1) { + if (index.getStoreId().equals(fullDiscountVO.getStoreId())) { + String fullDiscountKey = PromotionTypeEnum.FULL_DISCOUNT.name() + "-" + fullDiscountVO.getId(); + promotionMap.put(fullDiscountKey, fullDiscountVO); + } + } + } + List couponVOS = mongoTemplate.find(query, CouponVO.class); + for (CouponVO couponVO : couponVOS) { + if (couponVO.getPromotionGoodsList() == null && couponVO.getScopeType().equals(CouponScopeTypeEnum.ALL.name())) { + if (couponVO.getStoreId().equals("platform") || index.getStoreId().equals(couponVO.getStoreId())) { + String couponKey = PromotionTypeEnum.COUPON.name() + "-" + couponVO.getId(); + promotionMap.put(couponKey, couponVO); + } + } + } + LambdaQueryWrapper queryWrapper1 = new LambdaQueryWrapper<>(); + queryWrapper1.eq(PromotionGoods::getDeleteFlag, false); + queryWrapper1.eq(PromotionGoods::getPromotionStatus, PromotionStatusEnum.START.name()); + queryWrapper1.gt(PromotionGoods::getEndTime, new Date()); + queryWrapper1.eq(PromotionGoods::getSkuId, index.getId()); + List list1 = promotionGoodsService.list(queryWrapper1); + for (PromotionGoods promotionGoods : list1) { + String esPromotionKey = promotionGoods.getPromotionType() + "-" + promotionGoods.getPromotionId(); + switch (PromotionTypeEnum.valueOf(promotionGoods.getPromotionType())) { + case COUPON: + Coupon coupon = couponService.getById(promotionGoods.getPromotionId()); + promotionMap.put(esPromotionKey, coupon); + break; + case PINTUAN: + Pintuan pintuan = pintuanService.getById(promotionGoods.getPromotionId()); + promotionMap.put(esPromotionKey, pintuan); + index.setPromotionPrice(promotionGoods.getPrice()); + break; + case FULL_DISCOUNT: + FullDiscount fullDiscount = fullDiscountService.getById(promotionGoods.getPromotionId()); + promotionMap.put(esPromotionKey, fullDiscount); + break; + case SECKILL: + Seckill seckill = seckillService.getById(promotionGoods.getPromotionId()); + LambdaQueryWrapper seckillApplyLambdaQueryWrapper = new LambdaQueryWrapper<>(); + seckillApplyLambdaQueryWrapper.eq(SeckillApply::getSeckillId, promotionGoods.getPromotionId()); + seckillApplyLambdaQueryWrapper.eq(SeckillApply::getSkuId, promotionGoods.getSkuId()); + SeckillApply seckillApply = seckillApplyService.getOne(seckillApplyLambdaQueryWrapper); + int nextHour = 23; + String[] split = seckill.getHours().split(","); + int[] hoursSored = Arrays.stream(split).mapToInt(Integer::parseInt).toArray(); + Arrays.sort(hoursSored); + for (int i : hoursSored) { + if (seckillApply.getTimeLine() < i) { + nextHour = i; + } + } + String seckillKey = promotionGoods.getPromotionType() + "-" + nextHour; + seckill.setStartTime(promotionGoods.getStartTime()); + seckill.setEndTime(promotionGoods.getEndTime()); + promotionMap.put(seckillKey, seckill); + index.setPromotionPrice(promotionGoods.getPrice()); + break; + case POINTS_GOODS: + PointsGoods pointsGoods = pointsGoodsService.getById(promotionGoods.getPromotionId()); + promotionMap.put(esPromotionKey, pointsGoods); + break; + default: + break; + } + } + return promotionMap; + } + + private boolean updateFullDiscount(PromotionMessage promotionMessage, String esPromotionKey, PromotionTypeEnum promotionTypeEnum) { + boolean result; + FullDiscountVO fullDiscountVO = mongoTemplate.findById(promotionMessage.getPromotionId(), FullDiscountVO.class); + if (fullDiscountVO == null) { + this.throwPromotionException(promotionTypeEnum, promotionMessage.getPromotionId(), promotionMessage.getPromotionStatus()); + } + fullDiscountVO.setPromotionStatus(promotionMessage.getPromotionStatus()); + result = this.fullDiscountService.update(promotionMessage.updateWrapper()); + // clone一个活动信息,用于存放与索引中 + FullDiscountVO clone = ObjectUtil.clone(fullDiscountVO); + clone.setPromotionGoodsList(null); + if (fullDiscountVO.getPromotionGoodsList() == null && fullDiscountVO.getNumber() == -1) { + // 如果为全品类则更新全部索引 + this.goodsIndexService.updateEsGoodsIndexAllByList(clone, esPromotionKey); + } else { + // 如不为全品类,更新指定索引 + for (PromotionGoods promotionGoods : fullDiscountVO.getPromotionGoodsList()) { + promotionGoods.setPromotionStatus(promotionMessage.getPromotionStatus()); + } + this.promotionGoodsService.updateBatchById(fullDiscountVO.getPromotionGoodsList()); + this.goodsIndexService.updateEsGoodsIndexByList(fullDiscountVO.getPromotionGoodsList(), clone, esPromotionKey); + } + this.mongoTemplate.save(fullDiscountVO); + return result; + } + + private boolean updateCoupon(PromotionMessage promotionMessage, String esPromotionKey, PromotionTypeEnum promotionTypeEnum) { + boolean result; + CouponVO couponVO = this.mongoTemplate.findById(promotionMessage.getPromotionId(), CouponVO.class); + if (couponVO == null) { + this.throwPromotionException(promotionTypeEnum, promotionMessage.getPromotionId(), promotionMessage.getPromotionStatus()); + } + couponVO.setPromotionStatus(promotionMessage.getPromotionStatus()); + result = this.couponService.update(promotionMessage.updateWrapper()); + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper().eq(MemberCoupon::getCouponId, couponVO.getId()).set(MemberCoupon::getMemberCouponStatus, MemberCouponStatusEnum.EXPIRE.name()); + this.memberCouponService.update(updateWrapper); + // clone一个活动信息,用于存放与索引中 + CouponVO clone = ObjectUtil.clone(couponVO); + clone.setPromotionGoodsList(null); + if (CouponScopeTypeEnum.PORTION_GOODS.name().equals(couponVO.getScopeType())) { + // 如为部分商品,则更新部分商品索引 + this.promotionGoodsService.updateBatchById(couponVO.getPromotionGoodsList()); + this.goodsIndexService.updateEsGoodsIndexByList(couponVO.getPromotionGoodsList(), clone, esPromotionKey); + } else if (CouponScopeTypeEnum.ALL.name().equals(couponVO.getScopeType())) { + // 如为全部,则更新全部商品索引 + this.goodsIndexService.updateEsGoodsIndexAllByList(clone, esPromotionKey); + } + this.mongoTemplate.save(couponVO); + return result; + } + + /** + * 更新促销商品信息 + * + * @param promotionId + * @param promotionStatus + */ + private void updatePromotionGoods(String promotionId, String promotionStatus) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(PromotionGoods::getPromotionId, promotionId).set(PromotionGoods::getPromotionStatus, promotionStatus); + this.promotionGoodsService.update(updateWrapper); + } + + + /** + * 抛出促销异常 + * + * @param type 促销类型 + * @param id 促销编号 + * @param status 促销状态 + */ + private void throwPromotionException(PromotionTypeEnum type, String id, String status) { + log.error("当前" + type.name() + "活动ID为[" + id + "] 不存在,更改活动状态至[ " + status + " ]失败!"); + throw new ServiceException("当前活动已停止"); + } + + + @Autowired + public void setPintuanService(PintuanService pintuanService) { + this.pintuanService = pintuanService; + } + + @Autowired + public void setMemberCouponService(MemberCouponService memberCouponService) { + this.memberCouponService = memberCouponService; + } + + @Autowired + public void setSeckillService(SeckillService seckillService) { + this.seckillService = seckillService; + } + + @Autowired + public void setSeckillApplyService(SeckillApplyService seckillApplyService) { + this.seckillApplyService = seckillApplyService; + } + + @Autowired + public void setFullDiscountService(FullDiscountService fullDiscountService) { + this.fullDiscountService = fullDiscountService; + } + + @Autowired + public void setCouponService(CouponService couponService) { + this.couponService = couponService; + } + + @Autowired + public void setPromotionGoodsService(PromotionGoodsService promotionGoodsService) { + this.promotionGoodsService = promotionGoodsService; + } + + @Autowired + public void setPointsGoodsService(PointsGoodsService pointsGoodsService) { + this.pointsGoodsService = pointsGoodsService; + } + + @Autowired + public void setGoodsIndexService(EsGoodsIndexService goodsIndexService) { + this.goodsIndexService = goodsIndexService; + } + + @Autowired + public void setMongoTemplate(MongoTemplate mongoTemplate) { + this.mongoTemplate = mongoTemplate; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/SeckillApplyServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/SeckillApplyServiceImpl.java new file mode 100644 index 00000000..e3402bd9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/SeckillApplyServiceImpl.java @@ -0,0 +1,557 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.dos.Seckill; +import cn.lili.modules.promotion.entity.dos.SeckillApply; +import cn.lili.modules.promotion.entity.enums.PromotionApplyStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.*; +import cn.lili.modules.promotion.mapper.SeckillApplyMapper; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import cn.lili.modules.promotion.service.SeckillApplyService; +import cn.lili.modules.promotion.service.SeckillService; +import cn.lili.modules.promotion.tools.PromotionCacheKeys; +import cn.lili.modules.promotion.tools.PromotionTools; +import cn.lili.modules.search.service.EsGoodsIndexService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 秒杀申请业务层实现 + * + * @author Chopper + * @date 2020/8/21 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SeckillApplyServiceImpl extends ServiceImpl implements SeckillApplyService { + + //缓存 + private final Cache cache; + //Mongo + private final MongoTemplate mongoTemplate; + //规格商品 + private GoodsSkuService goodsSkuService; + //ES商品 + private EsGoodsIndexService esGoodsIndexService; + //促销商品 + private PromotionGoodsService promotionGoodsService; + //秒杀 + private SeckillService seckillService; + + @Override + public List getSeckillTimeline() { + List timelineVoS = new ArrayList<>(); + // 限时抢购缓存key + String seckillCacheKey = PromotionCacheKeys.getSeckillTimelineKey(DateUtil.toString(DateUtil.startOfTodDay(), DateUtil.STANDARD_DATE_NO_UNDERLINE_FORMAT)); + Map cacheSeckill = cache.getHash(seckillCacheKey); + if (cacheSeckill == null || cacheSeckill.isEmpty()) { + // 如缓存中不存在,则单独获取 + try { + timelineVoS = getSeckillTimelineToCache(seckillCacheKey); + } catch (Exception e) { + log.error("获取限时抢购信息发生错误!", e); + } + } else { + // 如缓存中存在,则取缓存中转为展示的信息 + for (Object value : cacheSeckill.values()) { + SeckillTimelineVO seckillTimelineVO = (SeckillTimelineVO) value; + timelineVoS.add(seckillTimelineVO); + } + } + return timelineVoS; + } + + @Override + public List getSeckillGoods(Integer timeline) { + List seckillGoodsVoS = new ArrayList<>(); + // 限时抢购缓存key + String seckillCacheKey = PromotionCacheKeys.getSeckillTimelineKey(DateUtil.toString(DateUtil.startOfTodDay(), DateUtil.STANDARD_DATE_NO_UNDERLINE_FORMAT)); + Map cacheSeckill = cache.getHash(seckillCacheKey); + if (cacheSeckill == null || cacheSeckill.isEmpty()) { + // 如缓存中不存在,则单独获取 + seckillGoodsVoS = wrapperSeckillGoods(timeline); + } else { + // 如缓存中存在,则取缓存中转为展示的信息 + for (Map.Entry entry : cacheSeckill.entrySet()) { + Integer timelineKey = Integer.parseInt(entry.getKey().toString()); + if (timelineKey.equals(timeline)) { + seckillGoodsVoS = (List) entry.getValue(); + } + } + } + return seckillGoodsVoS; + } + + @Override + public void auditBatchApply(String[] ids, String seckillId, String applyStatus, String failReason) { + if (ids == null || ids.length <= 0) { + throw new ServiceException("请提供要审核的商品"); + } + if (StringUtils.isEmpty(applyStatus) || PromotionApplyStatusEnum.valueOf(applyStatus).name().isEmpty()) { + throw new ServiceException("提供的审批状态值不正确"); + } + if (PromotionApplyStatusEnum.REFUSE.name().equals(applyStatus)) { + boolean isEmpty = StringUtils.isEmpty(failReason); + if (isEmpty) { + throw new ServiceException("在驳回状态下驳回原因必填"); + } + } + + SeckillVO seckillVO = this.mongoTemplate.findById(seckillId, SeckillVO.class); + if (seckillVO == null) { + log.error("编号为【" + seckillId + "】限时抢购请不存在"); + throw new ServiceException(); + } + List seckillApplyList = seckillVO.getSeckillApplyList(); + + for (String id : ids) { + + Optional seckillApplyOptional = seckillApplyList.stream().filter(i -> i.getId().equals(id)).findFirst(); + SeckillApply seckillApply; + if (seckillApplyOptional.isPresent()) { + seckillApply = seckillApplyOptional.get(); + } else { + log.error("编号为【" + id + "】限时抢购的申请不存在"); + throw new ServiceException(); + } + + seckillApply.setPromotionApplyStatus(PromotionApplyStatusEnum.valueOf(applyStatus).name()); + seckillApply.setFailReason(failReason); + this.updateById(seckillApply); + if (PromotionApplyStatusEnum.PASS.name().equals(applyStatus)) { + //检查缓存中是否存在相同商品参与的限时抢购活动 + checkCache(seckillVO.getStartTime().getTime()); + } + } + seckillVO.setSeckillApplyList(seckillApplyList); + mongoTemplate.save(seckillVO); + } + + @Override + public IPage getSeckillApplyFromMysql(SeckillSearchParams queryParam, PageVO pageVo) { + QueryWrapper queryWrapper = queryParam.wrapper(); + return page(PageUtil.initPage(pageVo), queryWrapper); + } + + @Override + public IPage getSeckillApplyFromMongo(SeckillSearchParams queryParam, PageVO pageVo) { + IPage seckillApplyIPage = new Page<>(); + Query query = queryParam.mongoQuery(); + + SeckillVO seckillVO = this.mongoTemplate.findOne(query, SeckillVO.class); + if (seckillVO != null && pageVo != null) { + seckillApplyIPage.setCurrent(pageVo.getMongoPageNumber()); + seckillApplyIPage.setSize(pageVo.getPageSize()); + List seckillApplyList = seckillVO.getSeckillApplyList() != null ? seckillVO.getSeckillApplyList() : new ArrayList<>(); + for (SeckillApply seckillApply : seckillApplyList) { + if (CharSequenceUtil.isNotEmpty(queryParam.getStoreId()) && !seckillApply.getStoreId().equals(queryParam.getStoreId())) { + seckillApplyList.remove(seckillApply); + } + try { + Integer goodsStock = promotionGoodsService.getPromotionGoodsStock(PromotionTypeEnum.SECKILL, seckillApply.getSeckillId(), seckillApply.getSkuId()); + seckillApply.setQuantity(goodsStock); + } catch (Exception e) { + log.error("获取促销商品促销失败!", e); + } + } + seckillApplyIPage.setTotal(seckillApplyList.size()); + List page = CollUtil.page(pageVo.getMongoPageNumber(), pageVo.getPageSize(), seckillApplyList); + seckillApplyIPage.setRecords(page); + return seckillApplyIPage; + } else { + return null; + } + } + + @Override + public void addSeckillApply(String seckillId, String storeId, List seckillApplyList) { + SeckillVO seckill = mongoTemplate.findById(seckillId, SeckillVO.class); + if (seckill == null) { + throw new ServiceException("当前参与的限时抢购不存在!"); + } + seckill.checkTime(); + // 检查限时抢购申请是否合法 + checkSeckillApplyList(seckill.getApplyEndTime().getTime(), seckill.getHours(), seckillApplyList, storeId); + String storeIds = seckill.getStoreIds() != null ? seckill.getStoreIds() : ""; + boolean containsStore = false; + List storeIdList = Arrays.asList(storeIds.split(",")); + // 检查是否为已参加活动的店铺 + if (CharSequenceUtil.isNotEmpty(seckillId) && !storeIdList.contains(storeId)) { + if (!CharSequenceUtil.isEmpty(storeIds)) { + String[] storeIdSplit = storeIds.split(","); + for (String s : storeIdSplit) { + if (s.equals(seckillId)) { + containsStore = true; + break; + } + } + storeIds = seckill.getStoreIds() + storeId + ","; + } else { + storeIds = storeId + ","; + } + seckill.setStoreIds(storeIds); + } + + List originList = seckill.getSeckillApplyList(); + List promotionGoodsList = new ArrayList<>(); + if (originList == null) { + originList = new ArrayList<>(); + } + for (SeckillApplyVO seckillApply : seckillApplyList) { + GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(seckillApply.getSkuId()); + if (goodsSku.getQuantity() < seckillApply.getQuantity()) { + throw new ServiceException(seckillApply.getGoodsName() + ",此商品库存不足"); + } + + /* + *************两种情况:****************** + 团购时间段: |________________| + 秒杀时间段: |_____| |_______| + + ************第三种情况:****************** + 团购时间段: |______| + 秒杀时间段: |________________| + + ************第四种情况:****************** + 团购时间段: |________________| + 秒杀时间段: |______| + + 这个商品的开始时间计算要用他参与的时间段来计算,结束时间是当天晚上23:59:59 + */ + String startTimeStr = DateUtil.toString(seckill.getStartTime(), DateUtil.STANDARD_DATE_FORMAT) + " " + (seckillApply.getTimeLine() > 10 ? ("0" + seckillApply.getTimeLine()) : seckillApply.getTimeLine()) + ":00:00"; + String endTimeStr = DateUtil.toString(seckill.getStartTime(), "yyyy-MM-dd") + " 23:59:59"; + + // 查询是否在同一时间段参与了拼团活动 + Integer count = promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.PINTUAN.name(), goodsSku.getId(), DateUtil.toDate(startTimeStr, DateUtil.STANDARD_FORMAT), DateUtil.toDate(endTimeStr, DateUtil.STANDARD_FORMAT)); + // 查询是否在同一时间段参与了限时抢购活动 + count += promotionGoodsService.findInnerOverlapPromotionGoods(PromotionTypeEnum.SECKILL.name(), goodsSku.getId(), DateUtil.toDate(startTimeStr, DateUtil.STANDARD_FORMAT), DateUtil.toDate(endTimeStr, DateUtil.STANDARD_FORMAT)); + if (count > 0) { + throw new ServiceException("商品[" + goodsSku.getGoodsName() + "]已经在重叠的时间段参加了团购或限时抢购活动,不能参加限时抢购活动"); + } + seckillApply.setOriginalPrice(goodsSku.getPrice()); + seckillApply.setPromotionApplyStatus(PromotionApplyStatusEnum.APPLY.name()); + seckillApply.setSalesNum(0); + + Optional first = originList.stream().filter(i -> i.getSkuId().equals(seckillApply.getSkuId())).findFirst(); + if (first.isPresent() && (first.get().getPromotionApplyStatus().equals(PromotionApplyStatusEnum.REFUSE.name()) || first.get().getPromotionApplyStatus().equals(PromotionApplyStatusEnum.APPLY.name()))) { + originList = originList.stream().filter(i -> !i.getSkuId().equals(seckillApply.getSkuId())).collect(Collectors.toList()); + } else if (first.isPresent() && first.get().getPromotionApplyStatus().equals(PromotionApplyStatusEnum.PASS.name())) { + continue; + } + originList.add(seckillApply); + PromotionGoods promotionGoods = new PromotionGoods(goodsSku); + promotionGoods.setPrice(seckillApply.getPrice()); + promotionGoods.setQuantity(seckillApply.getQuantity()); + // 设置单独每个促销商品的结束时间 + int nextHour = 23; + String[] split = seckill.getHours().split(","); + int[] hoursSored = Arrays.stream(split).mapToInt(Integer::parseInt).toArray(); + Arrays.sort(hoursSored); + for (int i : hoursSored) { + if (seckillApply.getTimeLine() < i) { + nextHour = i; + break; + } + } + String format = cn.hutool.core.date.DateUtil.format(seckill.getStartTime(), DateUtil.STANDARD_DATE_FORMAT); + DateTime parseStartTime = cn.hutool.core.date.DateUtil.parse((format + " " + seckillApply.getTimeLine()), "yyyy-MM-dd HH"); + DateTime parseEndTime = cn.hutool.core.date.DateUtil.parse((format + " " + nextHour), "yyyy-MM-dd HH"); + // 如果是当天最后的时间段则设置到当天结束时间的59分59秒 + if (nextHour == seckillApply.getTimeLine()) { + parseEndTime = cn.hutool.core.date.DateUtil.parse((format + " " + nextHour + ":59:59"), DateUtil.STANDARD_FORMAT); + } + promotionGoods.setStartTime(parseStartTime); + promotionGoods.setEndTime(parseEndTime); + promotionGoodsList.add(promotionGoods); + } + this.saveOrUpdateBatch(originList); + seckill.setSeckillApplyList(originList); + this.mongoTemplate.save(seckill); + if (!promotionGoodsList.isEmpty()) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.in(PromotionGoods::getSkuId, promotionGoodsList.stream().map(PromotionGoods::getSkuId).collect(Collectors.toList())).eq(PromotionGoods::getStoreId, storeId); + promotionGoodsService.remove(queryWrapper); + PromotionTools.promotionGoodsInit(promotionGoodsList, seckill, PromotionTypeEnum.SECKILL); + promotionGoodsService.saveBatch(promotionGoodsList); + } + + if (Boolean.FALSE.equals(containsStore)) { + seckillService.storeApply(storeId, seckill.getId()); + } + } + + /** + * 批量删除限时抢购申请 + * + * @param seckillId 限时抢购活动id + * @param ids 限时抢购申请id集合 + */ + @Override + public void removeSeckillApplyByIds(String seckillId, List ids) { + SeckillVO seckillVO = this.mongoTemplate.findById(seckillId, SeckillVO.class); + if (seckillVO == null) { + throw new ServiceException("当前限时抢购活动不存在!"); + } + if (seckillVO.getPromotionStatus().equals(PromotionStatusEnum.START.name())) { + throw new ServiceException("当前限时抢购活动已经开始,无法修改!"); + } + seckillVO.getSeckillApplyList().removeIf(seckillApply -> ids.contains(seckillApply.getId())); + this.mongoTemplate.save(seckillVO); + this.removeByIds(ids); + } + + /** + * 更新限时抢购库存数量 + * + * @param id 限时抢购申请(限时抢购商品)id + * @param num 数量 + * @return 是否成功 + */ + @Override + public boolean updateSeckillStock(String id, Integer num) { + List promotionIds = this.esGoodsIndexService.getPromotionIdByPromotionType(id, PromotionTypeEnum.SECKILL); + for (String promotionId : promotionIds) { + SeckillVO seckillVO = this.mongoTemplate.findById(promotionId, SeckillVO.class); + if (seckillVO == null) { + log.error("限时抢购活动id为" + promotionId + "的限时抢购活动不存在!"); + break; + } + if (seckillVO.getSeckillApplyList() != null && !seckillVO.getSeckillApplyList().isEmpty()) { + Optional seckillApplyOptional = seckillVO.getSeckillApplyList().stream().filter(i -> i.getSkuId().equals(id)).findFirst(); + if (seckillApplyOptional.isPresent()) { + SeckillApply seckillApply = seckillApplyOptional.get(); + // 设置售卖数量 + Integer countNum = seckillApply.getSalesNum() + num; + seckillApply.setSalesNum(countNum); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SeckillApply::getId, seckillApply.getId()); + updateWrapper.set(SeckillApply::getQuantity, seckillApply.getQuantity() - num); + updateWrapper.set(SeckillApply::getSalesNum, countNum); + this.update(updateWrapper); + this.mongoTemplate.save(seckillVO); + } + } + } + return true; + } + + /** + * 更新限时抢购库存数量 + * + * @param map key 为 限时抢购申请(限时抢购商品)id, value 为数量 + * @return 是否成功 + */ + @Override + public boolean updateSeckillStock(Map map) { + boolean result = false; + for (Map.Entry entry : map.entrySet()) { + result = this.updateSeckillStock(entry.getKey(), entry.getValue()); + } + return result; + } + + /** + * 检查限时抢购申请列表参数信息 + * + * @param applyEndTime 申请结束时间 + * @param hours 限时抢购时间段 + * @param seckillApplyList 限时抢购申请列表 + * @param storeId 当前申请商家编号 + */ + private void checkSeckillApplyList(Long applyEndTime, String hours, List seckillApplyList, String storeId) { + List existSku = new ArrayList<>(); + for (SeckillApplyVO seckillApply : seckillApplyList) { + seckillApply.setStoreId(storeId); + if (seckillApply.getPrice() > seckillApply.getOriginalPrice()) { + throw new ServiceException("活动价格不能大于商品原价"); + } + + // 检查限时抢购申请的时刻,是否存在在限时抢购的时间段内 + String[] rangeHours = hours.split(","); + boolean containsSame = Arrays.stream(rangeHours).anyMatch(i -> i.equals(seckillApply.getTimeLine().toString())); + if (!containsSame) { + throw new ServiceException("时刻参数异常"); + } + + if (existSku.contains(seckillApply.getSkuId())) { + throw new ServiceException(seckillApply.getGoodsName() + "该商品不能同时参加多个时间段的活动"); + } else { + existSku.add(seckillApply.getSkuId()); + } + + } + } + + /** + * 组装促销商品信息 + * + * @param seckillApply 限时抢购申请信息 + * @param seckillStartTime 当前限时抢购申请的开始时间 + * @return 促销商品信息 + */ + private PromotionGoods setPromotionGoods(SeckillApply seckillApply, Date seckillStartTime) { + PromotionGoods promotionGoods = new PromotionGoods(); + promotionGoods.setTitle("限时抢购"); + promotionGoods.setSkuId(seckillApply.getSkuId()); + promotionGoods.setPromotionType(PromotionTypeEnum.SECKILL.name()); + promotionGoods.setPromotionId(seckillApply.getSeckillId()); + promotionGoods.setPrice(seckillApply.getPrice()); + promotionGoods.setNum(seckillApply.getQuantity()); + promotionGoods.setStoreId(seckillApply.getStoreId()); + promotionGoods.setPromotionStatus(PromotionStatusEnum.NEW.name()); + //商品活动的开始时间为当前商品的参加时间段 + int timeLine = seckillApply.getTimeLine(); + String date = DateUtil.toString(seckillStartTime, DateUtil.STANDARD_DATE_FORMAT); + long startTime = DateUtil.getDateline(date + " " + timeLine + ":00:00", DateUtil.STANDARD_FORMAT); + long endTime = DateUtil.getDateline(date + " 23:59:59", DateUtil.STANDARD_FORMAT); + + promotionGoods.setStartTime(new Date(startTime)); + promotionGoods.setEndTime(new Date(endTime)); + return promotionGoods; + } + + /** + * 检查缓存中是否存在相同商品参与的限时抢购活动 + * + * @param startTime 限时抢购开始时间 + */ + private void checkCache(Long startTime) { + String seckillCacheKey = PromotionCacheKeys.getSeckillTimelineKey(DateUtil.toString(startTime, DateUtil.STANDARD_DATE_NO_UNDERLINE_FORMAT)); + Map hash = cache.getHash(seckillCacheKey); + //如果缓存中存在当前审核商品参与的限时抢购活动商品信息,清除 + if (hash != null && !hash.isEmpty()) { + cache.remove(seckillCacheKey); + } + } + + /** + * 从缓存中获取限时抢购信息 + * + * @param seckillCacheKey 限时抢购缓存键 + * @return 限时抢购信息 + */ + private List getSeckillTimelineToCache(String seckillCacheKey) { + List timelineList = new ArrayList<>(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // 查询当天时间段内的且状态不为结束或关闭的限时抢购活动 + queryWrapper.gt(Seckill::getStartTime, new Date(DateUtil.startOfTodDay() * 1000)).lt(Seckill::getEndTime, DateUtil.endOfDate()) + .and(i -> i.eq(Seckill::getPromotionStatus, PromotionStatusEnum.NEW.name()) + .or(j -> j.eq(Seckill::getPromotionStatus, PromotionStatusEnum.START.name()))); + List seckillList = seckillService.list(queryWrapper); + if (!seckillList.isEmpty()) { + for (Seckill seckill : seckillList) { + //读取系统时间的时刻 + Calendar c = Calendar.getInstance(); + int hour = c.get(Calendar.HOUR_OF_DAY); + String[] split = seckill.getHours().split(","); + int[] hoursSored = Arrays.stream(split).mapToInt(Integer::parseInt).toArray(); + Arrays.sort(hoursSored); + for (int i = 0; i < hoursSored.length; i++) { + SeckillTimelineVO tempTimeline = new SeckillTimelineVO(); + if (hoursSored[i] >= hour || ((i + 1) < hoursSored.length && hoursSored[i + 1] > hour)) { + SimpleDateFormat format = new SimpleDateFormat(DateUtil.STANDARD_DATE_FORMAT); + String date = format.format(new Date()); + //当前时间的秒数 + long currentTime = DateUtil.getDateline(); + //限时抢购的时刻 + long timeLine = DateUtil.getDateline(date + " " + hoursSored[i], "yyyy-MM-dd HH"); + if ((i + 1) < hoursSored.length && hour > hoursSored[i] && hour <= hoursSored[i + 1]) { + timeLine = DateUtil.getDateline(date + " " + hoursSored[i + 1], "yyyy-MM-dd HH"); + } + Long distanceTime = timeLine - currentTime < 0 ? 0 : timeLine - currentTime; + tempTimeline.setDistanceStartTime(distanceTime); + tempTimeline.setStartTime(timeLine); + tempTimeline.setTimeLine(hoursSored[i]); + tempTimeline.setSeckillGoodsList(wrapperSeckillGoods(hoursSored[i])); + timelineList.add(tempTimeline); + } + } + } + } + + return timelineList; + } + + /** + * 组装当时间限时抢购的商品数据 + * w + * + * @param startTimeline 限时抢购开始时刻 + * @return 当时间限时抢购的商品数据 + */ + private List wrapperSeckillGoods(Integer startTimeline) { + List seckillGoodsVoS = new ArrayList<>(); + LambdaQueryWrapper seckillLambdaQueryWrapper = new LambdaQueryWrapper<>(); + seckillLambdaQueryWrapper.gt(Seckill::getStartTime, new Date(DateUtil.startOfTodDay() * 1000)).lt(Seckill::getEndTime, DateUtil.endOfDate()) + .and(i -> i.eq(Seckill::getPromotionStatus, PromotionStatusEnum.NEW.name()) + .or(j -> j.eq(Seckill::getPromotionStatus, PromotionStatusEnum.START.name()))); + List seckillList = this.seckillService.list(seckillLambdaQueryWrapper); + if (!seckillList.isEmpty()) { + for (Seckill seckill : seckillList) { + LambdaQueryWrapper seckillApplyLambdaQueryWrapper = new LambdaQueryWrapper().eq(SeckillApply::getTimeLine, startTimeline).eq(SeckillApply::getSeckillId, seckill.getId()).eq(SeckillApply::getPromotionApplyStatus, PromotionApplyStatusEnum.PASS.name()); + List list = this.list(seckillApplyLambdaQueryWrapper); + for (SeckillApply seckillApply : list) { + GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(seckillApply.getSkuId()); + if (goodsSku != null) { + SeckillGoodsVO goodsVO = new SeckillGoodsVO(); + BeanUtil.copyProperties(seckillApply, goodsVO); + goodsVO.setGoodsImage(goodsSku.getThumbnail()); + goodsVO.setGoodsId(goodsSku.getGoodsId()); + goodsVO.setGoodsName(goodsSku.getGoodsName()); + seckillGoodsVoS.add(goodsVO); + } + } + } + } + return seckillGoodsVoS; + } + + @Autowired + public void setSeckillService(SeckillService seckillService) { + this.seckillService = seckillService; + } + + @Autowired + public void setPromotionGoodsService(PromotionGoodsService promotionGoodsService) { + this.promotionGoodsService = promotionGoodsService; + } + + @Autowired + public void setGoodsSkuService(GoodsSkuService goodsSkuService) { + this.goodsSkuService = goodsSkuService; + } + + @Autowired + public void setEsGoodsIndexService(EsGoodsIndexService esGoodsIndexService) { + this.esGoodsIndexService = esGoodsIndexService; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/SeckillServiceImpl.java b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/SeckillServiceImpl.java new file mode 100644 index 00000000..3d44ecd1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/serviceimpl/SeckillServiceImpl.java @@ -0,0 +1,296 @@ +package cn.lili.modules.promotion.serviceimpl; + +import cn.lili.common.delayqueue.DelayQueueTools; +import cn.lili.common.delayqueue.DelayQueueType; +import cn.lili.common.delayqueue.PromotionMessage; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.trigger.interfaces.TimeTrigger; +import cn.lili.common.trigger.model.TimeExecuteConstant; +import cn.lili.common.trigger.model.TimeTriggerMsg; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.dos.Seckill; +import cn.lili.modules.promotion.entity.dos.SeckillApply; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.SeckillSearchParams; +import cn.lili.modules.promotion.entity.vos.SeckillVO; +import cn.lili.modules.promotion.mapper.SeckillMapper; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import cn.lili.modules.promotion.service.SeckillApplyService; +import cn.lili.modules.promotion.service.SeckillService; +import cn.lili.modules.promotion.tools.PromotionTools; +import cn.lili.modules.search.service.EsGoodsIndexService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 限时抢购业务层实现 + * + * @author Chopper + * @date 2020/8/21 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SeckillServiceImpl extends ServiceImpl implements SeckillService { + + //延时任务 + private final TimeTrigger timeTrigger; + //Mongo + private final MongoTemplate mongoTemplate; + //Rocketmq + private final RocketmqCustomProperties rocketmqCustomProperties; + //商品索引 + private EsGoodsIndexService goodsIndexService; + //促销商品 + private PromotionGoodsService promotionGoodsService; + //秒杀申请 + private SeckillApplyService seckillApplyService; + + @Override + public IPage getSeckillByPageFromMysql(SeckillSearchParams queryParam, PageVO pageVo) { + QueryWrapper queryWrapper = queryParam.wrapper(); + return page(PageUtil.initPage(pageVo), queryWrapper); + } + + /** + * 从mongo中根据条件获取限时抢购分页列表 + * + * @param queryParam 查询参数 + * @param pageVo 分页参数 + * @return 限时抢购分页列表 + */ + @Override + public IPage getSeckillByPageFromMongo(SeckillSearchParams queryParam, PageVO pageVo) { + IPage seckill = new Page<>(pageVo.getPageNumber(), pageVo.getPageSize()); + if (queryParam == null) { + queryParam = new SeckillSearchParams(); + } + Query query = queryParam.mongoQuery(); + pageVo.setNotConvert(true); + PromotionTools.mongoQueryPageParam(query, pageVo); + seckill.setCurrent(pageVo.getPageNumber()); + seckill.setSize(pageVo.getPageSize()); + List seckillVOS = this.mongoTemplate.find(query, SeckillVO.class); + seckill.setRecords(seckillVOS); + seckill.setTotal(this.mongoTemplate.count(queryParam.mongoQuery(), SeckillVO.class)); + return seckill; + } + + /** + * 从mongo中获取限时抢购信息 + * + * @param id 限时抢购id + * @return 限时抢购信息 + */ + @Override + public SeckillVO getSeckillByIdFromMongo(String id) { + return this.checkSeckillExist(id); + } + + @Override + public boolean saveSeckill(SeckillVO seckill) { + // 检查限时抢购参数 + checkSeckillParam(seckill, seckill.getStoreId()); + seckill.setPromotionStatus(PromotionStatusEnum.NEW.name()); + // 保存到MYSQL中 + boolean result = this.save(seckill); + // 保存到MONGO中 + this.mongoTemplate.save(seckill); + this.addSeckillStartTask(seckill); + return result; + } + + @Override + public void storeApply(String storeId, String seckillId) { + Seckill seckill = this.getById(seckillId); + String storeIds; + if (!StringUtils.isEmpty(seckill.getStoreIds())) { + storeIds = seckill.getStoreIds() + storeId + ","; + } else { + storeIds = storeId + ","; + } + seckill.setStoreIds(storeIds); + this.updateById(seckill); + } + + @Override + public boolean modifySeckill(SeckillVO seckillVO) { + // 检查该限时抢购是否存在 + SeckillVO seckill = checkSeckillExist(seckillVO.getId()); + if (PromotionStatusEnum.START.name().equals(seckillVO.getPromotionStatus())) { + throw new ServiceException("活动已经开始,不能进行编辑删除操作"); + } + // 检查限时抢购参数 + this.checkSeckillParam(seckillVO, seckillVO.getStoreId()); + + // 更新到MYSQL中 + boolean result = this.updateById(seckillVO); + // 保存到MONGO中 + this.mongoTemplate.save(seckillVO); + if (seckill.getStartTime().getTime() != seckillVO.getStartTime().getTime()) { + PromotionMessage promotionMessage = new PromotionMessage(seckillVO.getId(), PromotionTypeEnum.SECKILL.name(), PromotionStatusEnum.START.name(), seckillVO.getStartTime(), seckillVO.getEndTime()); + // 更新延时任务 + this.timeTrigger.edit(TimeExecuteConstant.PROMOTION_EXECUTOR, + promotionMessage, + seckill.getStartTime().getTime(), + seckillVO.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + DateUtil.getDelayTime(seckillVO.getStartTime().getTime()), + rocketmqCustomProperties.getPromotionTopic()); + } + return result; + } + + @Override + public void deleteSeckill(String id) { + Seckill seckill = checkSeckillExist(id); + if (PromotionStatusEnum.CLOSE.name().equals(seckill.getPromotionStatus()) || PromotionStatusEnum.END.name().equals(seckill.getPromotionStatus())) { + // 更新限时抢购状态为关闭,标示删除标志 + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper().eq(Seckill::getId, id).set(Seckill::getDeleteFlag, true).set(Seckill::getPromotionStatus, PromotionStatusEnum.CLOSE.name()); + this.update(updateWrapper); + LambdaUpdateWrapper seckillApplyLambdaUpdateWrapper = new LambdaUpdateWrapper().eq(SeckillApply::getSeckillId, id).set(SeckillApply::getDeleteFlag, true); + this.seckillApplyService.update(seckillApplyLambdaUpdateWrapper); + this.mongoTemplate.remove(new Query().addCriteria(Criteria.where("id").is(id)), SeckillVO.class); + LambdaUpdateWrapper promotionGoodsQueryWrapper = new LambdaUpdateWrapper().eq(PromotionGoods::getPromotionId, id).set(PromotionGoods::getDeleteFlag, true); + this.promotionGoodsService.update(promotionGoodsQueryWrapper); + this.timeTrigger.delete(TimeExecuteConstant.PROMOTION_EXECUTOR, + seckill.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (PromotionTypeEnum.SECKILL.name() + seckill.getId())), + rocketmqCustomProperties.getPromotionTopic()); + } else { + throw new ServiceException("该限时抢购活动的状态不能删除"); + } + } + + /** + * 开启一个限时抢购 + * + * @param id 限时抢购编号 + */ + @Override + public void openSeckill(String id) { + SeckillVO seckillVO = checkSeckillExist(id); + PromotionTools.checkPromotionTime(seckillVO.getStartTime().getTime(), seckillVO.getEndTime().getTime()); + if (PromotionStatusEnum.NEW.name().equals(seckillVO.getPromotionStatus()) || PromotionStatusEnum.CLOSE.name().equals(seckillVO.getPromotionStatus())) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper().eq(Seckill::getId, id).set(Seckill::getPromotionStatus, PromotionStatusEnum.START.name()); + this.update(updateWrapper); + seckillVO.setPromotionStatus(PromotionStatusEnum.START.name()); + this.mongoTemplate.save(seckillVO); + this.addSeckillStartTask(seckillVO); + } + } + + @Override + public void closeSeckill(String id) { + SeckillVO seckillVO = checkSeckillExist(id); + if (PromotionStatusEnum.NEW.name().equals(seckillVO.getPromotionStatus()) || PromotionStatusEnum.START.name().equals(seckillVO.getPromotionStatus())) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper().eq(Seckill::getId, id).set(Seckill::getPromotionStatus, PromotionStatusEnum.CLOSE.name()); + this.update(updateWrapper); + seckillVO.setPromotionStatus(PromotionStatusEnum.CLOSE.name()); + this.mongoTemplate.save(seckillVO); + if (PromotionStatusEnum.CLOSE.name().equals(seckillVO.getPromotionStatus())) { + LambdaQueryWrapper deleteWrapper = new LambdaQueryWrapper<>(); + deleteWrapper.eq(PromotionGoods::getPromotionId, seckillVO.getId()); + promotionGoodsService.remove(deleteWrapper); + if (seckillVO.getSeckillApplyList() != null) { + List skuIds = seckillVO.getSeckillApplyList().stream().map(SeckillApply::getSkuId).collect(Collectors.toList()); + this.goodsIndexService.deleteEsGoodsPromotionIndexByList(skuIds, PromotionTypeEnum.SECKILL); + } + this.timeTrigger.delete(TimeExecuteConstant.PROMOTION_EXECUTOR, + seckillVO.getStartTime().getTime(), + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (PromotionTypeEnum.SECKILL.name() + seckillVO.getId())), + rocketmqCustomProperties.getPromotionTopic()); + } + } else { + throw new ServiceException("该限时抢购活动的状态不能关闭"); + } + } + + @Override + public Integer getApplyNum() { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + //秒杀申请时间未超过当前时间 + queryWrapper.le(Seckill::getApplyEndTime, cn.hutool.core.date.DateUtil.date()); + queryWrapper.eq(Seckill::getPromotionStatus, PromotionStatusEnum.NEW.name()); + return this.count(queryWrapper); + } + + private void addSeckillStartTask(SeckillVO seckill) { + PromotionMessage promotionMessage = new PromotionMessage(seckill.getId(), PromotionTypeEnum.SECKILL.name(), PromotionStatusEnum.START.name(), seckill.getStartTime(), seckill.getEndTime()); + TimeTriggerMsg timeTriggerMsg = new TimeTriggerMsg(TimeExecuteConstant.PROMOTION_EXECUTOR, + seckill.getStartTime().getTime(), + promotionMessage, + DelayQueueTools.wrapperUniqueKey(DelayQueueType.PROMOTION, (promotionMessage.getPromotionType() + promotionMessage.getPromotionId())), + rocketmqCustomProperties.getPromotionTopic()); + // 发送促销活动开始的延时任务 + this.timeTrigger.addDelay(timeTriggerMsg, DateUtil.getDelayTime(seckill.getStartTime().getTime())); + } + + /** + * 检查该限时抢购是否存在 + * + * @param id 限时抢购编号 + * @return 限时抢购信息 + */ + private SeckillVO checkSeckillExist(String id) { + SeckillVO seckill = this.mongoTemplate.findById(id, SeckillVO.class); + if (seckill == null) { + throw new ServiceException("当前限时抢购活动不存在"); + } + return seckill; + } + + /** + * 检查限时抢购参数 + * + * @param seckill 限时抢购信息 + * @param storeId 卖家编号 + */ + private void checkSeckillParam(SeckillVO seckill, String storeId) { + seckill.checkTime(); + // 同一时间段内相同的活动 + QueryWrapper queryWrapper = PromotionTools.checkActiveTime(seckill.getStartTime(), seckill.getEndTime(), PromotionTypeEnum.SECKILL, storeId, seckill.getId()); + int sameNum = this.count(queryWrapper); + // 当前时间段是否存在同类活动 + if (sameNum > 0) { + throw new ServiceException("当前时间内已存在同类活动"); + } + } + + @Autowired + public void setEsGoodsIndexService(EsGoodsIndexService goodsIndexService) { + this.goodsIndexService = goodsIndexService; + } + + @Autowired + public void setPromotionGoodsService(PromotionGoodsService promotionGoodsService) { + this.promotionGoodsService = promotionGoodsService; + } + + @Autowired + public void setSeckillApplyService(SeckillApplyService seckillApplyService) { + this.seckillApplyService = seckillApplyService; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/promotion/tools/PromotionCacheKeys.java b/framework/src/main/java/cn/lili/modules/promotion/tools/PromotionCacheKeys.java new file mode 100644 index 00000000..e2f4be21 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/tools/PromotionCacheKeys.java @@ -0,0 +1,39 @@ +package cn.lili.modules.promotion.tools; + +import cn.lili.common.cache.CachePrefix; + +/** + * 满额活动缓存Key + * @author paulG + * @date 2020/10/12 + **/ +public class PromotionCacheKeys { + + /** + * 读取满优惠redis key + * @param activityId 活动ID + * @return 满优惠redis key + */ + public static String getFullDiscountKey(String activityId){ + return CachePrefix.STORE_ID_FULL_DISCOUNT + "::" + activityId; + } + + /** + * 读取满优惠redis key + * @param id id + * @return 满优惠redis key + */ + public static String getPromotionGoodsKey(String id){ + return CachePrefix.PROMOTION_GOODS + "::" + id; + } + + /** + * 读取满优惠redis key + * @param timeStr 时间字符串(格式为 yyyyMMdd) + * @return 满优惠redis key + */ + public static String getSeckillTimelineKey(String timeStr){ + return CachePrefix.STORE_ID_SECKILL + "::" + timeStr; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/promotion/tools/PromotionTools.java b/framework/src/main/java/cn/lili/modules/promotion/tools/PromotionTools.java new file mode 100644 index 00000000..84ea7853 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/promotion/tools/PromotionTools.java @@ -0,0 +1,148 @@ +package cn.lili.modules.promotion.tools; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.data.mongodb.core.query.Query; + +import java.util.Date; +import java.util.List; + + +/** + * 优惠活动通用验证类 + * + * @author paulG + * @date 2020/8/18 + **/ +public class PromotionTools { + + /** + * 参数验证 + * 1、活动起始时间必须大于当前时间 + * 2、验证活动开始时间是否大于活动结束时间 + * + * @param startTime 活动开始时间 + * @param endTime 活动结束时间 + * @param num 参与活动商品数量 + * @param goodsList 选择的商品 + */ + public static void paramValid(Long startTime, Long endTime, int num, List goodsList) { + + checkPromotionTime(startTime, endTime); + + // 如果促销活动选择的是部分商品参加活动 + if (num != -1 && goodsList == null) { + throw new ServiceException("请选择要参与活动的商品"); + } + } + + /** + * 参数验证 + * 1、活动起始时间必须大于当前时间 + * 2、验证活动开始时间是否大于活动结束时间 + * + * @param startTime 活动开始时间 + * @param endTime 活动结束时间 + */ + public static void checkPromotionTime(Long startTime, Long endTime) { + + long nowTime = DateUtil.getDateline() * 1000; + + //如果活动起始时间小于现在时间 + if (startTime < nowTime) { + throw new ServiceException("活动起始时间必须大于当前时间"); + } + + // 开始时间不能大于结束时间 + if (startTime > endTime) { + throw new ServiceException("活动起始时间不能大于活动结束时间"); + } + } + + /** + * 组装检查促销活动时间 query wrapper + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param typeEnum 促销类型 + * @param storeId 店铺id + * @param activityId 促销活动id + * @param 促销类型 + * @return mybatis plus query wrapper对象 + */ + public static QueryWrapper checkActiveTime(Date startTime, Date endTime, PromotionTypeEnum typeEnum, String storeId, String activityId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + String startTimeColumn = "start_time"; + String endTimeColumn = "end_time"; + if (PromotionTypeEnum.SECKILL != typeEnum) { + queryWrapper.nested(i -> { + // 新活动起始时间 大于 之前活动的起始时间 小于 之前活动的截止时间 + i.nested(i2 -> i2.le(startTimeColumn, startTime).ge(endTimeColumn, startTime)); + // 新活动结束时间 大于 之前活动的起始时间 小于 之前活动的截止时间 + i.or(i1 -> i1.le(startTimeColumn, endTime).ge(endTimeColumn, endTime)); + }); + } else { + // queryWrapper.le(startTimeColumn, startTime).ge(endTimeColumn, startTime); + queryWrapper.ge(startTimeColumn, cn.hutool.core.date.DateUtil.beginOfDay(startTime)).le(endTimeColumn, cn.hutool.core.date.DateUtil.endOfDay(endTime)); + } + if (storeId != null) { + queryWrapper.eq("store_id", storeId); + } + if (activityId != null) { + queryWrapper.ne("id", activityId); + } + // 忽略已作废和已关闭的活动 + queryWrapper.ne("promotion_status", PromotionStatusEnum.END.name()); + queryWrapper.ne("promotion_status", PromotionStatusEnum.CLOSE.name()); + queryWrapper.eq("delete_flag", false); + return queryWrapper; + } + + /** + * 促销商品入库前填充 + * + * @param originList 原促销商品列表 + * @param promotion 促销信息 + * @return 促销商品列表 + */ + public static List promotionGoodsInit(List originList, BasePromotion promotion, PromotionTypeEnum promotionTypeEnum) { + // 本次促销商品入库 + for (PromotionGoods promotionGoods : originList) { + promotionGoods.setPromotionId(promotion.getId()); + promotionGoods.setStoreName(promotion.getStoreName()); + promotionGoods.setTitle(promotion.getPromotionName()); + promotionGoods.setStartTime(promotion.getStartTime()); + if (promotion.getEndTime() == null) { + promotionGoods.setEndTime(promotion.getEndTime()); + } + promotionGoods.setPromotionType(promotionTypeEnum.name()); + promotionGoods.setPromotionStatus(promotion.getPromotionStatus()); + promotionGoods.setNum(0); + } + return originList; + } + + /** + * 为mongoQuery组织分页排序参数 + * + * @param query 查询条件 + * @param page 分页排序参数 + */ + public static void mongoQueryPageParam(Query query, PageVO page) { + page.setNotConvert(true); + query.with(PageRequest.of(page.getMongoPageNumber(), page.getPageSize())); + if (!CharSequenceUtil.isEmpty(page.getOrder()) && !CharSequenceUtil.isEmpty(page.getSort())) { + query.with(Sort.by(Sort.Direction.valueOf(page.getOrder().toUpperCase()), page.getSort())); + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseOrder.java b/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseOrder.java new file mode 100644 index 00000000..0eac781d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseOrder.java @@ -0,0 +1,74 @@ +package cn.lili.modules.purchase.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Date; + +/** + * 供求单 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@Data +@Entity +@ApiModel(value = "供求单") +@TableName("li_purchase_order") +@Table(name = "li_purchase_order") +public class PurchaseOrder extends BaseEntity { + + @ApiModelProperty(value = "标题") + private String title; + + @ApiModelProperty(value = "截止时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date deadline; + + @ApiModelProperty(value = "收货时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date receiptTime; + + @ApiModelProperty(value = "价格类型", notes = "可议价、不可议价、面议") + private String priceMethod; + + @ApiModelProperty(value = "地址名称, ','分割") + private String consigneeAddressPath; + + @ApiModelProperty(value = "地址id,','分割 ") + private String consigneeAddressIdPath; + + @ApiModelProperty(value = "是否需要发票") + private Boolean needReceipt; + + @ApiModelProperty(value = "补充说明") + private String supplement; + + @ApiModelProperty(value = "联系类型", notes = "联系方式什么时候可见 公开后、公开") + private String contactType; + + @ApiModelProperty(value = "联系人") + private String contacts; + + @ApiModelProperty(value = "联系电话") + private String contactNumber; + + @ApiModelProperty(value = "供求人") + private String memberId; + + @ApiModelProperty(value = "状态,开启:OPEN,关闭:CLOSE") + private String status; + + @ApiModelProperty(value = "分类ID") + private String categoryId; + + @ApiModelProperty(value = "分类名称") + private String categoryName; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseOrderItem.java b/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseOrderItem.java new file mode 100644 index 00000000..c0b5449e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseOrderItem.java @@ -0,0 +1,70 @@ +package cn.lili.modules.purchase.entity.dos; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 采购单子内容 + * + * @author Bulbasaur + * @date 2020/11/26 19:32 + */ +@Data +@Entity +@Table(name = "li_purchase_order_item") +@TableName("li_purchase_order_item") +@ApiModel(value = "采购单子内容") +public class PurchaseOrderItem { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "采购ID") + private String purchaseOrderId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "数量") + private String num; + + @ApiModelProperty(value = "数量单位") + private String goodsUnit; + + @ApiModelProperty(value = "价格") + private Double price; + + @ApiModelProperty(value = "规格") + @Column(columnDefinition = "TEXT") + private String specs; + + @ApiModelProperty(value = "图片") + private String images; + + +} diff --git a/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseQuoted.java b/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseQuoted.java new file mode 100644 index 00000000..5bfb2331 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseQuoted.java @@ -0,0 +1,73 @@ +package cn.lili.modules.purchase.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.common.utils.SnowFlake; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 报价单 + * + * @author Chopper + * @date 2020/11/26 20:43 + */ +@Data +@Entity +@ApiModel(value = "供求单报价") +@TableName("li_purchase_quoted") +@Table(name = "li_purchase_quoted") +public class PurchaseQuoted { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "采购单ID") + private String purchaseOrderId; + + @ApiModelProperty(value = "标题") + private String title; + + @ApiModelProperty(value = "报价说明") + private String context; + + @ApiModelProperty(value = "附件") + private String annex; + + @ApiModelProperty(value = "公司名称") + private String companyName; + + @ApiModelProperty(value = "联系人") + private String contacts; + + @ApiModelProperty(value = "联系电话") + private String contactNumber; + + @ApiModelProperty(value = "报价人") + private String memberId; + +} diff --git a/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseQuotedItem.java b/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseQuotedItem.java new file mode 100644 index 00000000..16e85fcc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/entity/dos/PurchaseQuotedItem.java @@ -0,0 +1,66 @@ +package cn.lili.modules.purchase.entity.dos; + +import cn.lili.common.utils.SnowFlake; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 报价单字内容 + * + * @author Bulbasaur + * @date 2020/11/26 20:43 + */ +@Data +@Entity +@ApiModel(value = "供求单报价") +@TableName("li_purchase_quoted_item") +@Table(name = "li_purchase_quoted_item") +public class PurchaseQuotedItem { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "报价单ID") + private String PurchaseQuotedId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "规格") + @Column(columnDefinition = "TEXT") + private String specs; + + @ApiModelProperty(value = "数量") + private String num; + + @ApiModelProperty(value = "数量单位") + private String goodsUnit; + + @ApiModelProperty(value = "价格") + private Double price; +} diff --git a/framework/src/main/java/cn/lili/modules/purchase/entity/params/PurchaseOrderSearchParams.java b/framework/src/main/java/cn/lili/modules/purchase/entity/params/PurchaseOrderSearchParams.java new file mode 100644 index 00000000..0be43cd2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/entity/params/PurchaseOrderSearchParams.java @@ -0,0 +1,24 @@ +package cn.lili.modules.purchase.entity.params; + +import cn.lili.common.vo.PageVO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 供求单查询参数 + * + * @author Bulbasaur + * @date 2020/11/27 11:29 + */ +@Data +public class PurchaseOrderSearchParams extends PageVO { + + @ApiModelProperty(value = "会员ID") + private String memberId; + + @ApiModelProperty(value = "分类ID") + private String categoryId; + + @ApiModelProperty(value = "状态,开启:OPEN,关闭:CLOSE") + private String status; +} diff --git a/framework/src/main/java/cn/lili/modules/purchase/entity/vos/PurchaseOrderVO.java b/framework/src/main/java/cn/lili/modules/purchase/entity/vos/PurchaseOrderVO.java new file mode 100644 index 00000000..53e9f9f7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/entity/vos/PurchaseOrderVO.java @@ -0,0 +1,19 @@ +package cn.lili.modules.purchase.entity.vos; + +import cn.lili.modules.purchase.entity.dos.PurchaseOrder; +import cn.lili.modules.purchase.entity.dos.PurchaseOrderItem; +import lombok.Data; + +import java.util.List; + +/** + * 采购单VO + * + * @author Bulbasaur + * @date 2020/11/26 19:54 + */ +@Data +public class PurchaseOrderVO extends PurchaseOrder { + + private List purchaseOrderItems; +} diff --git a/framework/src/main/java/cn/lili/modules/purchase/entity/vos/PurchaseQuotedVO.java b/framework/src/main/java/cn/lili/modules/purchase/entity/vos/PurchaseQuotedVO.java new file mode 100644 index 00000000..e0524fbd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/entity/vos/PurchaseQuotedVO.java @@ -0,0 +1,19 @@ +package cn.lili.modules.purchase.entity.vos; + +import cn.lili.modules.purchase.entity.dos.PurchaseQuoted; +import cn.lili.modules.purchase.entity.dos.PurchaseQuotedItem; +import lombok.Data; + +import java.util.List; + +/** + * 报价单VO + * + * @author Bulbasaur + * @date 2020/11/26 19:54 + */ +@Data +public class PurchaseQuotedVO extends PurchaseQuoted { + + private List purchaseQuotedItems; +} diff --git a/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseOrderItemMapper.java b/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseOrderItemMapper.java new file mode 100644 index 00000000..41547acc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseOrderItemMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.purchase.mapper; + + +import cn.lili.modules.purchase.entity.dos.PurchaseOrderItem; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 采购单子内容数据处理层 + * + * @author Bulbasaur + * @date 2020/11/26 16:11 + */ +public interface PurchaseOrderItemMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseOrderMapper.java b/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseOrderMapper.java new file mode 100644 index 00000000..f7f6f1c1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseOrderMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.purchase.mapper; + + +import cn.lili.modules.purchase.entity.dos.PurchaseOrder; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 采购单数据处理层 + * + * @author Bulbasaur + * @date 2020/11/26 16:11 + */ +public interface PurchaseOrderMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseQuotedItemMapper.java b/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseQuotedItemMapper.java new file mode 100644 index 00000000..78ae523e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseQuotedItemMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.purchase.mapper; + + +import cn.lili.modules.purchase.entity.dos.PurchaseQuotedItem; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 采购单子内容数据处理层 + * + * @author Bulbasaur + * @date 2020/11/26 16:11 + */ +public interface PurchaseQuotedItemMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseQuotedMapper.java b/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseQuotedMapper.java new file mode 100644 index 00000000..a6587903 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/mapper/PurchaseQuotedMapper.java @@ -0,0 +1,16 @@ +package cn.lili.modules.purchase.mapper; + + +import cn.lili.modules.purchase.entity.dos.PurchaseQuoted; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 采购报价数据处理层 + * + * @author Bulbasaur + * @date 2020/11/26 16:11 + */ +public interface PurchaseQuotedMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseOrderItemService.java b/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseOrderItemService.java new file mode 100644 index 00000000..2d289a1b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseOrderItemService.java @@ -0,0 +1,26 @@ +package cn.lili.modules.purchase.service; + + +import cn.lili.modules.purchase.entity.dos.PurchaseOrderItem; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 采购单子内容业务层 + * + * @author Bulbasaur + * @date 2020/11/26 16:12 + */ +public interface PurchaseOrderItemService extends IService { + + /** + * 添加采购单子内容 + * + * @param purchaseOrderId 采购单ID + * @param purchaseOrderItemList 采购单子内容列表 + * @return 操作结果 + */ + boolean addPurchaseOrderItem(String purchaseOrderId, List purchaseOrderItemList); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseOrderService.java b/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseOrderService.java new file mode 100644 index 00000000..ba92322b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseOrderService.java @@ -0,0 +1,50 @@ +package cn.lili.modules.purchase.service; + + +import cn.lili.modules.purchase.entity.dos.PurchaseOrder; +import cn.lili.modules.purchase.entity.vos.PurchaseOrderVO; +import cn.lili.modules.purchase.entity.params.PurchaseOrderSearchParams; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 采购单业务层 + * + * @author Bulbasaur + * @date 2020/11/26 16:12 + */ +public interface PurchaseOrderService extends IService { + + /** + * 添加采购单 + * + * @param purchaseOrderVO 采购单 + * @return 采购单 + */ + PurchaseOrderVO addPurchaseOrder(PurchaseOrderVO purchaseOrderVO); + + /** + * 根据ID获取采购单 + * + * @param id 采购单ID + * @return 采购单 + */ + PurchaseOrderVO getPurchaseOrder(String id); + + /** + * 获取采购单分页列表 + * + * @param purchaseOrderSearchParams 查询参数 + * @return + */ + IPage page(PurchaseOrderSearchParams purchaseOrderSearchParams); + + /** + * 关闭供求单 + * + * @param id 供求单ID + * @return 供求单 + */ + boolean close(String id); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseQuotedItemService.java b/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseQuotedItemService.java new file mode 100644 index 00000000..7ee68777 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseQuotedItemService.java @@ -0,0 +1,34 @@ +package cn.lili.modules.purchase.service; + + +import cn.lili.modules.purchase.entity.dos.PurchaseQuotedItem; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 采购单子内容业务层 + * + * @author Bulbasaur + * @date 2020/11/26 16:12 + */ +public interface PurchaseQuotedItemService extends IService { + + /** + * 添加报价单子内容 + * + * @param PurchaseQuotedId 采购单ID + * @param purchaseQuotedItemList 采购单子内容列表 + * @return 操作结果 + */ + boolean addPurchaseQuotedItem(String PurchaseQuotedId, List purchaseQuotedItemList); + + /** + * 获取报价单子内容列表 + * + * @param purchaseQuotedId 报价单ID + * @return 报价单子内容列表 + */ + List purchaseQuotedItemList(String purchaseQuotedId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseQuotedService.java b/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseQuotedService.java new file mode 100644 index 00000000..b4a0646a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/service/PurchaseQuotedService.java @@ -0,0 +1,39 @@ +package cn.lili.modules.purchase.service; + + +import cn.lili.modules.purchase.entity.dos.PurchaseQuoted; +import cn.lili.modules.purchase.entity.vos.PurchaseQuotedVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 采购单报价业务层 + * + * @author Bulbasaur + * @date 2020/11/26 16:12 + */ +public interface PurchaseQuotedService extends IService { + /** + * 添加报价单 + * + * @param purchaseQuotedVO 报价单 + * @return 报价单 + */ + PurchaseQuotedVO addPurchaseQuoted(PurchaseQuotedVO purchaseQuotedVO); + + /** + * 根据采购单获取报价单列表 + * + * @param purchaseOrderId 采购单ID + * @return 报价单列表 + */ + List getByPurchaseOrderId(String purchaseOrderId); + + /** + * 获取采购单VO + * @param id 采购单ID + * @return 采购单VO + */ + PurchaseQuotedVO getById(String id); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseOrderItemServiceImpl.java b/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseOrderItemServiceImpl.java new file mode 100644 index 00000000..102d8062 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseOrderItemServiceImpl.java @@ -0,0 +1,34 @@ +package cn.lili.modules.purchase.serviceimpl; + +import cn.lili.modules.purchase.entity.dos.PurchaseOrderItem; +import cn.lili.modules.purchase.mapper.PurchaseOrderItemMapper; +import cn.lili.modules.purchase.service.PurchaseOrderItemService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +/** + * 采购单子内容业务层实现 + * + * @author Bulbasaur + * @date 2020/11/26 16:13 + */ +@Service +@Transactional +public class PurchaseOrderItemServiceImpl extends ServiceImpl implements PurchaseOrderItemService { + + @Override + public boolean addPurchaseOrderItem(String purchaseOrderId, List purchaseOrderItemList) { + //添加采购单子内容 + for (PurchaseOrderItem purchaseOrderItem : purchaseOrderItemList) { + purchaseOrderItem.setPurchaseOrderId(purchaseOrderId); + this.save(purchaseOrderItem); + } + return true; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseOrderServiceImpl.java b/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseOrderServiceImpl.java new file mode 100644 index 00000000..11cb57ea --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseOrderServiceImpl.java @@ -0,0 +1,96 @@ +package cn.lili.modules.purchase.serviceimpl; + +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.purchase.entity.dos.PurchaseOrder; +import cn.lili.modules.purchase.entity.vos.PurchaseOrderVO; +import cn.lili.modules.purchase.entity.params.PurchaseOrderSearchParams; +import cn.lili.modules.purchase.mapper.PurchaseOrderMapper; +import cn.lili.modules.purchase.service.PurchaseOrderItemService; +import cn.lili.modules.purchase.service.PurchaseOrderService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.Map; + + +/** + * 采购单业务层实现 + * + * @author Bulbasaur + * @date 2020/11/26 16:13 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PurchaseOrderServiceImpl extends ServiceImpl implements PurchaseOrderService { + + private final PurchaseOrderItemService purchaseOrderItemService; + + @Override + public PurchaseOrderVO addPurchaseOrder(PurchaseOrderVO purchaseOrderVO) { + PurchaseOrder purchaseOrder = new PurchaseOrder(); + BeanUtil.copyProperties(purchaseOrderVO, purchaseOrder); + //添加采购单 + purchaseOrder.setStatus("OPEN"); + this.save(purchaseOrder); + //添加采购单子内容 + purchaseOrderItemService.addPurchaseOrderItem(purchaseOrder.getId(), purchaseOrderVO.getPurchaseOrderItems()); + return purchaseOrderVO; + } + + @Override + public PurchaseOrderVO getPurchaseOrder(String id) { + PurchaseOrderVO purchaseOrderVO = new PurchaseOrderVO(); + //获取采购单内容 + PurchaseOrder purchaseOrder = this.getById(id); + BeanUtil.copyProperties(purchaseOrder, purchaseOrderVO); + + //获取采购单子内容 + Map map = new HashMap<>(); + map.put("purchaseOrderId", id); + purchaseOrderVO.setPurchaseOrderItems(purchaseOrderItemService.listByMap(map)); + return purchaseOrderVO; + } + + @Override + public IPage page(PurchaseOrderSearchParams purchaseOrderSearchParams) { + + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + if (purchaseOrderSearchParams.getMemberId() != null) { + lambdaQueryWrapper.eq(PurchaseOrder::getMemberId, purchaseOrderSearchParams.getMemberId()); + } + if (purchaseOrderSearchParams.getCategoryId() != null) { + lambdaQueryWrapper.eq(PurchaseOrder::getCategoryId, purchaseOrderSearchParams.getCategoryId()); + } + if (purchaseOrderSearchParams.getStatus() != null) { + lambdaQueryWrapper.eq(PurchaseOrder::getStatus, purchaseOrderSearchParams.getStatus()); + } + + Page page = new Page(); + page.setSize(purchaseOrderSearchParams.getPageSize()); + page.setPages(purchaseOrderSearchParams.getPageNumber()); + IPage purchaseOrders = this.page(page, lambdaQueryWrapper); + return purchaseOrders; + } + + @Override + public boolean close(String id) { + PurchaseOrder purchaseOrder = this.getById(id); + purchaseOrder.setStatus("CLOSE"); + + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("id", id); + updateWrapper.set("status", "CLOSE"); + + return this.update(updateWrapper); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseQuotedItemServiceImpl.java b/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseQuotedItemServiceImpl.java new file mode 100644 index 00000000..6569c7b1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseQuotedItemServiceImpl.java @@ -0,0 +1,36 @@ +package cn.lili.modules.purchase.serviceimpl; + +import cn.lili.modules.purchase.entity.dos.PurchaseQuotedItem; +import cn.lili.modules.purchase.mapper.PurchaseQuotedItemMapper; +import cn.lili.modules.purchase.service.PurchaseQuotedItemService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +/** + * 采购单子内容业务层实现 + * + * @author Bulbasaur + * @date 2020/11/26 16:13 + */ +@Service +@Transactional +public class PurchaseQuotedItemServiceImpl extends ServiceImpl implements PurchaseQuotedItemService { + + @Override + public boolean addPurchaseQuotedItem(String purchaseQuotedId, List purchaseQuotedItemList) { + for (PurchaseQuotedItem purchaseQuotedItem : purchaseQuotedItemList) { + purchaseQuotedItem.setPurchaseQuotedId(purchaseQuotedId); + this.save(purchaseQuotedItem); + } + return true; + } + + @Override + public List purchaseQuotedItemList(String purchaseQuotedId) { + return null; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseQuotedServiceImpl.java b/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseQuotedServiceImpl.java new file mode 100644 index 00000000..3d05a95f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/purchase/serviceimpl/PurchaseQuotedServiceImpl.java @@ -0,0 +1,61 @@ +package cn.lili.modules.purchase.serviceimpl; + +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.purchase.entity.dos.PurchaseQuoted; +import cn.lili.modules.purchase.entity.vos.PurchaseQuotedVO; +import cn.lili.modules.purchase.mapper.PurchaseQuotedMapper; +import cn.lili.modules.purchase.service.PurchaseQuotedItemService; +import cn.lili.modules.purchase.service.PurchaseQuotedService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +/** + * 采购单报价业务层实现 + * + * @author Bulbasaur + * @date 2020/11/26 16:13 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PurchaseQuotedServiceImpl extends ServiceImpl implements PurchaseQuotedService { + + private final PurchaseQuotedMapper purchaseQuotedMapper; + private final PurchaseQuotedItemService purchaseQuotedItemService; + + @Override + public PurchaseQuotedVO addPurchaseQuoted(PurchaseQuotedVO purchaseQuotedVO) { + PurchaseQuoted purchaseQuoted = new PurchaseQuoted(); + BeanUtil.copyProperties(purchaseQuotedVO, purchaseQuoted); + //添加报价单 + this.save(purchaseQuoted); + //添加采购单子内容 + purchaseQuotedItemService.addPurchaseQuotedItem(purchaseQuotedVO.getId(), purchaseQuotedVO.getPurchaseQuotedItems()); + return purchaseQuotedVO; + } + + @Override + public List getByPurchaseOrderId(String purchaseOrderId) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(PurchaseQuoted::getPurchaseOrderId, purchaseOrderId); + return this.list(lambdaQueryWrapper); + } + + @Override + public PurchaseQuotedVO getById(String id) { + //获取报价单 + PurchaseQuotedVO purchaseQuotedVO = new PurchaseQuotedVO(); + BeanUtil.copyProperties(this.getById(id), purchaseQuotedVO); + //获取报价单子内容 + purchaseQuotedVO.setPurchaseQuotedItems(purchaseQuotedItemService.purchaseQuotedItemList(id)); + return purchaseQuotedVO; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/script/LuaScript.java b/framework/src/main/java/cn/lili/modules/script/LuaScript.java new file mode 100644 index 00000000..2c837a5c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/script/LuaScript.java @@ -0,0 +1,42 @@ +package cn.lili.modules.script; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.scripting.support.ResourceScriptSource; + +/** + * redis 脚本 + * + * @author Chopper + * @version v1.0 + * @since + * 2020-06-16 10:40 + */ +@Configuration +public class LuaScript { + + /** + * 库存扣减脚本 + */ + @Bean + public DefaultRedisScript quantityScript() { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/quantity.lua"))); + redisScript.setResultType(Boolean.class); + return redisScript; + } + + /** + * 流量限制脚本 + * @return + */ + @Bean + public DefaultRedisScript limitScript() { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/limit.lua"))); + redisScript.setResultType(Number.class); + return redisScript; + } +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/dos/CustomWords.java b/framework/src/main/java/cn/lili/modules/search/entity/dos/CustomWords.java new file mode 100644 index 00000000..c7908e43 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/dos/CustomWords.java @@ -0,0 +1,42 @@ +package cn.lili.modules.search.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; + +/** + * 自定义分词 + * + * @author paulG + * @date 2020/10/15 + **/ +@Data +@Entity +@Table(name = "li_custom_words") +@TableName("li_custom_words") +@ApiModel(value = "自定义分词") +public class CustomWords extends BaseEntity { + + private static final long serialVersionUID = 650889506808657977L; + + /** + * 名称 + */ + @ApiModelProperty(value = "名称") + @NotEmpty(message = "分词名称必填") + @Length(max = 20, message = "分词名称长度不能大于20") + private String name; + + + @ApiModelProperty(value = "是否禁用") + private Integer disabled; + + +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/dos/EsGoodsAttribute.java b/framework/src/main/java/cn/lili/modules/search/entity/dos/EsGoodsAttribute.java new file mode 100644 index 00000000..5c153e9c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/dos/EsGoodsAttribute.java @@ -0,0 +1,57 @@ +package cn.lili.modules.search.entity.dos; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; + +import java.io.Serializable; + +/** + * 商品属性索引 + * @author paulG + * @date 2020/10/14 + **/ +@Data +@NoArgsConstructor +public class EsGoodsAttribute implements Serializable { + + private static final long serialVersionUID = 4018042777559970062L; + + /** + * 属性参数:0->规格;1->参数 + */ + @Field(type = FieldType.Integer) + private Integer type; + + /** + * 属性名称 + */ + private String nameId; + + /** + * 属性名称 + */ + @Field(type = FieldType.Text) + private String name; + + /** + * 属性值 + */ + @Field(type = FieldType.Text) + private String valueId; + + /** + * 属性值 + */ + @Field(type = FieldType.Text) + private String value; + + public EsGoodsAttribute(Integer type,String nameId,String name,String valueId,String value){ + this.type=type; + this.nameId=nameId; + this.name=name; + this.valueId=valueId; + this.value=value; + } +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/dos/EsGoodsIndex.java b/framework/src/main/java/cn/lili/modules/search/entity/dos/EsGoodsIndex.java new file mode 100644 index 00000000..76f54d9e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/dos/EsGoodsIndex.java @@ -0,0 +1,318 @@ +package cn.lili.modules.search.entity.dos; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.common.elasticsearch.EsSuffix; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.springframework.data.annotation.Id; +import org.springframework.data.elasticsearch.annotations.DateFormat; +import org.springframework.data.elasticsearch.annotations.Document; +import org.springframework.data.elasticsearch.annotations.Field; +import org.springframework.data.elasticsearch.annotations.FieldType; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 商品索引 + * @author paulG + * @date 2020/10/13 + **/ +@Data +@Document(indexName = "#{elasticsearchProperties.indexPrefix}_" + EsSuffix.GOODS_INDEX_NAME) +@ToString +@NoArgsConstructor +public class EsGoodsIndex implements Serializable { + + private static final long serialVersionUID = -6856471777036048874L; + + @Id + @ApiModelProperty("商品skuId") + private String id; + + /** + * 商品id + */ + @ApiModelProperty("商品Id") + @Field(type = FieldType.Text) + private String goodsId; + + /** + * 商品名称 + */ + @Field(type = FieldType.Text, analyzer = "ik_max_word") + @ApiModelProperty("商品名称") + private String goodsName; + + /** + * 商品编号 + */ + @Field(type = FieldType.Keyword) + @ApiModelProperty("商品编号") + private String sn; + + /** + * 卖家id + */ + @Field(type = FieldType.Text) + @ApiModelProperty("卖家id") + private String storeId; + + /** + * 卖家名称 + */ + @Field(type = FieldType.Text) + @ApiModelProperty("卖家名称") + private String storeName; + + /** + * 销量 + */ + @Field(type = FieldType.Integer) + @ApiModelProperty("销量") + private Integer buyCount; + + /** + * 小图 + */ + @ApiModelProperty("小图") + private String small; + + /** + * 缩略图 + */ + @ApiModelProperty("缩略图") + private String thumbnail; + + /** + * 品牌id + */ + @Field(type = FieldType.Integer, fielddata = true) + @ApiModelProperty("品牌id") + private String brandId; + + /** + * 分类path + */ + @Field(type = FieldType.Keyword, fielddata = true) + @ApiModelProperty("分类path") + private String categoryPath; + + /** + * 店铺分类id + */ + @Field(type = FieldType.Keyword) + @ApiModelProperty("店铺分类id") + private String storeCategoryPath; + + /** + * 商品价格 + */ + @Field(type = FieldType.Double) + @ApiModelProperty("商品价格") + private Double price; + + /** + * 促销价 + */ + @Field(type = FieldType.Double) + @ApiModelProperty("促销价") + private Double promotionPrice; + + /** + * 如果是积分商品需要使用的积分 + */ + @Field(type = FieldType.Integer) + @ApiModelProperty("积分商品需要使用的积分") + private Integer point; + + /** + * 评价数量 + */ + @Field(type = FieldType.Integer) + @ApiModelProperty("评价数量") + private Integer commentNum; + + /** + * 好评数量 + */ + @Field(type = FieldType.Integer) + @ApiModelProperty("好评数量") + private Integer highPraiseNum; + + /** + * 好评率 + */ + @Field(type = FieldType.Double) + @ApiModelProperty("好评率") + private Double grade; + + /** + * 详情 + */ + @Field(type = FieldType.Text) + @ApiModelProperty("详情") + private String intro; + + /** + * 商品移动端详情 + */ + @Field(type = FieldType.Text) + @ApiModelProperty("商品移动端详情") + private String mobileIntro; + + /** + * 是否自营 + */ + @Field(type = FieldType.Boolean) + @ApiModelProperty("是否自营") + private Boolean selfOperated; + + /** + * 是否为推荐商品 + */ + @Field(type = FieldType.Boolean) + @ApiModelProperty("是否为推荐商品") + private Boolean recommend; + + /** + * 销售模式 + */ + @Field(type = FieldType.Text) + @ApiModelProperty("销售模式") + private String salesModel; + + /** + * 审核状态 + */ + @Field(type = FieldType.Text) + @ApiModelProperty("审核状态") + private String isAuth; + + /** + * 卖点 + */ + @Field(type = FieldType.Text) + @ApiModelProperty("卖点") + private String sellingPoint; + + /** + * 上架状态 + */ + @Field(type = FieldType.Text) + @ApiModelProperty("上架状态") + private String marketEnable; + + /** + * 商品视频 + */ + @Field(type = FieldType.Text) + @ApiModelProperty("商品视频") + private String goodsVideo; + + @ApiModelProperty("商品发布时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @Field(type = FieldType.Date, format = DateFormat.basic_date_time) + private Date releaseTime; + + /** + * 商品属性(参数和规格) + */ + @Field(type = FieldType.Nested) + private List attrList; + + /** + * 商品促销活动集合 + * key 为 促销活动类型 + * + * @see cn.lili.modules.promotion.entity.enums.PromotionTypeEnum + * value 为 促销活动实体信息 + */ + @Field(type = FieldType.Nested) + @ApiModelProperty("商品促销活动集合,key 为 促销活动类型,value 为 促销活动实体信息 ") + private Map promotionMap; + + + public void setGoodsSku(GoodsSku sku) { + if (sku != null) { + this.id = sku.getId(); + this.goodsId = sku.getGoodsId(); + this.goodsName = sku.getGoodsName(); + this.price = sku.getPrice(); + this.storeName = sku.getStoreName(); + this.storeId = sku.getStoreId(); + this.thumbnail = sku.getThumbnail(); + this.categoryPath = sku.getCategoryPath(); + this.goodsVideo = sku.getGoodsVideo(); + this.mobileIntro = sku.getMobileIntro(); + this.buyCount = sku.getBuyCount(); + this.commentNum = sku.getCommentNum(); + this.small = sku.getSmall(); + this.brandId = sku.getBrandId(); + this.sn = sku.getSn(); + this.storeCategoryPath = sku.getStoreCategoryPath(); + this.sellingPoint = sku.getSellingPoint(); + this.selfOperated = sku.getSelfOperated(); + this.salesModel = sku.getSalesModel(); + this.marketEnable = sku.getMarketEnable(); + this.isAuth = sku.getIsAuth(); + this.intro = sku.getIntro(); + this.grade = sku.getGrade(); + this.releaseTime = new Date(); + } + } + + public EsGoodsIndex(GoodsSku sku) { + if (sku != null) { + this.id = sku.getId(); + this.goodsId = sku.getGoodsId(); + this.goodsName = sku.getGoodsName(); + this.price = sku.getPrice(); + this.storeName = sku.getStoreName(); + this.storeId = sku.getStoreId(); + this.thumbnail = sku.getThumbnail(); + this.categoryPath = sku.getCategoryPath(); + this.goodsVideo = sku.getGoodsVideo(); + this.mobileIntro = sku.getMobileIntro(); + this.buyCount = sku.getBuyCount(); + this.commentNum = sku.getCommentNum(); + this.small = sku.getSmall(); + this.brandId = sku.getBrandId(); + this.sn = sku.getSn(); + this.storeCategoryPath = sku.getStoreCategoryPath(); + this.sellingPoint = sku.getSellingPoint(); + this.selfOperated = sku.getSelfOperated(); + this.salesModel = sku.getSalesModel(); + this.marketEnable = sku.getMarketEnable(); + this.isAuth = sku.getIsAuth(); + this.intro = sku.getIntro(); + this.grade = sku.getGrade(); + this.releaseTime = new Date(); + if (StringUtils.isNotEmpty(sku.getSpecs())) { + List attributes = new ArrayList<>(); + JSONObject jsonObject = JSONUtil.parseObj(sku.getSpecs()); + for (Map.Entry entry : jsonObject.entrySet()) { + if (!entry.getKey().equals("images")) { + EsGoodsAttribute attribute = new EsGoodsAttribute(); + attribute.setType(0); + attribute.setName(entry.getKey()); + attribute.setValue(entry.getValue().toString()); + attributes.add(attribute); + } + } + this.attrList = attributes; + } + } + + } +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/dos/EsGoodsRelatedInfo.java b/framework/src/main/java/cn/lili/modules/search/entity/dos/EsGoodsRelatedInfo.java new file mode 100644 index 00000000..d45a8be1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/dos/EsGoodsRelatedInfo.java @@ -0,0 +1,34 @@ +package cn.lili.modules.search.entity.dos; + +import cn.lili.modules.search.entity.dto.ParamOptions; +import cn.lili.modules.search.entity.dto.SelectorOptions; +import lombok.Data; + +import java.util.List; + +/** + * 搜索相关商品品牌名称,分类名称及属性 + * + * @author paulG + * @date 2020/10/20 + **/ +@Data +public class EsGoodsRelatedInfo { + + /** + * 分类集合 + */ + List categories; + + /** + * 品牌集合 + */ + List brands; + + /** + * 参数集合 + */ + List paramOptions; + + +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/dto/EsGoods.java b/framework/src/main/java/cn/lili/modules/search/entity/dto/EsGoods.java new file mode 100644 index 00000000..e2c4da78 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/dto/EsGoods.java @@ -0,0 +1,21 @@ +package cn.lili.modules.search.entity.dto; + +import lombok.Data; + +/** + * 商品搜索结果实体 + * + * @author paulG + * @date 2020/10/16 + **/ +@Data +public class EsGoods { + + private String skuId; + + private String goodsId; + + private String goodsName; + + +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/dto/EsGoodsSearchDTO.java b/framework/src/main/java/cn/lili/modules/search/entity/dto/EsGoodsSearchDTO.java new file mode 100644 index 00000000..7451615f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/dto/EsGoodsSearchDTO.java @@ -0,0 +1,44 @@ +package cn.lili.modules.search.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author paulG + * @date 2020/10/15 + **/ +@Data +public class EsGoodsSearchDTO { + + @ApiModelProperty(value = "关键字") + private String keyword; + + @ApiModelProperty(value = "分类") + private String categoryId; + + @ApiModelProperty(value = "品牌,可以多选 品牌Id@品牌Id@品牌Id") + private String brandId; + + @ApiModelProperty(value = "价格", example = "10_30") + private String price; + + @ApiModelProperty(value = "属性:参数名_参数值@参数名_参数值", example = "屏幕类型_LED@屏幕尺寸_15英寸") + private String prop; + + @ApiModelProperty(value = "规格项列表") + private List nameIds; + + @ApiModelProperty(value = "卖家id,搜索店铺商品的时候使用") + private String storeId; + + @ApiModelProperty(value = "商家分组id,搜索店铺商品的时候使用") + private String storeCatId; + + @ApiModelProperty(hidden = true) + private Map> notShowCol = new HashMap<>(); + +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/dto/ParamOptions.java b/framework/src/main/java/cn/lili/modules/search/entity/dto/ParamOptions.java new file mode 100644 index 00000000..141d1e54 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/dto/ParamOptions.java @@ -0,0 +1,20 @@ +package cn.lili.modules.search.entity.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 参数属性选择器 + * + * @author paulG + * @date 2020/10/20 + **/ +@Data +public class ParamOptions { + + private String key; + + private List values; + +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/dto/SelectorOptions.java b/framework/src/main/java/cn/lili/modules/search/entity/dto/SelectorOptions.java new file mode 100644 index 00000000..1264c9a7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/dto/SelectorOptions.java @@ -0,0 +1,23 @@ +package cn.lili.modules.search.entity.dto; + +import lombok.Data; + +import java.util.List; + +/** + * @author paulG + * @date 2020/10/20 + **/ +@Data +public class SelectorOptions { + + private String name; + + private String value; + + private String url; + + private List otherOptions; + + +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/enums/HotWordsRedisKeyEnum.java b/framework/src/main/java/cn/lili/modules/search/entity/enums/HotWordsRedisKeyEnum.java new file mode 100644 index 00000000..c9ba5a38 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/enums/HotWordsRedisKeyEnum.java @@ -0,0 +1,21 @@ +package cn.lili.modules.search.entity.enums; + +/** + * @author paulG + * @date 2021/1/20 + **/ +public enum HotWordsRedisKeyEnum { + + SEARCH_HOT_WORD("搜索热词"); + + + private final String description; + + HotWordsRedisKeyEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } +} diff --git a/framework/src/main/java/cn/lili/modules/search/entity/vo/CustomWordsVO.java b/framework/src/main/java/cn/lili/modules/search/entity/vo/CustomWordsVO.java new file mode 100644 index 00000000..51899c0a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/entity/vo/CustomWordsVO.java @@ -0,0 +1,17 @@ +package cn.lili.modules.search.entity.vo; + +import cn.lili.modules.search.entity.dos.CustomWords; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author paulG + * @date 2020/12/7 + **/ +@EqualsAndHashCode(callSuper = true) +@Data +public class CustomWordsVO extends CustomWords { + + private static final long serialVersionUID = 143299060233417009L; + +} diff --git a/framework/src/main/java/cn/lili/modules/search/mapper/CustomWordsMapper.java b/framework/src/main/java/cn/lili/modules/search/mapper/CustomWordsMapper.java new file mode 100644 index 00000000..10c04a76 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/mapper/CustomWordsMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.search.mapper; + +import cn.lili.modules.search.entity.dos.CustomWords; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 自定义分词数据处理层 + * + * @author paulG + * @date 2020/10/15 + **/ +public interface CustomWordsMapper extends BaseMapper { +} diff --git a/framework/src/main/java/cn/lili/modules/search/repository/EsGoodsIndexRepository.java b/framework/src/main/java/cn/lili/modules/search/repository/EsGoodsIndexRepository.java new file mode 100644 index 00000000..3c5e9cf7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/repository/EsGoodsIndexRepository.java @@ -0,0 +1,12 @@ +package cn.lili.modules.search.repository; + +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; + +/** + * @author paulG + * @date 2020/10/15 + **/ +public interface EsGoodsIndexRepository extends ElasticsearchRepository { + +} diff --git a/framework/src/main/java/cn/lili/modules/search/service/CustomWordsService.java b/framework/src/main/java/cn/lili/modules/search/service/CustomWordsService.java new file mode 100644 index 00000000..6ca9f654 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/service/CustomWordsService.java @@ -0,0 +1,64 @@ +package cn.lili.modules.search.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.search.entity.dos.CustomWords; +import cn.lili.modules.search.entity.vo.CustomWordsVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 自定义分词业务层 + * + * @author paulG + * @date 2020/10/15 + **/ +public interface CustomWordsService extends IService { + + /** + * 自定义分词部署替换 + * @return 替换的内容 + */ + String deploy(); + + /** + * 是否存在分词 + * @param words 分词 + * @return 是否存在 + */ + boolean existWords(String words); + + /** + * 添加自定义分词 + * + * @param customWordsVO 自定义分词信息 + * @return 是否添加成功 + */ + boolean addCustomWords(CustomWordsVO customWordsVO); + + + /** + * 修改自定义分词 + * + * @param customWordsVO 自定义分词信息 + * @return 是否修改成功 + */ + boolean updateCustomWords(CustomWordsVO customWordsVO); + + /** + * 删除自定义分词 + * + * @param id 自定义分词id + * @return 是否删除成功 + */ + boolean deleteCustomWords(String id); + + /** + * 分页查询自定义分词 + * + * @param words 分词 + * @param pageVo 分页信息 + * @return 自定义分词分页信息 + */ + IPage getCustomWordsByPage(String words, PageVO pageVo); + +} diff --git a/framework/src/main/java/cn/lili/modules/search/service/EsGoodsIndexService.java b/framework/src/main/java/cn/lili/modules/search/service/EsGoodsIndexService.java new file mode 100644 index 00000000..9ac0ab64 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/service/EsGoodsIndexService.java @@ -0,0 +1,145 @@ +package cn.lili.modules.search.service; + +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; + +import java.util.List; +import java.util.Map; + +/** + * 商品索引业务层 + * + * @author paulG + * @date 2020/10/14 + **/ +public interface EsGoodsIndexService { + + /** + * 添加商品索引 + * + * @param goods 商品索引信息 + */ + void addIndex(EsGoodsIndex goods); + + /** + * 更新商品索引 + * + * @param goods 商品索引信息 + */ + void updateIndex(EsGoodsIndex goods); + + /** + * 更新商品索引的购买数量 + * + * @param id 商品索引id + * @param buyCount 更新后的购买数量 + */ + void updateIndexBuyNum(String id, Integer buyCount); + + /** + * 更新商品索引的评论相关数据 + * + * @param id 商品索引ID + * @param commentNum 评论数量 + * @param highPraiseNum 好评数量 + * @param grade 好评率 + */ + void updateIndexCommentNum(String id, Integer commentNum, Integer highPraiseNum, Double grade); + + /** + * 删除索引 + * + * @param goods 商品索引信息 + */ + void deleteIndex(EsGoodsIndex goods); + + /** + * 删除索引 + * + * @param id 商品索引信息 + */ + void deleteIndexById(String id); + + /** + * 初始化商品索引 + * + * @param goodsIndexList 商品索引列表 + */ + void initIndex(List goodsIndexList); + + /** + * 更新商品索引的促销信息 + * + * @param id id(skuId) + * @param promotion 促销信息 + * @param key 促销信息的key + * @param price 促销价格 + */ + void updateEsGoodsIndex(String id, BasePromotion promotion, String key, Double price); + + /** + * 根据列表更新商品索引的促销信息 + * + * @param promotionGoodsList 促销商品列表 + * @param promotion 促销信息 + * @param key 促销信息的key + */ + void updateEsGoodsIndexByList(List promotionGoodsList, BasePromotion promotion, String key); + + /** + * 更新全部商品索引的促销信息 + * + * @param promotion 促销信息 + * @param key 促销信息的key + */ + void updateEsGoodsIndexAllByList(BasePromotion promotion, String key); + + /** + * 删除指定商品的促销信息 + * + * @param skuIds skuId列表 + * @param promotionType 促销类型 + */ + void deleteEsGoodsPromotionIndexByList(List skuIds, PromotionTypeEnum promotionType); + + /** + * 清除所以商品索引的无效促销活动 + */ + void cleanInvalidPromotion(); + + /** + * 根据id获取商品索引信息 + * + * @param id skuId + * @return 商品索引信息 + */ + EsGoodsIndex findById(String id); + + /** + * 根据id获取商品索引信息的促销信息 + * + * @param id skuId + * @return 促销信息map + */ + Map getPromotionMap(String id); + + /** + * 根据id获取商品索引信息的指定促销活动的id + * + * @param id skuId + * @param promotionTypeEnum 促销活动类型 + * @return 当前商品参与的促销活动id集合 + */ + List getPromotionIdByPromotionType(String id, PromotionTypeEnum promotionTypeEnum); + + /** + * 重置当前商品索引 + * + * @param goodsSku 商品sku信息 + * @return 商品索引 + */ + EsGoodsIndex resetEsGoodsIndex(GoodsSku goodsSku); +} diff --git a/framework/src/main/java/cn/lili/modules/search/service/EsGoodsSearchService.java b/framework/src/main/java/cn/lili/modules/search/service/EsGoodsSearchService.java new file mode 100644 index 00000000..79cbdf70 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/service/EsGoodsSearchService.java @@ -0,0 +1,46 @@ +package cn.lili.modules.search.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.entity.dos.EsGoodsRelatedInfo; +import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO; +import org.springframework.data.domain.Page; + +import java.util.List; + +/** + * ES商品搜索业务层 + * @author paulG + * @date 2020/10/15 + **/ +public interface EsGoodsSearchService { + + /** + * 商品搜索 + * + * @param searchDTO 搜索参数 + * @param pageVo 分页参数 + * @return 搜索结果 + */ + Page searchGoods(EsGoodsSearchDTO searchDTO, PageVO pageVo); + + /** + * 获取热门关键词 + * + * @param start 查询范围开始位置 + * @param end 查询范围结束位置 + * @return + */ + List getHotWords(Integer start, Integer end); + + /** + * 获取筛选器 + * + * @param goodsSearch 搜索条件 + * @param pageVo 分页参数 + * @return Map + */ + EsGoodsRelatedInfo getSelector(EsGoodsSearchDTO goodsSearch, PageVO pageVo); + + List getEsGoodsBySkuIds(List skuIds); +} diff --git a/framework/src/main/java/cn/lili/modules/search/serviceimpl/CustomWordsServiceImpl.java b/framework/src/main/java/cn/lili/modules/search/serviceimpl/CustomWordsServiceImpl.java new file mode 100644 index 00000000..a4185096 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/serviceimpl/CustomWordsServiceImpl.java @@ -0,0 +1,119 @@ +package cn.lili.modules.search.serviceimpl; + +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.config.context.ThreadContextHolder; +import cn.lili.modules.search.entity.dos.CustomWords; +import cn.lili.modules.search.entity.vo.CustomWordsVO; +import cn.lili.modules.search.mapper.CustomWordsMapper; +import cn.lili.modules.search.service.CustomWordsService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 自定义分词业务层实现 + * @author paulG + * @date 2020/10/15 + **/ +@Service +public class CustomWordsServiceImpl extends ServiceImpl implements CustomWordsService { + + + @Override + public String deploy() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper().eq(CustomWords::getDisabled, 1); + List list = list(queryWrapper); + + HttpServletResponse response = ThreadContextHolder.getHttpResponse(); + StringBuilder builder = new StringBuilder(); + + if (list != null && !list.isEmpty()) { + boolean flag = true; + for (CustomWords customWords : list) { + if (flag) { + try { + response.setHeader("Last-Modified", customWords.getCreateTime().toString()); + response.setHeader("ETag", customWords.getUpdateTime().toString()); + } catch (Exception e) { + e.printStackTrace(); + } + builder.append(customWords.getName()); + flag = false; + } else { + builder.append("\n"); + builder.append(customWords.getName()); + } + } + } + + return builder.toString(); + } + + /** + * 添加自定义分词 + * + * @param customWordsVO 自定义分词信息 + * @return 是否添加成功 + */ + @Override + public boolean addCustomWords(CustomWordsVO customWordsVO) { + if (this.existWords(customWordsVO.getName())) { + throw new ServiceException("当前自定义分词已存在!"); + } + return this.save(customWordsVO); + } + + /** + * 删除自定义分词 + * + * @param id 自定义分词id + * @return 是否删除成功 + */ + @Override + public boolean deleteCustomWords(String id) { + if (this.getById(id) == null) { + throw new ServiceException("当前自定义分词不存在!"); + } + return this.removeById(id); + } + + /** + * 修改自定义分词 + * + * @param customWordsVO 自定义分词信息 + * @return 是否修改成功 + */ + @Override + public boolean updateCustomWords(CustomWordsVO customWordsVO) { + if (this.getById(customWordsVO.getId()) == null) { + throw new ServiceException("当前自定义分词不存在!"); + } + return this.updateById(customWordsVO); + } + + /** + * 分页查询自定义分词 + * + * @param words 分词 + * @param pageVo 分页信息 + * @return 自定义分词分页信息 + */ + @Override + public IPage getCustomWordsByPage(String words, PageVO pageVo) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper().like(CustomWords::getName, words); + return this.page(PageUtil.initPage(pageVo), queryWrapper); + } + + @Override + public boolean existWords(String words) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper().eq(CustomWords::getName, words); + int count = count(queryWrapper); + return count > 0; + } +} diff --git a/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsIndexServiceImpl.java b/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsIndexServiceImpl.java new file mode 100644 index 00000000..750b2111 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsIndexServiceImpl.java @@ -0,0 +1,391 @@ +package cn.lili.modules.search.serviceimpl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.extra.pinyin.PinyinUtil; +import cn.lili.common.elasticsearch.BaseElasticsearchService; +import cn.lili.common.elasticsearch.EsSuffix; +import cn.lili.config.elasticsearch.ElasticsearchProperties; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.dos.GoodsWords; +import cn.lili.modules.goods.entity.enums.GoodsWordsTypeEnum; +import cn.lili.modules.goods.service.GoodsWordsService; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.service.PromotionService; +import cn.lili.modules.search.entity.dos.EsGoodsAttribute; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO; +import cn.lili.modules.search.repository.EsGoodsIndexRepository; +import cn.lili.modules.search.service.EsGoodsIndexService; +import cn.lili.modules.search.service.EsGoodsSearchService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.assertj.core.util.IterableUtil; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.indices.AnalyzeRequest; +import org.elasticsearch.client.indices.AnalyzeResponse; +import org.elasticsearch.search.SearchHit; +import org.mybatis.spring.MyBatisSystemException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 商品索引业务层实现 + * @author paulG + * @since 2020/10/14 + **/ +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements EsGoodsIndexService { + + private final ElasticsearchProperties elasticsearchProperties; + private final EsGoodsIndexRepository goodsIndexRepository; + private final EsGoodsSearchService goodsSearchService; + private final GoodsWordsService goodsWordsService; + + private PromotionService promotionService; + + @Autowired + public void setPromotionService(PromotionService promotionService) { + this.promotionService = promotionService; + } + + @Override + public void addIndex(EsGoodsIndex goods) { + String indexName = elasticsearchProperties.getIndexPrefix() + "_" + EsSuffix.GOODS_INDEX_NAME; + try { + AnalyzeRequest analyzeRequest = AnalyzeRequest.withIndexAnalyzer(indexName, "ik_max_word", goods.getGoodsName()); + AnalyzeResponse analyze = client.indices().analyze(analyzeRequest, RequestOptions.DEFAULT); + List tokens = analyze.getTokens(); + if (goods.getAttrList() != null && !goods.getAttrList().isEmpty()) { + for (EsGoodsAttribute esGoodsAttribute : goods.getAttrList()) { + wordsToDb(esGoodsAttribute.getValue()); + } + } + for (AnalyzeResponse.AnalyzeToken token : tokens) { + wordsToDb(token.getTerm()); + } + goodsIndexRepository.save(goods); + } catch (IOException e) { + log.error("为商品[" + goods.getGoodsName() + "]生成索引异常", e); + } + } + + @Override + public void updateIndex(EsGoodsIndex goods) { + goodsIndexRepository.save(goods); + } + + /** + * 更新商品索引的购买数量 + * + * @param id 商品索引id + * @param buyCount 更新后的购买数量 + */ + @Override + public void updateIndexBuyNum(String id, Integer buyCount) { + EsGoodsIndex goodsIndex = this.findById(id); + goodsIndex.setBuyCount(buyCount); + this.updateIndex(goodsIndex); + } + + /** + * 更新商品索引的评论相关数据 + * + * @param id 商品索引ID + * @param commentNum 评论数量 + * @param highPraiseNum 好评数量 + * @param grade 好评率 + */ + @Override + public void updateIndexCommentNum(String id, Integer commentNum, Integer highPraiseNum, Double grade) { + EsGoodsIndex goodsIndex = this.findById(id); + goodsIndex.setCommentNum(commentNum); + goodsIndex.setHighPraiseNum(highPraiseNum); + goodsIndex.setGrade(grade); + this.updateIndex(goodsIndex); + } + + @Override + public void deleteIndex(EsGoodsIndex goods) { + if (ObjectUtils.isEmpty(goods)) { + // 如果对象为空,则删除全量 + goodsIndexRepository.deleteAll(); + } + goodsIndexRepository.delete(goods); + } + + /** + * 删除索引 + * + * @param id 商品索引信息 + */ + @Override + public void deleteIndexById(String id) { + goodsIndexRepository.deleteById(id); + } + + @Override + public void initIndex(List goodsIndexList) { + String indexName = elasticsearchProperties.getIndexPrefix() + "_" + EsSuffix.GOODS_INDEX_NAME; + // deleteIndexRequest(indexName); + if (!indexExist(indexName)) { + createIndexRequest(indexName); + } + if (goodsIndexList != null && !goodsIndexList.isEmpty()) { + goodsIndexRepository.deleteAll(); + for (EsGoodsIndex goodsIndex : goodsIndexList) { + addIndex(goodsIndex); + } + } + } + + @Override + public void updateEsGoodsIndex(String id, BasePromotion promotion, String key, Double price) { + EsGoodsIndex goodsIndex = findById(id); + if (goodsIndex != null) { + if (promotion.getPromotionStatus().equals(PromotionStatusEnum.START.name()) && price != null) { + goodsIndex.setPromotionPrice(price); + } else { + goodsIndex.setPromotionPrice(goodsIndex.getPrice()); + } + this.updateGoodsIndexPromotion(goodsIndex, key, promotion); + } else { + log.error("更新索引商品促销信息失败!skuId 为 【{}】的索引不存在!", id); + } + } + + @Override + public void updateEsGoodsIndexByList(List promotionGoodsList, BasePromotion promotion, String key) { + if (promotionGoodsList != null) { + for (PromotionGoods promotionGoods : promotionGoodsList) { + updateEsGoodsIndex(promotionGoods.getSkuId(), promotion, key, promotionGoods.getPrice()); + } + } + + } + + /** + * 更新全部商品索引的促销信息 + * + * @param promotion 促销信息 + * @param key 促销信息的key + */ + @Override + public void updateEsGoodsIndexAllByList(BasePromotion promotion, String key) { + List goodsIndices; + if (promotion.getStoreId() != null) { + EsGoodsSearchDTO searchDTO = new EsGoodsSearchDTO(); + searchDTO.setStoreId(promotion.getStoreId()); + Page esGoodsIndices = goodsSearchService.searchGoods(searchDTO, null); + goodsIndices = esGoodsIndices.getContent(); + } else { + Iterable all = goodsIndexRepository.findAll(); + goodsIndices = new ArrayList<>(IterableUtil.toCollection(all)); + } + for (EsGoodsIndex goodsIndex : goodsIndices) { + this.updateGoodsIndexPromotion(goodsIndex, key, promotion); + } + } + + @Override + public void deleteEsGoodsPromotionIndexByList(List skuIds, PromotionTypeEnum promotionType) { + for (String skuId : skuIds) { + EsGoodsIndex goodsIndex = findById(skuId); + if (goodsIndex != null) { + Map promotionMap = goodsIndex.getPromotionMap(); + if (promotionMap != null && !promotionMap.isEmpty()) { + // 如果存在同类型促销活动删除 + List collect = promotionMap.keySet().parallelStream().filter(i -> i.contains(promotionType.name())).collect(Collectors.toList()); + collect.forEach(promotionMap::remove); + goodsIndex.setPromotionMap(promotionMap); + updateIndex(goodsIndex); + } + } else { + log.error("更新索引商品促销信息失败!skuId 为 【{}】的索引不存在!", skuId); + } + } + } + + /** + * 清除所以商品索引的无效促销活动 + */ + @Override + public void cleanInvalidPromotion() { + Iterable all = goodsIndexRepository.findAll(); + for (EsGoodsIndex goodsIndex : all) { + Map promotionMap = goodsIndex.getPromotionMap(); + if (promotionMap != null && !promotionMap.isEmpty()) { + for (Map.Entry entry : promotionMap.entrySet()) { + BasePromotion promotion = (BasePromotion) entry.getValue(); + if (promotion.getEndTime().getTime() > DateUtil.date().getTime()) { + promotionMap.remove(entry.getKey()); + } + } + } + } + goodsIndexRepository.saveAll(all); + } + + @Override + public EsGoodsIndex findById(String id) { + Optional goodsIndex = goodsIndexRepository.findById(id); + if (!goodsIndex.isPresent()) { + log.error("商品skuId为" + id + "的es索引不存在!"); + return null; + } + return goodsIndex.get(); + } + + /** + * 根据id获取商品索引信息的促销信息 + * + * @param id skuId + * @return 促销信息map + */ + @Override + public Map getPromotionMap(String id) { + EsGoodsIndex goodsIndex = this.findById(id); + if (goodsIndex != null) { + Map promotionMap = goodsIndex.getPromotionMap(); + if (promotionMap == null || promotionMap.isEmpty()) { + return new HashMap<>(); + } + return promotionMap; + } + return null; + } + + /** + * 根据id获取商品索引信息的指定促销活动的id + * + * @param id skuId + * @param promotionTypeEnum 促销活动类型 + * @return 当前商品参与的促销活动id集合 + */ + @Override + public List getPromotionIdByPromotionType(String id, PromotionTypeEnum promotionTypeEnum) { + Map promotionMap = this.getPromotionMap(id); + if (promotionMap == null || promotionMap.isEmpty()) { + return new ArrayList<>(); + } + List keyCollect = promotionMap.keySet().stream().filter(i -> i.contains(promotionTypeEnum.name())).collect(Collectors.toList()); + List promotionIds = new ArrayList<>(); + for (String key : keyCollect) { + BasePromotion promotion = (BasePromotion) promotionMap.get(key); + promotionIds.add(promotion.getId()); + } + return promotionIds; + } + + /** + * 重置当前商品索引 + * + * @param goodsSku 商品sku信息 + * @return 商品索引 + */ + @Override + public EsGoodsIndex resetEsGoodsIndex(GoodsSku goodsSku) { + EsGoodsIndex index = new EsGoodsIndex(goodsSku); + Map goodsCurrentPromotionMap = promotionService.getGoodsCurrentPromotionMap(index); + index.setPromotionMap(goodsCurrentPromotionMap); + this.addIndex(index); + return index; + } + + private void updateGoodsIndexPromotion(EsGoodsIndex goodsIndex, String key, BasePromotion promotion) { + Map promotionMap; + if (goodsIndex.getPromotionMap() == null || goodsIndex.getPromotionMap().isEmpty()) { + promotionMap = new HashMap<>(1); + } else { + promotionMap = goodsIndex.getPromotionMap(); + } + + + if (promotion.getPromotionStatus().equals(PromotionStatusEnum.END.name()) || promotion.getPromotionStatus().equals(PromotionStatusEnum.CLOSE.name())) { + if (promotionMap.containsKey(key)) { + promotionMap.remove(key); + } else { + this.removePromotionKey(key, promotionMap, PromotionTypeEnum.SECKILL.name()); + } + } else { + // 添加促销活动前,如果是同一时间只可以有一个的活动,但商品索引的促销活动里存在其他(同一时间只可以有一个)的活动,则清除 + this.removePromotionKey(key, promotionMap, PromotionTypeEnum.PINTUAN.name(), PromotionTypeEnum.SECKILL.name(), PromotionTypeEnum.FULL_DISCOUNT.name()); + promotionMap.put(key, promotion); + } + goodsIndex.setPromotionMap(promotionMap); + updateIndex(goodsIndex); + } + + /** + * 移除需要移除的促销活动 + * + * @param currentKey 当前操作的促销活动key + * @param promotionMap 促销活动 + * @param needRemoveKeys 需移除的促销活动 + */ + private void removePromotionKey(String currentKey, Map promotionMap, String... needRemoveKeys) { + if (CharSequenceUtil.containsAny(currentKey, needRemoveKeys)) { + List removeKeys = new ArrayList<>(); + for (String entry : promotionMap.keySet()) { + for (String needRemoveKey : needRemoveKeys) { + if (entry.contains(needRemoveKey) && currentKey.contains(needRemoveKey)) { + removeKeys.add(entry); + break; + } + } + } + promotionMap.keySet().removeAll(removeKeys); + } + } + + /** + * 将商品关键字入库 + * + * @param words 商品关键字 + */ + private void wordsToDb(String words) { + try { + // 是否有重复 + GoodsWords entity = goodsWordsService.getOne(new LambdaQueryWrapper().eq(GoodsWords::getWords, words)); + if (entity == null) { + GoodsWords goodsWords = new GoodsWords(); + goodsWords.setWords(words); + goodsWords.setWholeSpell(PinyinUtil.getPinyin(words, "")); + goodsWords.setAbbreviate(PinyinUtil.getFirstLetter(words, "")); + goodsWords.setType(GoodsWordsTypeEnum.SYSTEM.name()); + goodsWords.setSort(0); + goodsWordsService.save(goodsWords); + } + } catch (MyBatisSystemException e) { + log.error(words + "关键字已存在!"); + } + } + + public List searchList(String index) { + SearchResponse searchResponse = search(index); + SearchHit[] hits = searchResponse.getHits().getHits(); + List goodsIndices = new ArrayList<>(); + Arrays.stream(hits).forEach(hit -> { + Map sourceAsMap = hit.getSourceAsMap(); + EsGoodsIndex person = BeanUtil.mapToBean(sourceAsMap, EsGoodsIndex.class, false, CopyOptions.create()); + goodsIndices.add(person); + }); + return goodsIndices; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsSearchServiceImpl.java b/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsSearchServiceImpl.java new file mode 100644 index 00000000..4f7da41e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsSearchServiceImpl.java @@ -0,0 +1,373 @@ +package cn.lili.modules.search.serviceimpl; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.Brand; +import cn.lili.modules.goods.entity.dos.Category; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.service.BrandService; +import cn.lili.modules.goods.service.CategoryService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.entity.dos.EsGoodsRelatedInfo; +import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO; +import cn.lili.modules.search.entity.dto.ParamOptions; +import cn.lili.modules.search.entity.dto.SelectorOptions; +import cn.lili.modules.search.entity.enums.HotWordsRedisKeyEnum; +import cn.lili.modules.search.repository.EsGoodsIndexRepository; +import cn.lili.modules.search.service.EsGoodsSearchService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.lucene.search.join.ScoreMode; +import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; +import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.AggregationBuilder; +import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested; +import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms; +import org.elasticsearch.search.aggregations.bucket.terms.Terms; +import org.elasticsearch.search.sort.SortBuilders; +import org.elasticsearch.search.sort.SortOrder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; +import org.springframework.data.redis.core.DefaultTypedTuple; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * ES商品搜索业务层实现 + * @author paulG + * @since 2020/10/16 + **/ +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class EsGoodsSearchServiceImpl implements EsGoodsSearchService { + + private static final String ATTR_PATH = "attrList"; + private static final String ATTR_VALUE = "attrList.value"; + private static final String ATTR_NAME = "attrList.name"; + private static final String ATTR_NAME_KEY = "nameList"; + private static final String ATTR_VALUE_KEY = "valueList"; + + private final EsGoodsIndexRepository goodsIndexRepository; + //商品分类 + private final CategoryService categoryService; + //品牌 + private final BrandService brandService; + //ES + private final ElasticsearchRestTemplate restTemplate; + //缓存 + private final Cache cache; + + @Override + public Page searchGoods(EsGoodsSearchDTO searchDTO, PageVO pageVo) { + if (CharSequenceUtil.isNotEmpty(searchDTO.getKeyword())) { + cache.incrementScore(HotWordsRedisKeyEnum.SEARCH_HOT_WORD.name(), searchDTO.getKeyword()); + } + NativeSearchQueryBuilder searchQueryBuilder = createSearchQueryBuilder(searchDTO, pageVo, false); + NativeSearchQuery searchQuery = searchQueryBuilder.build(); + log.info("searchGoods DSL:{}", searchQuery.getQuery()); + + return goodsIndexRepository.search(searchQuery); + } + + @Override + public List getHotWords(Integer start, Integer end) { + List hotWords = new ArrayList<>(); + Set set = cache.reverseRangeWithScores(HotWordsRedisKeyEnum.SEARCH_HOT_WORD.name(), start, end); + for (DefaultTypedTuple defaultTypedTuple : set) { + hotWords.add(Objects.requireNonNull(defaultTypedTuple.getValue()).toString()); + } + return hotWords; + } + + @Override + public EsGoodsRelatedInfo getSelector(EsGoodsSearchDTO goodsSearch, PageVO pageVo) { + NativeSearchQueryBuilder builder = createSearchQueryBuilder(goodsSearch, null, true); + //分类 + builder.addAggregation(AggregationBuilders.terms("categoryAgg").field("categoryPath")); + //品牌 + builder.addAggregation(AggregationBuilders.terms("brandAgg").field("brandId").size(Integer.MAX_VALUE)); + //参数 + AggregationBuilder valuesBuilder = AggregationBuilders.terms("valueAgg").field(ATTR_VALUE); + AggregationBuilder paramsNameBuilder = AggregationBuilders.terms("nameAgg").field(ATTR_NAME).subAggregation(valuesBuilder); + builder.addAggregation(AggregationBuilders.nested("attrAgg", ATTR_PATH).subAggregation(paramsNameBuilder)); + NativeSearchQuery searchQuery = builder.build(); + SearchHits search = restTemplate.search(searchQuery, EsGoodsIndex.class); + + log.info("getSelector DSL:{}", searchQuery.getQuery()); + Map aggregationMap = Objects.requireNonNull(search.getAggregations()).getAsMap(); + return convertToEsGoodsRelatedInfo(aggregationMap, goodsSearch); + } + + @Override + public List getEsGoodsBySkuIds(List skuIds) { + NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder(); + NativeSearchQuery build = searchQueryBuilder.build(); + build.setIds(skuIds); + return restTemplate.multiGet(build, EsGoodsIndex.class, restTemplate.getIndexCoordinatesFor(EsGoodsIndex.class)); + } + + /** + * 转换搜索结果为聚合商品展示信息 + * + * @param aggregationMap 搜索结果 + * @return 聚合商品展示信息 + */ + private EsGoodsRelatedInfo convertToEsGoodsRelatedInfo(Map aggregationMap, EsGoodsSearchDTO goodsSearch) { + EsGoodsRelatedInfo esGoodsRelatedInfo = new EsGoodsRelatedInfo(); + // 分类 + List categoryOptions = new ArrayList<>(); + ParsedStringTerms categoryTerms = (ParsedStringTerms) aggregationMap.get("categoryAgg"); + List categoryBuckets = categoryTerms.getBuckets(); + if (categoryBuckets != null && !categoryBuckets.isEmpty()) { + for (Terms.Bucket categoryBucket : categoryBuckets) { + String categoryPath = categoryBucket.getKey().toString(); + String[] split = categoryPath.split(","); + for (String s : split) { + if (CharSequenceUtil.isNotEmpty(s)) { + Category category = categoryService.getById(s); + if (category != null) { + SelectorOptions so = new SelectorOptions(); + so.setName(category.getName()); + so.setValue(category.getId()); + if (!categoryOptions.contains(so)) { + categoryOptions.add(so); + } + } + } + } + + } + } + esGoodsRelatedInfo.setCategories(categoryOptions); + + //品牌 + ParsedStringTerms brandTerms = (ParsedStringTerms) aggregationMap.get("brandAgg"); + List brandBuckets = brandTerms.getBuckets(); + List brandOptions = new ArrayList<>(); + if (brandBuckets != null && !brandBuckets.isEmpty()) { + for (Terms.Bucket brandBucket : brandBuckets) { + if (CharSequenceUtil.isNotEmpty(goodsSearch.getBrandId())) { + List brandList = Arrays.asList(goodsSearch.getBrandId().split("@")); + if (brandList.contains(brandBucket.getKey().toString())) { + continue; + } + } + Brand brand = brandService.getById(brandBucket.getKey().toString()); + if (brand != null) { + SelectorOptions so = new SelectorOptions(); + so.setName(brand.getName()); + so.setValue(brand.getId()); + so.setUrl(brand.getLogo()); + brandOptions.add(so); + } + } + } + esGoodsRelatedInfo.setBrands(brandOptions); + + //参数 + ParsedNested attrTerms = (ParsedNested) aggregationMap.get("attrAgg"); + if (!goodsSearch.getNotShowCol().isEmpty()) { + if (goodsSearch.getNotShowCol().containsKey(ATTR_NAME_KEY) && goodsSearch.getNotShowCol().containsKey(ATTR_VALUE_KEY)) { + esGoodsRelatedInfo.setParamOptions(buildGoodsParam(attrTerms, goodsSearch.getNotShowCol().get(ATTR_NAME_KEY), goodsSearch.getNotShowCol().get(ATTR_VALUE_KEY))); + } + } else { + esGoodsRelatedInfo.setParamOptions(buildGoodsParam(attrTerms, null, null)); + } + + return esGoodsRelatedInfo; + } + + /** + * 构建商品参数信息 + * + * @param attrTerms 商品参数搜索结果 + * @param nameList 查询的规格名 + * @param valueList 查询的规格项 + * @return 商品参数信息 + */ + private List buildGoodsParam(ParsedNested attrTerms, List nameList, List valueList) { + List paramOptions = new ArrayList<>(); + if (attrTerms != null) { + Aggregations attrAggregations = attrTerms.getAggregations(); + Map attrMap = attrAggregations.getAsMap(); + ParsedStringTerms nameAgg = (ParsedStringTerms) attrMap.get("nameAgg"); + + if (nameAgg != null) { + List nameBuckets = nameAgg.getBuckets(); + + for (Terms.Bucket bucket : nameBuckets) { + String name = bucket.getKey().toString(); + ParamOptions paramOptions1 = new ParamOptions(); + ParsedStringTerms valueAgg = bucket.getAggregations().get("valueAgg"); + List valueBuckets = valueAgg.getBuckets(); + List valueSelectorList = new ArrayList<>(); + + for (Terms.Bucket valueBucket : valueBuckets) { + String value = valueBucket.getKey().toString(); + + if (CharSequenceUtil.isNotEmpty(value)) { + valueSelectorList.add(value); + } + + } + if (nameList == null || !nameList.contains(name)) { + paramOptions1.setKey(name); + paramOptions1.setValues(valueSelectorList); + paramOptions.add(paramOptions1); + } + } + + + } + + } + return paramOptions; + } + + + /** + * 创建es搜索builder + * + * @param searchDTO 搜索条件 + * @param pageVo 分页参数 + * @param isAggregation 是否是聚合查询 + * @return es搜索builder + */ + private NativeSearchQueryBuilder createSearchQueryBuilder(EsGoodsSearchDTO searchDTO, PageVO pageVo, boolean isAggregation) { + NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); + if (pageVo != null) { + Pageable pageable = PageRequest.of(pageVo.getPageNumber(), pageVo.getPageSize()); + //分页 + nativeSearchQueryBuilder.withPageable(pageable); + } + if (searchDTO != null) { + BoolQueryBuilder filterBuilder = QueryBuilders.boolQuery(); + BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery(); + + this.commonSearch(filterBuilder, queryBuilder, searchDTO, isAggregation); + + // 未上架的商品不显示 + filterBuilder.must(QueryBuilders.matchQuery("marketEnable", GoodsStatusEnum.UPPER.name())); + // 待审核和审核不通过的商品不显示 + filterBuilder.must(QueryBuilders.matchQuery("isAuth", GoodsAuthEnum.PASS.name())); + + + // 关键字检索 + if (StringUtils.isEmpty(searchDTO.getKeyword())) { + nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery()); + } else { + this.keywordSearch(filterBuilder, queryBuilder, searchDTO.getKeyword(), isAggregation); + } + + if (isAggregation) { + nativeSearchQueryBuilder.withQuery(filterBuilder); + } else { + nativeSearchQueryBuilder.withQuery(queryBuilder); + nativeSearchQueryBuilder.withFilter(filterBuilder); + } + + + if (pageVo != null && CharSequenceUtil.isNotEmpty(pageVo.getOrder()) && CharSequenceUtil.isNotEmpty(pageVo.getSort())) { + nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(pageVo.getSort()).order(SortOrder.valueOf(pageVo.getOrder().toUpperCase()))); + } else { + nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC)); + } + + } + return nativeSearchQueryBuilder; + } + + private void commonSearch(BoolQueryBuilder filterBuilder, BoolQueryBuilder queryBuilder, EsGoodsSearchDTO searchDTO, boolean isAggregation) { + if (CharSequenceUtil.isNotEmpty(searchDTO.getBrandId())) { + String[] brands = searchDTO.getBrandId().split("@"); + filterBuilder.must(QueryBuilders.termsQuery("brandId", brands)); + } + if (searchDTO.getNameIds() != null && !searchDTO.getNameIds().isEmpty()) { + filterBuilder.must(QueryBuilders.nestedQuery(ATTR_PATH, QueryBuilders.termsQuery("attrList.nameId", searchDTO.getNameIds()), ScoreMode.None)); + } + if (CharSequenceUtil.isNotEmpty(searchDTO.getCategoryId())) { + filterBuilder.must(QueryBuilders.wildcardQuery("categoryPath", "*" + searchDTO.getCategoryId() + "*")); + } + if (CharSequenceUtil.isNotEmpty(searchDTO.getStoreCatId())) { + filterBuilder.must(QueryBuilders.wildcardQuery("storeCategoryPath", "*" + searchDTO.getStoreCatId() + "*")); + } + if (CharSequenceUtil.isNotEmpty(searchDTO.getStoreId())) { + filterBuilder.filter(QueryBuilders.termQuery("storeId", searchDTO.getStoreId())); + } + if (CharSequenceUtil.isNotEmpty(searchDTO.getProp())) { + String[] props = searchDTO.getProp().split("@"); + List nameList = new ArrayList<>(); + List valueList = new ArrayList<>(); + for (String prop : props) { + String[] propValues = prop.split("_"); + String name = propValues[0]; + String value = propValues[1]; + if (!nameList.contains(name)) { + nameList.add(name); + } + if (!valueList.contains(value)) { + valueList.add(value); + } + if (isAggregation) { + filterBuilder.must(QueryBuilders.nestedQuery(ATTR_PATH, QueryBuilders.termQuery(ATTR_NAME, name), ScoreMode.None)); + filterBuilder.should(QueryBuilders.nestedQuery(ATTR_PATH, QueryBuilders.termsQuery(ATTR_VALUE, value), ScoreMode.None)); + } else { + queryBuilder.must(QueryBuilders.nestedQuery(ATTR_PATH, QueryBuilders.termQuery(ATTR_NAME, name), ScoreMode.None)); + queryBuilder.must(QueryBuilders.nestedQuery(ATTR_PATH, QueryBuilders.wildcardQuery(ATTR_VALUE, "*" + value + "*"), ScoreMode.None)); + } + + } + searchDTO.getNotShowCol().put(ATTR_NAME_KEY, nameList); + searchDTO.getNotShowCol().put(ATTR_VALUE_KEY, valueList); + } + if (CharSequenceUtil.isNotEmpty(searchDTO.getPrice())) { + String[] prices = searchDTO.getPrice().split("_"); + if(prices.length==0){ + return; + } + double min = StringUtils.toDouble(prices[0], 0.0); + double max = Integer.MAX_VALUE; + + if (prices.length == 2) { + max = StringUtils.toDouble(prices[1], Double.MAX_VALUE); + } + filterBuilder.must(QueryBuilders.rangeQuery("price").from(min).to(max).includeLower(true).includeUpper(true)); + } + } + + private void keywordSearch(BoolQueryBuilder filterBuilder, BoolQueryBuilder queryBuilder, String keyword, boolean isAggregation) { + List filterFunctionBuilders = new ArrayList<>(); + filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.wildcardQuery("goodsName", "*" + keyword + "*"), + ScoreFunctionBuilders.weightFactorFunction(10))); + filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.nestedQuery(ATTR_PATH, QueryBuilders.wildcardQuery(ATTR_VALUE, "*" + keyword + "*"), ScoreMode.None), + ScoreFunctionBuilders.weightFactorFunction(8))); + FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()]; + filterFunctionBuilders.toArray(builders); + FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders) + .scoreMode(FunctionScoreQuery.ScoreMode.SUM) + .setMinScore(2); + if (isAggregation) { + filterBuilder.must(functionScoreQueryBuilder); + } else { + queryBuilder.must(functionScoreQueryBuilder); + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/aop/PageViewPoint.java b/framework/src/main/java/cn/lili/modules/statistics/aop/PageViewPoint.java new file mode 100644 index 00000000..0a8cb42a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/aop/PageViewPoint.java @@ -0,0 +1,25 @@ +package cn.lili.modules.statistics.aop; + +import cn.lili.modules.statistics.aop.enums.PageViewEnum; + +import java.lang.annotation.*; + +/** + * 埋点统计 + */ +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface PageViewPoint { + + /** + * 描述 + */ + PageViewEnum type(); + + /** + * 如:商品id,店铺id + * 字段类型为string ,支持 spel语法,也可以填写 + */ + String id(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/aop/aspect/PageViewInterceptor.java b/framework/src/main/java/cn/lili/modules/statistics/aop/aspect/PageViewInterceptor.java new file mode 100644 index 00000000..4044c75c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/aop/aspect/PageViewInterceptor.java @@ -0,0 +1,108 @@ +package cn.lili.modules.statistics.aop.aspect; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.utils.SpelUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.connect.util.IpUtils; +import cn.lili.modules.goods.entity.vos.GoodsSkuVO; +import cn.lili.modules.statistics.aop.PageViewPoint; +import cn.lili.modules.statistics.aop.enums.PageViewEnum; +import cn.lili.modules.statistics.util.StatisticsSuffix; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * 页面浏览统计拦截 + * + * @author Chopper + * @date 2021-01-14 18:01 + */ +@Aspect +@Configuration +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PageViewInterceptor { + + private final Cache cache; + + private final HttpServletRequest request; + + + @AfterReturning(returning = "rvt", pointcut = "@annotation(cn.lili.modules.statistics.aop.PageViewPoint)") + public void interceptor(JoinPoint point, Object rvt) { + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + PageViewPoint pageViewPoint = method.getAnnotation(PageViewPoint.class); + PageViewEnum pageViewEnum = pageViewPoint.type(); + //store id 为-1 代表平台访问 + String storeId; + //商品访问 + String goodsId = null; + + switch (pageViewEnum) { + case SKU: + ResultMessage> skuRvt = (ResultMessage>) rvt; + GoodsSkuVO goodsSkuDetail = (GoodsSkuVO) skuRvt.getResult().get("data"); + storeId = goodsSkuDetail.getStoreId(); + goodsId = goodsSkuDetail.getGoodsId(); + break; + case STORE: + Map map = spelFormat(point); + storeId = map.get("id"); + break; + default: + storeId = "-1"; + } + String ip = IpUtils.getIpAddress(request); +// //如果用户不为空,则ip后追加用户id,这样一个用户多个ip登录,可以被多次记录访客数 +// if (UserContext.getCurrentUser() != null) { +// ip += UserContext.getCurrentUser().getId(); +// } + + try { + //PV 统计48小时过期 留下一定时间予以统计累计数据库 + cache.incr(CachePrefix.PV.getPrefix() + StatisticsSuffix.suffix(), 60 * 60 * 48); + + //平台UV统计 + cache.cumulative(CachePrefix.UV.getPrefix() + StatisticsSuffix.suffix(), ip); + + //PV 统计48小时过期 留下一定时间予以统计累计数据库 + cache.incr(CachePrefix.STORE_PV.getPrefix() + StatisticsSuffix.suffix(storeId), 60 * 60 * 48); + + //店铺UV 统计,则需要对id去重复,所以如下处理 + cache.cumulative(CachePrefix.STORE_UV.getPrefix() + StatisticsSuffix.suffix(storeId), ip); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param joinPoint 切点 + * @return 方法描述 + * @throws Exception + */ + private static Map spelFormat(JoinPoint joinPoint) { + + Map result = new HashMap<>(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + PageViewPoint pageViewPoint = signature.getMethod().getAnnotation(PageViewPoint.class); + String id = SpelUtil.compileParams(joinPoint, pageViewPoint.id()); + result.put("id", id); + return result; + } +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/aop/enums/PageViewEnum.java b/framework/src/main/java/cn/lili/modules/statistics/aop/enums/PageViewEnum.java new file mode 100644 index 00000000..fb89dd0f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/aop/enums/PageViewEnum.java @@ -0,0 +1,15 @@ +package cn.lili.modules.statistics.aop.enums; + +/** + * 统计页面类型 + * + * @author Chopper + * @date 2021-01-14 17:55 + */ +public enum PageViewEnum { + /** + * 店铺,商品,其他页面 + */ + STORE, SKU, OTHER + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/mapper/GoodsStatisticsDataMapper.java b/framework/src/main/java/cn/lili/modules/statistics/mapper/GoodsStatisticsDataMapper.java new file mode 100644 index 00000000..0f691763 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/mapper/GoodsStatisticsDataMapper.java @@ -0,0 +1,30 @@ +package cn.lili.modules.statistics.mapper; + +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.statistics.model.vo.CategoryStatisticsDataVO; +import cn.lili.modules.statistics.model.vo.GoodsStatisticsDataVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 商品统计数据处理层 + * + * @author Bulbasaur + * @date 2020/11/17 7:34 下午 + */ +public interface GoodsStatisticsDataMapper extends BaseMapper { + + //商品统计 + @Select("SELECT goods_id,goods_name,SUM(final_price) AS price,SUM(num) AS num FROM li_store_flow ${ew.customSqlSegment}") + List getGoodsStatisticsData(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + //分类统计 + @Select("SELECT category_id,category_name,SUM(price) AS price,SUM(num) AS num FROM li_store_flow ${ew.customSqlSegment}") + List getCateGoryStatisticsData(@Param(Constants.WRAPPER) Wrapper queryWrapper); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/mapper/MemberStatisticsDataMapper.java b/framework/src/main/java/cn/lili/modules/statistics/mapper/MemberStatisticsDataMapper.java new file mode 100644 index 00000000..62673999 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/mapper/MemberStatisticsDataMapper.java @@ -0,0 +1,22 @@ +package cn.lili.modules.statistics.mapper; + +import cn.lili.modules.statistics.model.dos.MemberStatisticsData; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 会员统计数据处理层 + * + * @author Bulbasaur + * @date 2020/11/17 7:34 下午 + */ +public interface MemberStatisticsDataMapper extends BaseMapper { + + + @Select("SELECT COUNT(0) FROM li_member ${ew.customSqlSegment}") + Integer customSqlQuery(@Param(Constants.WRAPPER) Wrapper queryWrapper); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/mapper/OrderStatisticsDataMapper.java b/framework/src/main/java/cn/lili/modules/statistics/mapper/OrderStatisticsDataMapper.java new file mode 100644 index 00000000..894fba0b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/mapper/OrderStatisticsDataMapper.java @@ -0,0 +1,28 @@ +package cn.lili.modules.statistics.mapper; + +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.statistics.model.vo.OrderStatisticsDataVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 订单统计数据处理层 + * + * @author Bulbasaur + * @date 2020/11/17 7:34 下午 + */ +public interface OrderStatisticsDataMapper extends BaseMapper { + + @Select("SELECT DATE_FORMAT(create_time,'%Y-%m-%d') AS create_time,sum(flow_price) AS price FROM li_order " + + " ${ew.customSqlSegment}") + List getOrderStatisticsData(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Select("SELECT count(0) FROM li_order ${ew.customSqlSegment}") + Integer count(@Param(Constants.WRAPPER) Wrapper queryWrapper); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/mapper/PlatformViewDataMapper.java b/framework/src/main/java/cn/lili/modules/statistics/mapper/PlatformViewDataMapper.java new file mode 100644 index 00000000..b52ff7e6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/mapper/PlatformViewDataMapper.java @@ -0,0 +1,20 @@ +package cn.lili.modules.statistics.mapper; + +import cn.lili.modules.statistics.model.dos.PlatformViewData; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 平台流量数据 + * + * @author Bulbasaur + * @date 2020/11/17 7:34 下午 + */ +public interface PlatformViewDataMapper extends BaseMapper { + //UV流量统计 + @Select("SELECT sum(uv_num) FROM li_s_platform_view_data ${ew.customSqlSegment}") + Integer count(@Param(Constants.WRAPPER) QueryWrapper queryWrapper); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/mapper/RefundOrderStatisticsDataMapper.java b/framework/src/main/java/cn/lili/modules/statistics/mapper/RefundOrderStatisticsDataMapper.java new file mode 100644 index 00000000..1cd4d80d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/mapper/RefundOrderStatisticsDataMapper.java @@ -0,0 +1,22 @@ +package cn.lili.modules.statistics.mapper; + +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.statistics.model.vo.RefundOrderStatisticsDataVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 退款统计数据处理层 + * + * @author Bulbasaur + * @date 2020/12/10 11:22 + */ +public interface RefundOrderStatisticsDataMapper extends BaseMapper { + + @Select("SELECT refund_sn,store_name,member_name,name,specs,final_price FROM li_store_flow ${ew.customSqlSegment}") + IPage getRefundStatisticsData(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/mapper/StoreStatisticsDataMapper.java b/framework/src/main/java/cn/lili/modules/statistics/mapper/StoreStatisticsDataMapper.java new file mode 100644 index 00000000..bc8dd21b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/mapper/StoreStatisticsDataMapper.java @@ -0,0 +1,26 @@ +package cn.lili.modules.statistics.mapper; + +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.statistics.model.vo.GoodsStatisticsDataVO; +import cn.lili.modules.statistics.model.vo.StoreStatisticsDataVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 店铺统计数据处理层 + * + * @author Bulbasaur + * @date 2020/11/17 7:34 下午 + */ +public interface StoreStatisticsDataMapper extends BaseMapper { + + @Select("SELECT store_id AS storeId,store_name AS storeName,SUM(final_price) AS price,SUM(num) AS num FROM li_store_flow ${ew.customSqlSegment}") + List getStoreStatisticsData(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/dos/MemberStatisticsData.java b/framework/src/main/java/cn/lili/modules/statistics/model/dos/MemberStatisticsData.java new file mode 100644 index 00000000..0d02c6a0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/dos/MemberStatisticsData.java @@ -0,0 +1,52 @@ +package cn.lili.modules.statistics.model.dos; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 会员统计 + * @author Chopper + * @date 2020/11/17 7:34 下午 + */ +@Data +@Entity +@Table(name = "li_member_statistics_data") +@TableName("li_member_statistics_data") +@ApiModel(value = "会员统计") +public class MemberStatisticsData { + + private static final long serialVersionUID = 1L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @ApiModelProperty(value = "统计日") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + private Date createDate; + + @ApiModelProperty(value = "当前会员数量") + private Integer memberCount; + + @ApiModelProperty(value = "新增会员数量") + private Integer newlyAdded; + + @ApiModelProperty(value = "当日活跃数量") + private Integer activeQuantity; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/dos/PlatformViewData.java b/framework/src/main/java/cn/lili/modules/statistics/model/dos/PlatformViewData.java new file mode 100644 index 00000000..c82beed6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/dos/PlatformViewData.java @@ -0,0 +1,51 @@ +package cn.lili.modules.statistics.model.dos; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 平台pv统计 + * + * @author Chopper + * @date 2020-06-19 17:50 + */ +@Data +@Entity +@Table(name = "li_s_platform_view_data") +@TableName("li_s_platform_view_data") +@ApiModel(value = "平台pv统计") +public class PlatformViewData { + + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @ApiModelProperty(value = "pv数量") + private Long pvNum; + + @ApiModelProperty(value = "uv数量") + private Long uvNum; + + + @ApiModelProperty(value = "统计日") + private Date date; + + //默认是平台流量统计// + + @ApiModelProperty(value = "店铺id") + private String storeId = "-1"; +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/dto/GoodsStatisticsQueryParam.java b/framework/src/main/java/cn/lili/modules/statistics/model/dto/GoodsStatisticsQueryParam.java new file mode 100644 index 00000000..03c0190f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/dto/GoodsStatisticsQueryParam.java @@ -0,0 +1,18 @@ +package cn.lili.modules.statistics.model.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 商品统计查询参数 + * + * @author Chopper + * @date 2020/11/17 7:34 下午 + */ +@Data +public class GoodsStatisticsQueryParam extends StatisticsQueryParam { + + @ApiModelProperty(value = "查询类型:按数量(NUM)、按金额(PRICE)") + private String type; + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/dto/StatisticsQueryParam.java b/framework/src/main/java/cn/lili/modules/statistics/model/dto/StatisticsQueryParam.java new file mode 100644 index 00000000..f0f57018 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/dto/StatisticsQueryParam.java @@ -0,0 +1,30 @@ +package cn.lili.modules.statistics.model.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 统计查询参数 + * + * @author Bulbasaur + * @date 2020/12/9 14:20 + */ +@Data +public class StatisticsQueryParam { + + @ApiModelProperty(value = "快捷搜索", allowableValues = "TODAY, YESTERDAY, LAST_SEVEN, LAST_THIRTY") + private String searchType; + + @ApiModelProperty(value = "类型:年(YEAR)、月(MONTH)") + private String timeType; + + @ApiModelProperty(value = "年份") + private Integer year; + + @ApiModelProperty(value = "月份") + private Integer month; + + @ApiModelProperty(value = "店铺ID") + private String storeId; + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/enums/SearchTypeEnum.java b/framework/src/main/java/cn/lili/modules/statistics/model/enums/SearchTypeEnum.java new file mode 100644 index 00000000..22850d5b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/enums/SearchTypeEnum.java @@ -0,0 +1,16 @@ +package cn.lili.modules.statistics.model.enums; + +/** + * 搜索类型 + * + * @author Chopper + * @since 2021/2/9 16:17 + */ +public enum SearchTypeEnum { + + /** + * 昨天,今天,过去七天,过去30天 + */ + TODAY, YESTERDAY, LAST_SEVEN, LAST_THIRTY +} + diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/enums/StatisticsQuery.java b/framework/src/main/java/cn/lili/modules/statistics/model/enums/StatisticsQuery.java new file mode 100644 index 00000000..bf001aa2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/enums/StatisticsQuery.java @@ -0,0 +1,19 @@ +package cn.lili.modules.statistics.model.enums; + +/** + * 统计查询 + * @author Chopper + * @since 2021/2/9 16:17 + */ +public enum StatisticsQuery { + + /** + * 数量 + */ + NUM, + + /** + * 金额 + */ + PRICE, +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/enums/TimeTypeEnum.java b/framework/src/main/java/cn/lili/modules/statistics/model/enums/TimeTypeEnum.java new file mode 100644 index 00000000..8b131641 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/enums/TimeTypeEnum.java @@ -0,0 +1,24 @@ +package cn.lili.modules.statistics.model.enums; + +/** + * 时间类型 + * @author Chopper + * @since 2021/2/9 16:17 + */ +public enum TimeTypeEnum { + + /** + * 月 + */ + MONTH, + + /** + * 年 + */ + YEAR, + + /** + * 全部 + */ + ALL; +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/CategoryStatisticsDataVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/CategoryStatisticsDataVO.java new file mode 100644 index 00000000..b05c7745 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/CategoryStatisticsDataVO.java @@ -0,0 +1,27 @@ +package cn.lili.modules.statistics.model.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 分类统计VO + * + * @author Bulbasaur + * @date 2020/12/10 15:42 + */ +@Data +public class CategoryStatisticsDataVO { + + + @ApiModelProperty("一级分类ID") + private String categoryId; + + @ApiModelProperty("一级分类名称") + private String categoryName; + + @ApiModelProperty(value = "销售数量") + private String num; + + @ApiModelProperty(value = "销售金额") + private Double price; +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/GoodsStatisticsDataVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/GoodsStatisticsDataVO.java new file mode 100644 index 00000000..169d82e5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/GoodsStatisticsDataVO.java @@ -0,0 +1,26 @@ +package cn.lili.modules.statistics.model.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 商品统计VO + * + * @author Bulbasaur + * @date 2020/12/9 14:25 + */ +@Data +public class GoodsStatisticsDataVO { + + @ApiModelProperty(value = "商品ID") + private String goodsId; + + @ApiModelProperty(value = "商品名称") + private String goodsName; + + @ApiModelProperty(value = "销售数量") + private String num; + + @ApiModelProperty(value = "销售金额") + private Double price; +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/IndexNoticeVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/IndexNoticeVO.java new file mode 100644 index 00000000..e9956bf7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/IndexNoticeVO.java @@ -0,0 +1,33 @@ +package cn.lili.modules.statistics.model.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 消息提示 + * + * @author Bulbasaur + * @date 2020/12/9 14:25 + */ +@Data +public class IndexNoticeVO { + + @ApiModelProperty(value = "待处理商品审核") + private Integer goods; + + @ApiModelProperty(value = "待处理店铺入驻审核") + private Integer store; + + @ApiModelProperty(value = "待处理售后申请") + private Integer refund; + + @ApiModelProperty(value = "待处理投诉审核") + private Integer complain; + + @ApiModelProperty(value = "待处理分销员提现申请") + private Integer distributionCash; + + @ApiModelProperty(value = "待处理商家结算") + private Integer waitPayBill; + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/IndexStatisticsVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/IndexStatisticsVO.java new file mode 100644 index 00000000..2064a11c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/IndexStatisticsVO.java @@ -0,0 +1,55 @@ +package cn.lili.modules.statistics.model.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 首页统计内容 + * + * @author Bulbasaur + * @date 2020/12/22 14:23 + */ +@Data +public class IndexStatisticsVO { + + @ApiModelProperty(value = "订单总数量") + private Integer orderNum; + @ApiModelProperty(value = "商品总数量") + private Integer goodsNum; + @ApiModelProperty(value = "会员总数量") + private Integer memberNum; + @ApiModelProperty(value = "店铺总数量") + private Integer storeNum; + + /** + * 流量概括 + */ + @ApiModelProperty(value = "今日访问数UV") + private Integer todayUV; + @ApiModelProperty(value = "昨日访问数UV") + private Integer yesterdayUV; + @ApiModelProperty(value = "前七日访问数UV") + private Integer lastSevenUV; + @ApiModelProperty(value = "三十日访问数UV") + private Integer lastThirtyUV; + + /** + * 今日信息概括 + */ + @ApiModelProperty(value = "今日订单数") + private Long todayOrderNum; + @ApiModelProperty(value = "今日下单金额") + private Double todayOrderPrice; + @ApiModelProperty(value = "今日新增会员数量") + private Integer todayMemberNum; + @ApiModelProperty(value = "今日新增商品数量") + private Integer todayGoodsNum; + @ApiModelProperty(value = "今日新增店铺数量") + private Integer todayStoreNum; + @ApiModelProperty(value = "今日新增评论数量") + private Integer todayMemberEvaluation; + + //当前在线人数 + @ApiModelProperty(value = "当前在线人数") + private Long currentNumberPeopleOnline; +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/OnlineMemberVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/OnlineMemberVO.java new file mode 100644 index 00000000..ecad5579 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/OnlineMemberVO.java @@ -0,0 +1,26 @@ +package cn.lili.modules.statistics.model.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.Date; + +/** + * 在线会员 + * + * @author Chopper + * @date 2021-02-21 09:59 + */ +@Data +@AllArgsConstructor +public class OnlineMemberVO { + + //在线时间 + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH") + private Date date; + + //在线会员人数 + private Integer num; + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/OrderOverviewVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/OrderOverviewVO.java new file mode 100644 index 00000000..cae22f65 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/OrderOverviewVO.java @@ -0,0 +1,57 @@ +package cn.lili.modules.statistics.model.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 订单统计概述 + * + * @author Chopper + * @date 2021-03-03 10:27 + */ +@Data +public class OrderOverviewVO { + + @ApiModelProperty(value = "UV人次") + private Integer uvNum; + + //下单统计 + @ApiModelProperty(value = "下单数量") + private Long orderNum; + + @ApiModelProperty(value = "下单人数") + private Long orderMemberNum; + + @ApiModelProperty(value = "下单金额") + private Double orderAmount; + + //付款统计 + @ApiModelProperty(value = "付款订单数量") + private Long paymentOrderNum; + + @ApiModelProperty(value = "付款人数") + private Long paymentsNum; + + @ApiModelProperty(value = "付款金额") + private Double paymentAmount; + + + //退单统计 + @ApiModelProperty(value = "退单笔数") + private Long refundOrderNum; + + @ApiModelProperty(value = "退单金额") + private Double refundOrderPrice; + + // 转换率 + @ApiModelProperty(value = "下单转换率") + private String orderConversionRate; + + @ApiModelProperty(value = "付款转换率") + private String paymentsConversionRate; + + @ApiModelProperty(value = "整体转换率") + private String overallConversionRate; + + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/OrderStatisticsDataVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/OrderStatisticsDataVO.java new file mode 100644 index 00000000..01b2e4ca --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/OrderStatisticsDataVO.java @@ -0,0 +1,35 @@ +package cn.lili.modules.statistics.model.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 订单统计数据VO + * + * @author Bulbasaur + * @date 2020/12/9 17:13 + */ +@Data +public class OrderStatisticsDataVO { + + @ApiModelProperty(value = "店铺") + private String storeName; + + @ApiModelProperty(value = "购买人") + private String memberName; + + @ApiModelProperty(value = "订单金额") + private Double price; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "创建时间") + private Date createTime; + + @ApiModelProperty(value = "订单编号") + private String orderItemSn; +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/PlatformViewVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/PlatformViewVO.java new file mode 100644 index 00000000..64ba5320 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/PlatformViewVO.java @@ -0,0 +1,59 @@ +package cn.lili.modules.statistics.model.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 流量数据展示VO + * + * @author Chopper + * @date 2020-06-19 17:50 + */ +@Data +public class PlatformViewVO { + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "展示时间") + private Date date; + + @ApiModelProperty(value = "pv数量") + private Long pvNum; + + @ApiModelProperty(value = "uv数量") + private Long uvNum; + + @ApiModelProperty(value = "店铺id") + private String storeId = "-1"; + + + public PlatformViewVO() { + //初始化参数 + pvNum = 0L; + uvNum = 0L; + } + + public Long getPvNum() { + if(pvNum==null){ + return 0L; + } + return pvNum; + } + + public Long getUvNum() { + if(uvNum==null){ + return 0L; + } + return uvNum; + } + + public PlatformViewVO(Date date) { + //初始化参数 + pvNum = 0L; + uvNum = 0L; + this.date = date; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/RefundOrderStatisticsDataVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/RefundOrderStatisticsDataVO.java new file mode 100644 index 00000000..30512db4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/RefundOrderStatisticsDataVO.java @@ -0,0 +1,32 @@ +package cn.lili.modules.statistics.model.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 退款统计VO + * + * @author Bulbasaur + * @date 2020/12/10 11:24 + */ +@Data +public class RefundOrderStatisticsDataVO { + + @ApiModelProperty(value = "售后SN") + private String refundSn; + + @ApiModelProperty(value = "商家名称 ") + private String storeName; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "商品名称") + private String name; + + @ApiModelProperty(value = "规格内容") + private String specs; + + @ApiModelProperty(value = "实际退款金额") + private Double finalPrice; +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/StoreIndexStatisticsVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/StoreIndexStatisticsVO.java new file mode 100644 index 00000000..1b9b025d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/StoreIndexStatisticsVO.java @@ -0,0 +1,51 @@ +package cn.lili.modules.statistics.model.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 店铺首页数据 + * + * @author Chopper + * @date 2021/3/17 4:04 下午 + */ +@Data +public class StoreIndexStatisticsVO { + + @ApiModelProperty(value = "商品总数量") + private Integer goodsNum; + @ApiModelProperty(value = "订单总数量") + private Integer orderNum; + @ApiModelProperty(value = "订单总额") + private Double orderPrice; + @ApiModelProperty(value = "访客数UV") + private Integer storeUV; + + @ApiModelProperty(value = "待付款订单数量") + private Integer unPaidOrder; + @ApiModelProperty(value = "待发货订单数量") + private Integer unDeliveredOrder; + @ApiModelProperty(value = "待收货订单数量") + private Integer deliveredOrder; + + @ApiModelProperty(value = "待处理退货数量") + private Integer returnGoods; + @ApiModelProperty(value = "待处理退款数量") + private Integer returnMoney; + @ApiModelProperty(value = "待回复评价数量") + private Integer memberEvaluation; + @ApiModelProperty(value = "待处理交易投诉数量") + private Integer complaint; + + @ApiModelProperty(value = "待上架商品数量") + private Integer waitUpper; + @ApiModelProperty(value = "待审核商品数量") + private Integer waitAuth; + + @ApiModelProperty(value = "可参与秒杀活动数量") + private Integer seckillNum; + @ApiModelProperty(value = "未对账结算单数量") + private Integer waitPayBill; + + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/model/vo/StoreStatisticsDataVO.java b/framework/src/main/java/cn/lili/modules/statistics/model/vo/StoreStatisticsDataVO.java new file mode 100644 index 00000000..14f6c306 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/model/vo/StoreStatisticsDataVO.java @@ -0,0 +1,26 @@ +package cn.lili.modules.statistics.model.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 商品统计VO + * + * @author Bulbasaur + * @date 2020/12/9 14:25 + */ +@Data +public class StoreStatisticsDataVO { + + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "销售数量") + private String num; + + @ApiModelProperty(value = "销售金额") + private Double price; +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/service/GoodsStatisticsDataService.java b/framework/src/main/java/cn/lili/modules/statistics/service/GoodsStatisticsDataService.java new file mode 100644 index 00000000..e45c6e33 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/service/GoodsStatisticsDataService.java @@ -0,0 +1,38 @@ +package cn.lili.modules.statistics.service; + +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.statistics.model.dto.GoodsStatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.CategoryStatisticsDataVO; +import cn.lili.modules.statistics.model.vo.GoodsStatisticsDataVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 商品统计业务层 + * + * @author Bulbasaur + * @date 2020/12/9 11:06 + */ +public interface GoodsStatisticsDataService extends IService { + + /** + * 查询热卖商品 + * 查询TOP100的商品 + * + * @param goodsStatisticsQueryParam 查询参数 + * @param num 数量 + * @return + */ + List getGoodsStatisticsData(GoodsStatisticsQueryParam goodsStatisticsQueryParam, Integer num); + + /** + * 查询行业统计 + * 根据商品一级分类ID查询 + * + * @param goodsStatisticsQueryParam 查询参数 + * @return + */ + List getCategoryStatisticsData(GoodsStatisticsQueryParam goodsStatisticsQueryParam); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/service/IndexStatisticsService.java b/framework/src/main/java/cn/lili/modules/statistics/service/IndexStatisticsService.java new file mode 100644 index 00000000..5ddbfa12 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/service/IndexStatisticsService.java @@ -0,0 +1,51 @@ +package cn.lili.modules.statistics.service; + +import cn.lili.modules.statistics.model.vo.*; + +import java.util.List; + +/** + * 首页统计数据业务层 + * + * @author Bulbasaur + * @date 2020/12/15 17:57 + */ +public interface IndexStatisticsService { + + /** + * 获取首页统计数据 + * + * @return 运营后台首页统计数据 + */ + IndexStatisticsVO indexStatistics(); + + /** + * 商家首页统计数据 + * + * @return 商家后台首页统计数据 + */ + StoreIndexStatisticsVO storeIndexStatistics(); + + /** + * 消息通知 + * + * @return 通知内容 + */ + IndexNoticeVO indexNotice(); + + /** + * 查询热卖商品TOP10 + * + * @return 热卖商品TOP10 + */ + List goodsStatisticsOfMonth(); + + /** + * 查询热卖店铺TOP10 + * + * @return 当月的热卖店铺TOP10 + */ + List storeStatisticsOfMonth(); + + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/service/MemberStatisticsDataService.java b/framework/src/main/java/cn/lili/modules/statistics/service/MemberStatisticsDataService.java new file mode 100644 index 00000000..9d3e5011 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/service/MemberStatisticsDataService.java @@ -0,0 +1,64 @@ +package cn.lili.modules.statistics.service; + +import cn.lili.modules.statistics.model.dos.MemberStatisticsData; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Date; +import java.util.List; + +/** + * 会员统计业务层 + * + * @author Bulbasaur + * @date 2020/12/9 11:06 + */ +public interface MemberStatisticsDataService extends IService { + + /** + * 获取会员数量 + * + * @return 会员统计 + */ + Integer getMemberCount(); + + /** + * 获取今日新增会员数量 + * + * @return 今日新增会员数量 + */ + Integer todayMemberNum(); + + /** + * 获取指定结束时间前的会员数量 + * + * @param endTime + * @return + */ + Integer memberCount(Date endTime); + + /** + * 当天活跃会员数量 + * + * @param startTime + * @return + */ + Integer activeQuantity(Date startTime); + + /** + * 时间段内新增会员数量 + * + * @param endTime + * @param startTime + * @return + */ + Integer newlyAdded(Date endTime, Date startTime); + + /** + * 根据参数,查询这段时间的会员统计 + * + * @param statisticsQueryParam + * @return + */ + List statistics(StatisticsQueryParam statisticsQueryParam); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/service/OrderStatisticsDataService.java b/framework/src/main/java/cn/lili/modules/statistics/service/OrderStatisticsDataService.java new file mode 100644 index 00000000..ec68595a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/service/OrderStatisticsDataService.java @@ -0,0 +1,55 @@ +package cn.lili.modules.statistics.service; + +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.OrderOverviewVO; +import cn.lili.modules.statistics.model.vo.OrderStatisticsDataVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; +import java.util.Map; + +/** + * 订单统计业务层 + * + * @author Bulbasaur + * @date 2020/12/9 11:06 + */ +public interface OrderStatisticsDataService extends IService { + + /** + * 订单统计概览 + * + * @param statisticsQueryParam + * @return + */ + OrderOverviewVO overview(StatisticsQueryParam statisticsQueryParam); + + /** + * 查询订单统计金额 + * + * @return 订单统计 + */ + Map getStoreOrderStatisticsPrice(); + + + /** + * 查询今日付款统计 + */ + Map getOrderStatisticsPrice(); + + /** + * 获取订单总数量 + * @param orderStatus 订单状态 + * @return 订单总数量 + */ + Integer orderNum(String orderStatus); + + /** + * 图表统计 + * + * @return 订单总数量 + */ + List statisticsChart(StatisticsQueryParam statisticsQueryParam); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/service/PlatformViewDataService.java b/framework/src/main/java/cn/lili/modules/statistics/service/PlatformViewDataService.java new file mode 100644 index 00000000..a1403c3a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/service/PlatformViewDataService.java @@ -0,0 +1,57 @@ +package cn.lili.modules.statistics.service; + +import cn.lili.modules.member.entity.vo.MemberDistributionVO; +import cn.lili.modules.statistics.model.dos.PlatformViewData; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.OnlineMemberVO; +import cn.lili.modules.statistics.model.vo.PlatformViewVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 平台PV统计 + * + * @author Bulbasaur + * @date 2020/12/9 11:06 + */ +public interface PlatformViewDataService extends IService { + + + /** + * 当前在线人数 + * + * @return + */ + Long online(); + + /** + * 会员分布 + * + * @return + */ + List memberDistribution(); + + /** + * 在线人数记录 + * + * @return + */ + List statisticsOnline(); + + /** + * 数据查询 + * + * @param queryParam + * @return + */ + List list(StatisticsQueryParam queryParam); + + /** + * 查询累计访客数 + * + * @param queryParam + * @return + */ + Integer countUv(StatisticsQueryParam queryParam); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/statistics/service/RefundOrderStatisticsService.java b/framework/src/main/java/cn/lili/modules/statistics/service/RefundOrderStatisticsService.java new file mode 100644 index 00000000..55a63aec --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/service/RefundOrderStatisticsService.java @@ -0,0 +1,34 @@ +package cn.lili.modules.statistics.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.RefundOrderStatisticsDataVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 退款订单统计业务层 + * + * @author Bulbasaur + * @date 2020/12/9 11:06 + */ +public interface RefundOrderStatisticsService extends IService { + + + /** + * 查询订单统计分页 + * + * @param statisticsQueryParam 查询参数 + * @return 退款统计 + */ + IPage getRefundOrderStatisticsData(PageVO pageVO, StatisticsQueryParam statisticsQueryParam); + + /** + * 查询退款订单统计金额 + * + * @param statisticsQueryParam 查询参数 + * @return 退款统计金额 + */ + Double getRefundOrderStatisticsPrice(StatisticsQueryParam statisticsQueryParam); +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/GoodsStatisticsDataServiceImpl.java b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/GoodsStatisticsDataServiceImpl.java new file mode 100644 index 00000000..a8fd8eb9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/GoodsStatisticsDataServiceImpl.java @@ -0,0 +1,81 @@ +package cn.lili.modules.statistics.serviceimpl; + +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.order.order.entity.enums.FlowTypeEnum; +import cn.lili.modules.statistics.mapper.GoodsStatisticsDataMapper; +import cn.lili.modules.statistics.model.dto.GoodsStatisticsQueryParam; +import cn.lili.modules.statistics.model.enums.StatisticsQuery; +import cn.lili.modules.statistics.model.vo.CategoryStatisticsDataVO; +import cn.lili.modules.statistics.model.vo.GoodsStatisticsDataVO; +import cn.lili.modules.statistics.service.GoodsStatisticsDataService; +import cn.lili.modules.statistics.util.StatisticsDateUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +/** + * 商品统计业务层实现 + * + * @author Bulbasaur + * @date 2020/12/9 11:30 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsStatisticsDataServiceImpl extends ServiceImpl implements GoodsStatisticsDataService { + + /** + * 商品统计 + */ + private final GoodsStatisticsDataMapper goodsStatisticsDataMapper; + + @Override + public List getGoodsStatisticsData(GoodsStatisticsQueryParam goodsStatisticsQueryParam, Integer num) { + //获取查询条件 + QueryWrapper queryWrapper = getQueryWrapper(goodsStatisticsQueryParam); + //根据商品分组 + queryWrapper.groupBy("goods_id"); + queryWrapper.groupBy("goods_name"); + + queryWrapper.eq(!StringUtils.isEmpty(goodsStatisticsQueryParam.getStoreId()), "store_id", goodsStatisticsQueryParam.getStoreId()); + //查询前一百条记录 + Page page = new Page(1, num); + return goodsStatisticsDataMapper.getGoodsStatisticsData(page, queryWrapper); + } + + @Override + public List getCategoryStatisticsData(GoodsStatisticsQueryParam goodsStatisticsQueryParam) { + //获取查询条件 + QueryWrapper queryWrapper = getQueryWrapper(goodsStatisticsQueryParam); + //根据分类分组 + queryWrapper.groupBy("category_id"); + return goodsStatisticsDataMapper.getCateGoryStatisticsData(queryWrapper); + } + + + private QueryWrapper getQueryWrapper(GoodsStatisticsQueryParam goodsStatisticsQueryParam) { + + QueryWrapper queryWrapper = Wrappers.query(); + //判断搜索类型是:年、月 + Date[] date = StatisticsDateUtil.getDateArray(goodsStatisticsQueryParam); + queryWrapper.between("create_time", date[0], date[1]); + + //判断是按照数量统计还是按照金额统计 + if (goodsStatisticsQueryParam.getType().equals(StatisticsQuery.NUM.name())) { + queryWrapper.orderByDesc("num"); + } else { + queryWrapper.orderByDesc("price"); + } + //设置为付款查询 + queryWrapper.eq("flow_type", FlowTypeEnum.PAY.name()); + return queryWrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/IndexStatisticsServiceImpl.java b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/IndexStatisticsServiceImpl.java new file mode 100644 index 00000000..c2321ca7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/IndexStatisticsServiceImpl.java @@ -0,0 +1,286 @@ +package cn.lili.modules.statistics.serviceimpl; + +import cn.hutool.core.date.DateTime; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.distribution.service.DistributionCashService; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.service.GoodsService; +import cn.lili.modules.member.service.MemberEvaluationService; +import cn.lili.modules.order.order.entity.enums.FlowTypeEnum; +import cn.lili.modules.order.order.entity.enums.OrderStatusEnum; +import cn.lili.modules.order.order.service.AfterSaleService; +import cn.lili.modules.order.order.service.OrderComplaintService; +import cn.lili.modules.promotion.service.SeckillService; +import cn.lili.modules.statistics.mapper.StoreStatisticsDataMapper; +import cn.lili.modules.statistics.model.dto.GoodsStatisticsQueryParam; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.enums.SearchTypeEnum; +import cn.lili.modules.statistics.model.enums.StatisticsQuery; +import cn.lili.modules.statistics.model.vo.*; +import cn.lili.modules.statistics.service.*; +import cn.lili.modules.store.entity.enums.BillStatusEnum; +import cn.lili.modules.store.service.BillService; +import cn.lili.modules.store.service.StoreService; +import cn.lili.modules.order.trade.entity.enums.AfterSaleTypeEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 首页统计数据业务层实现 + * + * @author Bulbasaur + * @date 2020/12/15 17:57 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class IndexStatisticsServiceImpl implements IndexStatisticsService { + + /** + * 订单统计 + */ + private final OrderStatisticsDataService orderStatisticsDataService; + /** + * 会员统计 + */ + private final MemberStatisticsDataService memberStatisticsDataService; + /** + * 商品统计 + */ + private final GoodsStatisticsDataService goodsStatisticsDataService; + /** + * 店铺统计 + */ + private final StoreStatisticsDataMapper storeStatisticsDataMapper; + /** + * 商品 + */ + private final GoodsService goodsService; + /** + * 店铺 + */ + private final StoreService storeService; + /** + * 店铺 + */ + private final MemberEvaluationService memberEvaluationService; + /** + * 售后 + */ + private final AfterSaleService afterSaleService; + /** + * 投诉 + */ + private final OrderComplaintService orderComplaintService; + /** + * 分销员提现 + */ + private final DistributionCashService distributionCashService; + /** + * 平台PV统计 + */ + private final PlatformViewDataService platformViewDataService; + /** + * 结算单 + */ + private final BillService billService; + /** + * 秒杀活动 + */ + private final SeckillService seckillService; + + @Override + public IndexNoticeVO indexNotice() { + + IndexNoticeVO indexNoticeVO = new IndexNoticeVO(); + //商品审核 + indexNoticeVO.setGoods(goodsService.goodsNum(GoodsStatusEnum.UPPER, GoodsAuthEnum.TOBEAUDITED)); + //店铺入驻审核 + indexNoticeVO.setStore(storeService.auditNum()); + //售后申请 + indexNoticeVO.setRefund(afterSaleService.applyNum(null)); + //投诉审核 + indexNoticeVO.setComplain(orderComplaintService.newComplainNum()); + //分销员提现审核 + indexNoticeVO.setDistributionCash(distributionCashService.newDistributionCash()); + //待处理商家结算 + indexNoticeVO.setWaitPayBill(billService.billNum(BillStatusEnum.CHECK)); + return indexNoticeVO; + } + + @Override + public IndexStatisticsVO indexStatistics() { + + //首页统计内容 + IndexStatisticsVO indexStatisticsVO = new IndexStatisticsVO(); + + //获取总订单数量 + indexStatisticsVO.setOrderNum(orderStatisticsDataService.orderNum(null)); + //获取总会员数量 + indexStatisticsVO.setMemberNum(memberStatisticsDataService.getMemberCount()); + //获取总上架商品数量 + indexStatisticsVO.setGoodsNum(goodsService.goodsNum(GoodsStatusEnum.UPPER, null)); + //获取总店铺数量 + indexStatisticsVO.setStoreNum(storeService.storeNum()); + + //下单统计 + Map map = orderStatisticsDataService.getOrderStatisticsPrice(); + //今日下单数 + indexStatisticsVO.setTodayOrderNum(map.get("num") == null ? 0L : (Long)map.get("num")); + //今日下单金额 + indexStatisticsVO.setTodayOrderPrice(map.get("price") == null ? 0D : (Double) map.get("price")); + + //今日新增会员数量 + indexStatisticsVO.setTodayMemberNum(memberStatisticsDataService.todayMemberNum()); + //今日新增商品数量 + indexStatisticsVO.setTodayGoodsNum(goodsService.todayUpperNum()); + //今日新增店铺数量 + indexStatisticsVO.setTodayStoreNum(storeService.todayStoreNum()); + //今日新增评论数量 + indexStatisticsVO.setTodayMemberEvaluation(memberEvaluationService.todayMemberEvaluation()); + //当前在线人数 + indexStatisticsVO.setCurrentNumberPeopleOnline(platformViewDataService.online()); + + + //流量统计 + StatisticsQueryParam queryParam = new StatisticsQueryParam(); + + //今日uv + queryParam.setSearchType(SearchTypeEnum.TODAY.name()); + indexStatisticsVO.setTodayUV(platformViewDataService.countUv(queryParam)); + +// 昨日访问数UV + queryParam.setSearchType(SearchTypeEnum.YESTERDAY.name()); + indexStatisticsVO.setYesterdayUV(platformViewDataService.countUv(queryParam)); + +// 前七日访问数UV + queryParam.setSearchType(SearchTypeEnum.LAST_SEVEN.name()); + indexStatisticsVO.setLastSevenUV(platformViewDataService.countUv(queryParam)); + +// 三十日访问数UV + queryParam.setSearchType(SearchTypeEnum.LAST_THIRTY.name()); + indexStatisticsVO.setLastThirtyUV(platformViewDataService.countUv(queryParam)); + + + return indexStatisticsVO; + } + + @Override + public StoreIndexStatisticsVO storeIndexStatistics() { + + StoreIndexStatisticsVO storeIndexStatisticsVO = new StoreIndexStatisticsVO(); + + //商品总数量 + storeIndexStatisticsVO.setGoodsNum(goodsService.goodsNum(GoodsStatusEnum.UPPER, null)); + //订单总数量、订单总金额 + Map map = orderStatisticsDataService.getStoreOrderStatisticsPrice(); + storeIndexStatisticsVO.setOrderNum(Integer.parseInt(map.get("num").toString())); + storeIndexStatisticsVO.setOrderPrice(map.get("price") != null ? Double.parseDouble(map.get("price").toString()) : 0.0); + + //访问量 + StatisticsQueryParam queryParam = new StatisticsQueryParam(); + queryParam.setSearchType(SearchTypeEnum.TODAY.name()); + queryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + PlatformViewVO platformViewVO = platformViewDataService.list(queryParam).get(0); + storeIndexStatisticsVO.setStoreUV(platformViewVO.getUvNum().intValue()); + + //待付款订单数量 + storeIndexStatisticsVO.setUnPaidOrder(orderStatisticsDataService.orderNum(OrderStatusEnum.UNPAID.name())); + //待发货订单数量 + storeIndexStatisticsVO.setUnDeliveredOrder(orderStatisticsDataService.orderNum(OrderStatusEnum.UNDELIVERED.name())); + //待收货订单数量 + storeIndexStatisticsVO.setDeliveredOrder(orderStatisticsDataService.orderNum(OrderStatusEnum.DELIVERED.name())); + + //待处理退货数量 + storeIndexStatisticsVO.setReturnGoods(afterSaleService.applyNum(AfterSaleTypeEnum.RETURN_GOODS.name())); + //待处理退款数量 + storeIndexStatisticsVO.setReturnMoney(afterSaleService.applyNum(AfterSaleTypeEnum.RETURN_MONEY.name())); + //待回复评价数量 + storeIndexStatisticsVO.setMemberEvaluation(memberEvaluationService.getWaitReplyNum()); + //待处理交易投诉数量 + storeIndexStatisticsVO.setComplaint(orderComplaintService.newComplainNum()); + + //待上架商品数量 + storeIndexStatisticsVO.setWaitUpper(goodsService.goodsNum(GoodsStatusEnum.DOWN, null)); + //待审核商品数量 + storeIndexStatisticsVO.setWaitAuth(goodsService.goodsNum(null, GoodsAuthEnum.TOBEAUDITED)); + + //可参与秒杀活动数量 + storeIndexStatisticsVO.setSeckillNum(seckillService.getApplyNum()); + //待处理商家结算 + storeIndexStatisticsVO.setWaitPayBill(billService.billNum(BillStatusEnum.OUT)); + + return storeIndexStatisticsVO; + } + + @Override + public List goodsStatisticsOfMonth() { + //获取查询参数 + GoodsStatisticsQueryParam goodsStatisticsQueryParam = getGoodsStatisticsQueryParam(); + //查询商品 + return goodsStatisticsDataService.getGoodsStatisticsData(goodsStatisticsQueryParam, 10); + } + + @Override + public List storeStatisticsOfMonth() { + + QueryWrapper queryWrapper = Wrappers.query(); + + queryWrapper.between("create_time", cn.hutool.core.date.DateUtil.beginOfYear(new DateTime()), + cn.hutool.core.date.DateUtil.endOfYear(new DateTime())); + + queryWrapper.orderByDesc("price"); + + queryWrapper.groupBy("store_id,store_name "); + + queryWrapper.eq("flow_type", FlowTypeEnum.PAY.name()); + + //查询前十条记录 + Page page = new Page(1, 10); + + return storeStatisticsDataMapper.getStoreStatisticsData(page, queryWrapper); + } + + + /** + * 获取当月查询参数 + * + * @return 当月查询参数 + */ + private StatisticsQueryParam getStatisticsQueryParam() { + + StatisticsQueryParam statisticsQueryParam = new StatisticsQueryParam(); + + return statisticsQueryParam; + } + + /** + * 获取当月订单查询条件 + * + * @return 当月订单查询参数 + */ + private GoodsStatisticsQueryParam getGoodsStatisticsQueryParam() { + GoodsStatisticsQueryParam goodsStatisticsQueryParam = new GoodsStatisticsQueryParam(); + StatisticsQueryParam statisticsQueryParam = this.getStatisticsQueryParam(); + BeanUtil.copyProperties(goodsStatisticsQueryParam, statisticsQueryParam); + + //如果登录是商家的账号,获取商家相关统计内容 + if (UserContext.getCurrentUser().getRole().equals(UserEnums.STORE)) { + goodsStatisticsQueryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + } + goodsStatisticsQueryParam.setType(StatisticsQuery.PRICE.name()); + DateTime dateTime = new DateTime(); + goodsStatisticsQueryParam.setYear(dateTime.year()); + goodsStatisticsQueryParam.setMonth(dateTime.monthBaseOne()); + return goodsStatisticsQueryParam; + } +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/MemberStatisticsDataServiceImpl.java b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/MemberStatisticsDataServiceImpl.java new file mode 100644 index 00000000..645d36d7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/MemberStatisticsDataServiceImpl.java @@ -0,0 +1,80 @@ +package cn.lili.modules.statistics.serviceimpl; + +import cn.hutool.core.date.DateUtil; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.modules.statistics.mapper.MemberStatisticsDataMapper; +import cn.lili.modules.statistics.model.dos.MemberStatisticsData; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.service.MemberStatisticsDataService; +import cn.lili.modules.statistics.util.StatisticsDateUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +/** + * 会员统计业务层实现 + * + * @author Bulbasaur + * @date 2020/12/9 18:33 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberStatisticsDataServiceImpl extends ServiceImpl implements MemberStatisticsDataService { + + /** + * 会员统计 + */ + private final MemberStatisticsDataMapper memberStatisticsDataMapper; + + @Override + public Integer getMemberCount() { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("disabled", SwitchEnum.OPEN.name()); + return memberStatisticsDataMapper.customSqlQuery(queryWrapper); + } + + @Override + public Integer todayMemberNum() { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.gt("create_time", DateUtil.beginOfDay(new Date())); + return memberStatisticsDataMapper.customSqlQuery(queryWrapper); + } + + @Override + public Integer memberCount(Date endTime) { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.lt("create_time", endTime); + return memberStatisticsDataMapper.customSqlQuery(queryWrapper); + } + + @Override + public Integer activeQuantity(Date startTime) { + + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.ge("last_login_date", startTime); + return memberStatisticsDataMapper.customSqlQuery(queryWrapper); + } + + @Override + public Integer newlyAdded(Date startTime, Date endTime) { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.between("create_time", startTime, endTime); + return memberStatisticsDataMapper.customSqlQuery(queryWrapper); + } + + @Override + public List statistics(StatisticsQueryParam statisticsQueryParam) { + Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam); + Date startTime = dates[0], endTime = dates[1]; + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.between("create_date", startTime, endTime); + + return list(queryWrapper); + } +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/OrderStatisticsDataServiceImpl.java b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/OrderStatisticsDataServiceImpl.java new file mode 100644 index 00000000..fe0fd8df --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/OrderStatisticsDataServiceImpl.java @@ -0,0 +1,261 @@ +package cn.lili.modules.statistics.serviceimpl; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.order.order.entity.enums.FlowTypeEnum; +import cn.lili.modules.order.order.entity.enums.PayStatusEnum; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.statistics.mapper.OrderStatisticsDataMapper; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.OrderOverviewVO; +import cn.lili.modules.statistics.model.vo.OrderStatisticsDataVO; +import cn.lili.modules.statistics.service.OrderStatisticsDataService; +import cn.lili.modules.statistics.service.PlatformViewDataService; +import cn.lili.modules.statistics.util.StatisticsDateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * 订单统计业务层实现 + * + * @author Bulbasaur + * @date 2020/12/9 17:16 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderStatisticsDataServiceImpl extends ServiceImpl implements OrderStatisticsDataService { + + //订单统计 + private final OrderStatisticsDataMapper orderStatisticsDataMapper; + //平台PV统计 + private final PlatformViewDataService platformViewDataService; + //订单 + private final OrderService orderService; + + @Override + public OrderOverviewVO overview(StatisticsQueryParam statisticsQueryParam) { + Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam); + + OrderOverviewVO orderOverviewVO = new OrderOverviewVO(); + //访客数 + orderOverviewVO.setUvNum(platformViewDataService.countUv(statisticsQueryParam)); + if (orderOverviewVO.getUvNum() == null) { + orderOverviewVO.setUvNum(0); + } + + //下单统计 + initOrder(dates, orderOverviewVO); + + //付款统计 + initPayment(dates, orderOverviewVO); + + //退单统计 + initAfterSale(dates, orderOverviewVO); + + //数据运算(转换率,比例相关) + conversionRateOperation(orderOverviewVO); + return orderOverviewVO; + } + + // 运算转换率 + private void conversionRateOperation(OrderOverviewVO orderOverviewVO) { + + //下单转换率 订单数/UV + Double orderConversionRate = CurrencyUtil.div(orderOverviewVO.getOrderNum(), orderOverviewVO.getUvNum(), 4); + if (orderConversionRate > 1) { + orderConversionRate = 1d; + } + orderOverviewVO.setOrderConversionRate(CurrencyUtil.mul(orderConversionRate, 100) + "%"); + //付款转换率 付款订单数/订单数 + Double paymentsConversionRate = CurrencyUtil.div(orderOverviewVO.getPaymentOrderNum(), orderOverviewVO.getOrderNum(), 4); + if (paymentsConversionRate > 1) { + paymentsConversionRate = 1d; + } + orderOverviewVO.setPaymentsConversionRate(CurrencyUtil.mul(paymentsConversionRate, 100) + "%"); + //整体转换率 付款数/UV + Double overallConversionRate = CurrencyUtil.div(orderOverviewVO.getPaymentOrderNum(), orderOverviewVO.getUvNum(), 4); + if (overallConversionRate > 1) { + overallConversionRate = 1d; + } + orderOverviewVO.setOverallConversionRate(CurrencyUtil.mul(overallConversionRate, 100) + "%"); + } + + /** + * 订单统计-下单属性填充 + * + * @param dates + * @param orderOverviewVO + */ + private void initOrder(Date[] dates, OrderOverviewVO orderOverviewVO) { + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.between("create_time", dates[0], dates[1]); + queryWrapper.select("SUM(flow_price) AS price , COUNT(0) AS num"); + Map order = orderService.getMap(queryWrapper); + orderOverviewVO.setOrderNum(order != null && order.containsKey("num") ? (Long) order.get("num") : 0L); + orderOverviewVO.setOrderAmount(order != null && order.containsKey("price") ? (double) order.get("price") : 0L); + + queryWrapper = Wrappers.query(); + queryWrapper.between("create_time", dates[0], dates[1]); + queryWrapper.select("count(DISTINCT member_id) AS num"); + Map memberNum = orderService.getMap(queryWrapper); + + orderOverviewVO.setOrderMemberNum(memberNum != null && memberNum.containsKey("num") ? (Long) memberNum.get("num") : 0L); + + if (orderOverviewVO.getOrderAmount() == null) { + orderOverviewVO.setOrderAmount(0D); + } + } + + /** + * 订单统计-付款属性填充 + * + * @param dates + * @param orderOverviewVO + */ + private void initPayment(Date[] dates, OrderOverviewVO orderOverviewVO) { + //付款订单数,付款金额 + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.between("create_time", dates[0], dates[1]); + queryWrapper.select("SUM(final_price) AS price , COUNT(0) AS num"); + queryWrapper.eq("flow_type", FlowTypeEnum.PAY.name()); + Map payment = this.getMap(queryWrapper); + + orderOverviewVO.setPaymentOrderNum(payment != null && payment.containsKey("num") ? (Long) payment.get("num") : 0L); + orderOverviewVO.setPaymentAmount(payment != null && payment.containsKey("price") ? (Double) payment.get("price") : 0D); + + //付款人数 + queryWrapper = Wrappers.query(); + queryWrapper.between("create_time", dates[0], dates[1]); + queryWrapper.select("COUNT(0) AS num"); + queryWrapper.groupBy("member_id"); + Map paymentMemberNum = this.getMap(queryWrapper); + + orderOverviewVO.setPaymentsNum(paymentMemberNum != null && paymentMemberNum.containsKey("num") ? (Long) paymentMemberNum.get("num") : 0L); + } + + /** + * 订单统计-付款属性填充 + * + * @param dates + * @param orderOverviewVO + */ + private void initAfterSale(Date[] dates, OrderOverviewVO orderOverviewVO) { + //付款订单数,付款金额 + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.between("create_time", dates[0], dates[1]); + queryWrapper.select("SUM(final_price) AS price , COUNT(0) AS num"); + queryWrapper.eq("flow_type", FlowTypeEnum.REFUND.name()); + Map payment = this.getMap(queryWrapper); + orderOverviewVO.setRefundOrderNum(payment != null && payment.containsKey("num") ? (Long) payment.get("num") : 0L); + orderOverviewVO.setRefundOrderPrice(payment != null && payment.containsKey("price") ? (Double) payment.get("price") : 0D); + } + + + @Override + public Map getStoreOrderStatisticsPrice() { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq(StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), + "store_id", UserContext.getCurrentUser().getStoreId()); + queryWrapper.select("SUM(final_price) AS price , COUNT(0) AS num"); + return this.getMap(queryWrapper); + } + + @Override + public Map getOrderStatisticsPrice() { + QueryWrapper queryWrapper = Wrappers.query(); + //支付订单 + queryWrapper.eq("flow_type", FlowTypeEnum.PAY.name()); + + //商家查询,则增加商家判定 + AuthUser authUser = UserContext.getCurrentUser(); + if (authUser.getRole().equals(UserEnums.STORE)) { + queryWrapper.eq("store_id", authUser.getStoreId()); + } + //大于今天凌晨 + queryWrapper.gt("create_time", DateUtil.startOfTodDayTime()); + + queryWrapper.select("SUM(final_price) AS price , COUNT(0) AS num"); + return this.getMap(queryWrapper); + } + + @Override + public Integer orderNum(String orderStatus) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); + //queryWrapper.eq("flow_type", FlowTypeEnum.PAY.name()); + queryWrapper.eq(StringUtils.isNotEmpty(orderStatus),Order::getOrderStatus,orderStatus); + queryWrapper.eq(StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), + Order::getStoreId, UserContext.getCurrentUser().getStoreId()); + return orderService.count(queryWrapper); + } + + @Override + public List statisticsChart(StatisticsQueryParam statisticsQueryParam) { + Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam); + QueryWrapper queryWrapper = new QueryWrapper(); + //已支付 + queryWrapper.eq("pay_status", PayStatusEnum.PAID.name()); + //选择商家判定 + queryWrapper.eq(StringUtils.isNotEmpty(statisticsQueryParam.getStoreId()), "seller_id", statisticsQueryParam.getStoreId()); +// 查询时间区间 + queryWrapper.between("create_time", dates[0], dates[1]); +// 格式化时间 + queryWrapper.groupBy("DATE_FORMAT(create_time,'%Y-%m-%d')"); + List orderStatisticsDataVOS = orderStatisticsDataMapper.getOrderStatisticsData(queryWrapper); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(dates[0]); + + List result = new ArrayList<>(); + //时间判定,将数据填充好 + //如果当前的时间,在结束时间之前 + while (calendar.getTime().before(dates[1])) { + OrderStatisticsDataVO item = null; + //判定是否已经有这一天的数据 + for (OrderStatisticsDataVO orderStatisticsDataVO : orderStatisticsDataVOS) { + if (orderStatisticsDataVO.getCreateTime().equals(calendar.getTime())) { + item = orderStatisticsDataVO; + } + } + //如果数据不存在,则进行数据填充 + if (item == null) { + item = new OrderStatisticsDataVO(); + item.setPrice(0d); + item.setCreateTime(calendar.getTime()); + } + result.add(item); + //增加时间 + calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) + 1); + } + return result; + } + + private QueryWrapper getQueryWrapper(StatisticsQueryParam statisticsQueryParam) { + + QueryWrapper queryWrapper = Wrappers.query(); + + Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam); + queryWrapper.between("create_time", dates[0], dates[1]); + + //设置店铺ID + queryWrapper.eq(StringUtils.isNotEmpty(statisticsQueryParam.getStoreId()), "store_id", statisticsQueryParam.getStoreId()); + + + //设置为付款查询 + queryWrapper.eq("flow_type", FlowTypeEnum.PAY.name()); + + return queryWrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/PlatformViewDataServiceImpl.java b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/PlatformViewDataServiceImpl.java new file mode 100644 index 00000000..48cd985a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/PlatformViewDataServiceImpl.java @@ -0,0 +1,286 @@ +package cn.lili.modules.statistics.serviceimpl; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.DateUtil; +import cn.lili.config.properties.StatisticsProperties; +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.member.entity.vo.MemberDistributionVO; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.statistics.mapper.PlatformViewDataMapper; +import cn.lili.modules.statistics.model.dos.PlatformViewData; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.enums.SearchTypeEnum; +import cn.lili.modules.statistics.model.vo.OnlineMemberVO; +import cn.lili.modules.statistics.model.vo.PlatformViewVO; +import cn.lili.modules.statistics.service.PlatformViewDataService; +import cn.lili.modules.statistics.util.StatisticsDateUtil; +import cn.lili.modules.statistics.util.StatisticsSuffix; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * 流量统计 + * + * @author Chopper + * @version v1.0 + * @Description: + * @since v7.0 + * 2021/1/18 12:07 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PlatformViewDataServiceImpl extends ServiceImpl implements PlatformViewDataService { + //在线人数统计 + private final StatisticsProperties statisticsProperties; + //会员 + private final MemberService memberService; + //平台流量统计 + private final PlatformViewDataMapper platformViewDataMapper; + //缓存 + private final Cache cache; + + + @Override + public Long online() { + Object object = cache.get(CachePrefix.ONLINE_NUM.getPrefix()); + + if (null != object) { + return (Long) cache.get(CachePrefix.ONLINE_NUM.getPrefix()); + } + //这里统计的是有效的accessToken ,如果需要数据精确,需要调整accessToken的有效时间,开发人员建议2小时误差较为合适 + Long num = Long.valueOf(cache.keys(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.MEMBER) + "*").size()); + cache.put(CachePrefix.ONLINE_NUM.getPrefix(), num, statisticsProperties.getCurrentOnlineUpdate().longValue()); + return num; + } + + @Override + public List memberDistribution() { + Object object = cache.get(CachePrefix.MEMBER_DISTRIBUTION.getPrefix()); + + if (null != object) { + return (List) cache.get(CachePrefix.MEMBER_DISTRIBUTION.getPrefix()); + } + List memberDistributionVOS = memberService.distribution(); + + //统计总数 + int count = 0; + for (MemberDistributionVO vo : memberDistributionVOS) { + count += vo.getNum(); + } + //初始化数据,填充枚举和比例 + for (MemberDistributionVO vo : memberDistributionVOS) { + vo.setProportion(CurrencyUtil.div(vo.getNum(), count, 4)); + //客户端填充 + if (StringUtils.isNotEmpty(vo.getClientEnum())) { + vo.setClientEnum(ClientTypeEnum.valueOf(vo.getClientEnum()).clientName()); + } else { + vo.setClientEnum(ClientTypeEnum.UNKNOWN.clientName()); + } + } + + cache.put(CachePrefix.MEMBER_DISTRIBUTION.getPrefix(), memberDistributionVOS); + return memberDistributionVOS; + } + + @Override + public List statisticsOnline() { + Object object = cache.get(CachePrefix.ONLINE_MEMBER.getPrefix()); + List result = new ArrayList<>(); + if (object != null) { + result = (List) object; + } + return this.initData(result); + } + + /** + * 在线会员数据初始化 + * + * @param source + * @return + */ + private List initData(List source) { + List onlineMemberVOS = new ArrayList<>(); + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - statisticsProperties.getOnlineMember() - 1); + //循环填充数据 + for (int i = 0; i < statisticsProperties.getOnlineMember(); i++) { + calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1); + OnlineMemberVO exitOnlineNum = null; + for (int j = 0; j < source.size(); j++) { + if (source.get(j).getDate().equals(calendar.getTime())) { + exitOnlineNum = source.get(j); + } + } + if (exitOnlineNum == null) { + onlineMemberVOS.add(new OnlineMemberVO(calendar.getTime(), 0)); + } else { + onlineMemberVOS.add(exitOnlineNum); + } + } + return onlineMemberVOS; + } + + @Override + public List list(StatisticsQueryParam queryParam) { + List result = new ArrayList<>(); + + //查询开始时间和结束时间,用于数据库查询 + Date endTime = null, startTime = null; + + //搜索类型判定,如果不为空,则按照搜索类型进行查询 + if (StringUtils.isNotEmpty(queryParam.getSearchType())) { + SearchTypeEnum searchTypeEnum = SearchTypeEnum.valueOf(queryParam.getSearchType()); + switch (searchTypeEnum) { + case TODAY: + PlatformViewVO today = new PlatformViewVO(); + //查询 平台流量 + if (StringUtils.isEmpty(queryParam.getStoreId())) { + //设置PV UV属性 + String pv = cache.getString(CachePrefix.PV.getPrefix() + StatisticsSuffix.suffix()); + if (pv == null) { + pv = "0"; + } + today.setPvNum(Long.valueOf(pv)); + today.setUvNum(cache.counter(CachePrefix.UV.getPrefix() + StatisticsSuffix.suffix()).longValue()); + } + //店铺流量 + else { + //设置PV UV属性 + + String pv = cache.getString(CachePrefix.STORE_PV.getPrefix() + StatisticsSuffix.suffix(queryParam.getStoreId())); + if (pv == null) { + pv = "0"; + } + today.setPvNum(Long.valueOf(pv)); + today.setUvNum(cache.counter(CachePrefix.STORE_UV.getPrefix() + StatisticsSuffix.suffix(queryParam.getStoreId())).longValue()); + } + today.setDate(new Date()); + result.add(today); + break; + case YESTERDAY: + case LAST_SEVEN: + case LAST_THIRTY: { + Date[] dates = StatisticsDateUtil.getDateArray(searchTypeEnum); + endTime = dates[1]; + startTime = dates[0]; + break; + } + } + } else { + //根据查询时间来确定查询参数 + Integer year = queryParam.getYear(); + Integer month = queryParam.getMonth(); + + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + calendar.set(year, month - 1, 1); + startTime = calendar.getTime(); + calendar.set(year, month, -1); + endTime = calendar.getTime(); + } + //时间不为空则按照时间开始数据查询 + if (startTime != null) { + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.between(PlatformViewData::getDate, startTime, endTime); + lambdaQueryWrapper.eq(PlatformViewData::getStoreId, StringUtils.isEmpty(queryParam.getStoreId()) ? + "-1" : queryParam.getStoreId()); + List dataList = this.list(lambdaQueryWrapper); + result = builderVOS(startTime, endTime, dataList); + } + return result; + } + + @Override + public Integer countUv(StatisticsQueryParam queryParam) { + Date[] dates = StatisticsDateUtil.getDateArray(queryParam); + //获取当前时间 + Calendar calendar = Calendar.getInstance(); + + + calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 0); + calendar.set(Calendar.MILLISECOND, 0); + + System.out.println(DateUtil.toString(calendar.getTime().getTime(), DateUtil.STANDARD_FORMAT)); + //如果是今天的统计,则从redis 中拿,否则从数据库中拿 + if (dates[0].equals(calendar.getTime())) { + if (StringUtils.isNotEmpty(queryParam.getStoreId())) { + return cache.counter(CachePrefix.UV.getPrefix() + StatisticsSuffix.suffix(queryParam.getStoreId())).intValue(); + } + return cache.counter(CachePrefix.UV.getPrefix() + StatisticsSuffix.suffix()).intValue(); + } else { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.between("date", dates[0], dates[1]); + //根据店铺查询判定,如果有,则店铺查询,如果没有,则根据商家查询 + if (queryParam.getStoreId() != null) { + queryWrapper.eq("store_id", queryParam.getStoreId()); + } else { + queryWrapper.eq("store_id", -1); + } + return platformViewDataMapper.count(queryWrapper); + } + } + + /** + * 根据查询条件,创建数据 + * + * @param startDate + * @param endDate + * @param dataList + * @return + */ + private List builderVOS(Date startDate, Date endDate, List dataList) { + + Calendar startTime = Calendar.getInstance(); + startTime.setTime(startDate); + + Calendar endTime = Calendar.getInstance(); + endTime.setTime(endDate); + + List result = new ArrayList<>(); + + //构造所有需要统计展示等流量数据 + List listDate = new ArrayList<>(); + while (startTime.before(endTime) || startTime.getTime().equals(endTime.getTime())) { + listDate.add(startTime.getTime()); + startTime.set(Calendar.DAY_OF_MONTH, startTime.get(Calendar.DAY_OF_MONTH) + 1); + + } + //根据时间集,匹配查询到等数据,构建返回等VO + listDate.forEach(date -> { + PlatformViewVO platformViewVO = new PlatformViewVO(date); + dataList.forEach(platformViewData -> { + if (platformViewData.getDate().equals(date)) { + BeanUtils.copyProperties(platformViewData, platformViewVO); + } + }); + //没有匹配到数据库查询的数据,则初始化数据 + result.add(platformViewVO); + }); + return result; + + } + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/RefundOrderStatisticsServiceImpl.java b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/RefundOrderStatisticsServiceImpl.java new file mode 100644 index 00000000..89aa8676 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/serviceimpl/RefundOrderStatisticsServiceImpl.java @@ -0,0 +1,68 @@ +package cn.lili.modules.statistics.serviceimpl; + +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.order.order.entity.enums.FlowTypeEnum; +import cn.lili.modules.statistics.mapper.RefundOrderStatisticsDataMapper; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.enums.TimeTypeEnum; +import cn.lili.modules.statistics.model.vo.RefundOrderStatisticsDataVO; +import cn.lili.modules.statistics.service.RefundOrderStatisticsService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 退款订单统计业务层实现 + * + * @author Bulbasaur + * @date 2020/12/10 11:30 + */ +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RefundOrderStatisticsServiceImpl extends ServiceImpl implements RefundOrderStatisticsService { + + //退款统计 + private final RefundOrderStatisticsDataMapper refundOrderStatisticsDataMapper; + + @Override + public IPage getRefundOrderStatisticsData(PageVO pageVO, StatisticsQueryParam statisticsQueryParam) { + QueryWrapper queryWrapper = getQueryWrapper(statisticsQueryParam); + return refundOrderStatisticsDataMapper.getRefundStatisticsData(PageUtil.initPage(pageVO), queryWrapper); + } + + @Override + public Double getRefundOrderStatisticsPrice(StatisticsQueryParam statisticsQueryParam) { + + QueryWrapper queryWrapper = getQueryWrapper(statisticsQueryParam); + queryWrapper.select("SUM(final_price) AS price"); + return (Double) this.getMap(queryWrapper).get("price"); + } + + + private QueryWrapper getQueryWrapper(StatisticsQueryParam statisticsQueryParam) { + + QueryWrapper queryWrapper = Wrappers.query(); + + //判断搜索类型是:年、月 + if (statisticsQueryParam.getTimeType().equals(TimeTypeEnum.MONTH.name())) { + queryWrapper.between("create_time", DateUtil.getBeginTime(statisticsQueryParam.getYear(), statisticsQueryParam.getMonth()), DateUtil.getEndTime(statisticsQueryParam.getYear(), statisticsQueryParam.getMonth())); + } else { + queryWrapper.between("create_time", DateUtil.getBeginTime(statisticsQueryParam.getYear(), 1), DateUtil.getEndTime(statisticsQueryParam.getYear(), 12)); + } + + //设置店铺ID + queryWrapper.eq(!StringUtils.isEmpty(statisticsQueryParam.getStoreId()), "store_id", statisticsQueryParam.getStoreId()); + + //设置为退款查询 + queryWrapper.eq("flow_type", FlowTypeEnum.REFUND); + return queryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/util/StatisticsDateUtil.java b/framework/src/main/java/cn/lili/modules/statistics/util/StatisticsDateUtil.java new file mode 100644 index 00000000..54ade392 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/util/StatisticsDateUtil.java @@ -0,0 +1,120 @@ +package cn.lili.modules.statistics.util; + +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.enums.SearchTypeEnum; + +import java.util.Calendar; +import java.util.Date; + +/** + * 统计缓存后缀工具 + * + * @author Chopper + * @date 2021-01-15 15:30 + */ +public class StatisticsDateUtil { + + + /** + * 快捷搜索,得到开始时间和结束时间 + * + * @param searchTypeEnum + * @return + */ + public static Date[] getDateArray(SearchTypeEnum searchTypeEnum) { + Date[] dateArray = new Date[2]; + + Calendar calendar = Calendar.getInstance(); + // 时间归到今天凌晨0点 + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + switch (searchTypeEnum) { + case TODAY: + dateArray[0] = calendar.getTime(); + + calendar.set(Calendar.HOUR_OF_DAY, +24); + calendar.set(Calendar.MILLISECOND, -1); + dateArray[1] = calendar.getTime(); + break; + + case YESTERDAY: + //获取昨天 + calendar.set(Calendar.HOUR_OF_DAY, -24); + dateArray[0] = calendar.getTime(); + + //昨天结束时间 + calendar.set(Calendar.HOUR_OF_DAY, +24); + calendar.set(Calendar.MILLISECOND, -1); + dateArray[1] = calendar.getTime(); + break; + case LAST_SEVEN: + calendar.set(Calendar.HOUR_OF_DAY, -24 * 7); + dateArray[0] = calendar.getTime(); + + + calendar.set(Calendar.HOUR_OF_DAY, +24 * 7); + calendar.set(Calendar.MILLISECOND, -1); + //获取过去七天 + dateArray[1] = calendar.getTime(); + break; + case LAST_THIRTY: + //获取最近三十天 + calendar.set(Calendar.HOUR_OF_DAY, -24 * 30); + dateArray[0] = calendar.getTime(); + + + calendar.set(Calendar.HOUR_OF_DAY, +24 * 30); + calendar.set(Calendar.MILLISECOND, -1); + //获取过去七天 + dateArray[1] = calendar.getTime(); + break; + } + return dateArray; + } + + + /** + * 获取年月获取开始结束时间 + * + * @param year 年 + * @param month 月 + * @return 返回时间 + */ + public static Date[] getDateArray(Integer year, Integer month) { + Date[] dateArray = new Date[2]; + + Calendar calendar = Calendar.getInstance(); + calendar.set(year, month, 0); + dateArray[1] = calendar.getTime(); + calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1); + dateArray[0] = calendar.getTime(); + return dateArray; + } + + /** + * 根据搜索参数获取搜索开始结束时间 + * + * @param statisticsQueryParam + * @return + */ + public static Date[] getDateArray(StatisticsQueryParam statisticsQueryParam) { + //如果快捷搜搜哦 + if (StringUtils.isNotEmpty(statisticsQueryParam.getSearchType())) { + return getDateArray(SearchTypeEnum.valueOf(statisticsQueryParam.getSearchType())); + } + //按照年月查询 + else if (statisticsQueryParam.getMonth() != null && statisticsQueryParam.getYear() != null) { + return getDateArray(statisticsQueryParam.getYear(), statisticsQueryParam.getMonth()); + } + //默认查询当前月份 + else { + Calendar calendar = Calendar.getInstance(); + return getDateArray(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1); + } + } + +} diff --git a/framework/src/main/java/cn/lili/modules/statistics/util/StatisticsSuffix.java b/framework/src/main/java/cn/lili/modules/statistics/util/StatisticsSuffix.java new file mode 100644 index 00000000..1f83aae9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/statistics/util/StatisticsSuffix.java @@ -0,0 +1,70 @@ +package cn.lili.modules.statistics.util; + +import java.util.Calendar; + +/** + * 统计缓存后缀工具 + * + * @author Chopper + * @date 2021-01-15 15:30 + */ +public class StatisticsSuffix { + + + /** + * 平台统计后缀 + * + * @return + */ + public static String suffix() { // 取得系统当前时间 + Calendar calendar = Calendar.getInstance(); + int year = calendar.get(Calendar.YEAR); + int month = calendar.get(Calendar.MONTH) + 1; + int day = calendar.get(Calendar.DAY_OF_MONTH); + + return year + "-" + month + "-" + day; + } + + /** + * 平台统计后缀(制定日期) + * + * @return + */ + public static String suffix(Calendar calendar) { // 取得系统当前时间 + int year = calendar.get(Calendar.YEAR); + int month = calendar.get(Calendar.MONTH) + 1; + int day = calendar.get(Calendar.DAY_OF_MONTH); + + return year + "-" + month + "-" + day; + } + + + /** + * 获取商家统计后缀 + * + * @param storeId + * @return + */ + public static String suffix(String storeId) { + Calendar calendar = Calendar.getInstance(); + int year = calendar.get(Calendar.YEAR); + int month = calendar.get(Calendar.MONTH) + 1; + int day = calendar.get(Calendar.DAY_OF_MONTH); + + return year + "-" + month + "-" + day + "-" + storeId; + } + + /** + * 获取商家统计后缀(指定日) + * + * @param storeId + * @return + */ + public static String suffix(Calendar calendar, String storeId) { + int year = calendar.get(Calendar.YEAR); + int month = calendar.get(Calendar.MONTH) + 1; + int day = calendar.get(Calendar.DAY_OF_MONTH); + + return year + "-" + month + "-" + day + "-" + storeId; + } +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dos/Bill.java b/framework/src/main/java/cn/lili/modules/store/entity/dos/Bill.java new file mode 100644 index 00000000..23dd4d19 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dos/Bill.java @@ -0,0 +1,122 @@ +package cn.lili.modules.store.entity.dos; + +import cn.lili.modules.store.entity.enums.BillStatusEnum; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 结算单 + * + * @author Chopper + * @date 2020/11/17 4:27 下午 + */ +@Data +@Entity +@Table(name = "li_bill") +@TableName("li_bill") +@ApiModel(value = "结算单") +public class Bill { + + private static final long serialVersionUID = 1L; + + @Id + @TableId + @TableField + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id ; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @ApiModelProperty(value = "账单号") + private String sn; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "结算开始时间") + private Date startTime; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "结算结束时间") + private Date endTime; + + + /** + * @see BillStatusEnum + */ + @ApiModelProperty(value = "状态:OUT(已出账),CHECK(已对账),EXAMINE(已审核),PAY(已付款)") + private String billStatus; + + @ApiModelProperty(value = "店铺id") + private String storeId; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "平台付款时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private Date payTime; + + @ApiModelProperty(value = "银行开户名") + private String bankAccountName; + + @ApiModelProperty(value = "公司银行账号") + private String bankAccountNumber; + + @ApiModelProperty(value = "开户银行支行名称") + private String bankName; + + @ApiModelProperty(value = "支行联行号") + private String bankCode; + + ////开始算钱啦 + //billPrice=orderPrice-refundPrice + // -commissionPrice+refundCommissionPrice + // -distributionCommission+distributionRefundCommission + // +siteCouponCommission-siteCouponRefundCommission + @ApiModelProperty(value = "结算周期内订单付款总金额") + private Double orderPrice; + + @ApiModelProperty(value = "退单金额") + private Double refundPrice; + + @ApiModelProperty(value = "平台收取佣金") + private Double commissionPrice; + + @ApiModelProperty(value = "退单产生退还佣金金额") + private Double refundCommissionPrice; + + @ApiModelProperty(value = "分销返现支出") + private Double distributionCommission; + + @ApiModelProperty(value = "分销订单退还,返现佣金返还") + private Double distributionRefundCommission; + + @ApiModelProperty(value = "平台优惠券补贴") + private Double siteCouponCommission; + + @ApiModelProperty(value = "退货平台优惠券补贴返还") + private Double siteCouponRefundCommission; + + @ApiModelProperty(value = "最终结算金额") + private Double billPrice; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dos/FreightTemplate.java b/framework/src/main/java/cn/lili/modules/store/entity/dos/FreightTemplate.java new file mode 100644 index 00000000..98058968 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dos/FreightTemplate.java @@ -0,0 +1,42 @@ +package cn.lili.modules.store.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.modules.store.entity.enums.FreightTemplateEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; + +/** + * 运费模板 + * @author Chopper + * @date 2020/11/17 4:27 下午 + * + */ +@Data +@Entity +@Table(name = "li_freight_template") +@TableName("li_freight_template") +@ApiModel(value = "运费模板") +public class FreightTemplate extends BaseEntity { + + @ApiModelProperty(value = "店铺ID", hidden = true) + private String storeId; + + @NotEmpty(message = "模板名称不能为空") + @ApiModelProperty(value = "模板名称") + private String name; + + /** + * @see FreightTemplateEnum + */ + @NotEmpty(message = "计价方式不能为空") + @ApiModelProperty(value = "计价方式:按件、按重量", allowableValues = "WEIGHT, NUM") + private String pricingMethod; + + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dos/FreightTemplateChild.java b/framework/src/main/java/cn/lili/modules/store/entity/dos/FreightTemplateChild.java new file mode 100644 index 00000000..8cbe4ef9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dos/FreightTemplateChild.java @@ -0,0 +1,51 @@ +package cn.lili.modules.store.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 运费模板子配置 + * + * @author Chopper + * @date 2020/11/17 4:27 下午 + */ +@Data +@Entity +@Table(name = "li_freight_template_child") +@TableName("li_freight_template_child") +@ApiModel(value = "运费模板子配置") +public class FreightTemplateChild extends BaseEntity { + + private static final long serialVersionUID = -5043707833032504674L; + + @ApiModelProperty(value = "店铺模板ID") + private String freightTemplateId; + + @ApiModelProperty(value = "首重/首件") + private Double firstCompany; + + @ApiModelProperty(value = "运费") + private Double firstPrice; + + @ApiModelProperty(value = "续重/续件") + private Double continuedCompany; + + @ApiModelProperty(value = "续费") + private Double continuedPrice; + + @ApiModelProperty(value = "地址,示例参数:上海,江苏,浙江") + @Column(columnDefinition = "TEXT") + private String area; + + @ApiModelProperty(value = "地区ID,示例参数:1,2,3,4") + @Column(columnDefinition = "TEXT") + private String areaId; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dos/Store.java b/framework/src/main/java/cn/lili/modules/store/entity/dos/Store.java new file mode 100644 index 00000000..c271a186 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dos/Store.java @@ -0,0 +1,124 @@ +package cn.lili.modules.store.entity.dos; + +import cn.lili.base.BaseEntity; +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.store.entity.dto.AdminStoreApplyDTO; +import cn.lili.modules.store.entity.enums.StoreStatusEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; +import java.util.Date; + +/** + * 店铺 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@Data +@Entity +@Table(name = "li_store") +@TableName("li_store") +@ApiModel(value = "店铺") +@NoArgsConstructor +public class Store extends BaseEntity { + + private static final long serialVersionUID = -5861767726387892272L; + + @ApiModelProperty(value = "会员Id") + private String memberId; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "店铺关闭时间") + private Date storeEndTime; + + /** + * @see StoreStatusEnum + */ + @ApiModelProperty(value = "店铺状态") + private String storeDisable; + + @ApiModelProperty(value = "是否自营") + private Boolean selfOperated; + + @ApiModelProperty(value = "店铺logo") + private String storeLogo; + + @ApiModelProperty(value = "经纬度") + @NotEmpty + private String storeCenter; + + @Size(min = 6, max = 200, message = "店铺简介需在6-200字符之间") + @NotBlank(message = "店铺简介不能为空") + @ApiModelProperty(value = "店铺简介") + private String storeDesc; + + @ApiModelProperty(value = "地址名称, ','分割") + private String storeAddressPath; + + @ApiModelProperty(value = "地址id,','分割 ") + private String storeAddressIdPath; + + @ApiModelProperty(value = "详细地址") + private String storeAddressDetail; + + @ApiModelProperty(value = "描述评分") + private Double descriptionScore; + + @ApiModelProperty(value = "服务评分") + private Double serviceScore; + + @ApiModelProperty(value = "物流描述") + private Double deliveryScore; + + @ApiModelProperty(value = "商品数量") + private Integer goodsNum; + + @ApiModelProperty(value = "收藏数量") + private Integer collectionNum; + + public Store(Member member) { + this.memberId = member.getId(); + this.memberName = member.getUsername(); + storeDisable = StoreStatusEnum.APPLY.value(); + selfOperated = false; + deliveryScore = 5.0; + serviceScore = 5.0; + descriptionScore = 5.0; + goodsNum=0; + collectionNum=0; + } + + public Store(Member member,AdminStoreApplyDTO adminStoreApplyDTO) { + BeanUtil.copyProperties(adminStoreApplyDTO, this); + + this.memberId = member.getId(); + this.memberName = member.getUsername(); + storeDisable = StoreStatusEnum.APPLYING.value(); + selfOperated = false; + deliveryScore = 5.0; + serviceScore = 5.0; + descriptionScore = 5.0; + goodsNum=0; + collectionNum=0; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreAddress.java b/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreAddress.java new file mode 100644 index 00000000..b2b91782 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreAddress.java @@ -0,0 +1,43 @@ +package cn.lili.modules.store.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; + +/** + * 店铺自提点 + * + * @author Bulbasaur + * @date 2020/12/7 15:09 + */ +@Data +@Entity +@TableName("li_store_address") +@Table(name = "li_store_address") +@ApiModel(value = "店铺自提点") +public class StoreAddress extends BaseEntity { + + @ApiModelProperty(value = "店铺id", hidden = true) + private String storeId; + + @NotEmpty + @ApiModelProperty(value = "自提点名称") + private String addressName; + + @ApiModelProperty(value = "经纬度") + @NotEmpty + private String center; + + @ApiModelProperty(value = "地址") + private String address; + + @ApiModelProperty(value = "电话") + private String mobile; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreDetail.java b/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreDetail.java new file mode 100644 index 00000000..e1ac24c8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreDetail.java @@ -0,0 +1,196 @@ +package cn.lili.modules.store.entity.dos; + + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.lili.common.validation.Mobile; +import cn.lili.modules.store.entity.dto.AdminStoreApplyDTO; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.*; +import java.util.Date; + +/** + * 店铺详细 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@Data +@Entity +@Table(name = "li_store_detail") +@TableName("li_store_detail") +@ApiModel(value = "店铺详细") +@NoArgsConstructor +public class StoreDetail { + + private static final long serialVersionUID = 4949782642253898816L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @NotBlank(message = "店铺不能为空") + @ApiModelProperty(value = "店铺id") + private String storeId; + + @Size(min = 2, max = 200, message = "店铺名称长度为2-200位") + @NotBlank(message = "店铺名称不能为空") + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @NotBlank(message = "公司名称不能为空") + @Size(min = 2, max = 100, message = "公司名称错误") + @ApiModelProperty(value = "公司名称") + private String companyName; + + @NotBlank(message = "公司地址不能为空") + @Size(min = 1, max = 200, message = "公司地址,长度为1-200字符") + @ApiModelProperty(value = "公司地址") + private String companyAddress; + + @ApiModelProperty(value = "公司地址地区Id") + private String companyAddressIdPath; + + @ApiModelProperty(value = "公司地址地区") + private String companyAddressPath; + + @Mobile + @ApiModelProperty(value = "公司电话") + private String companyPhone; + + @Email + @ApiModelProperty(value = "电子邮箱") + private String companyEmail; + + @Min(value = 1, message = "员工总数,至少一位") + @ApiModelProperty(value = "员工总数") + private Integer employeeNum; + + @Min(value = 1, message = "注册资金,至少一位") + @ApiModelProperty(value = "注册资金") + private Double registeredCapital; + + @NotBlank(message = "联系人姓名为空") + @Length(min = 2, max = 20, message = "联系人长度为:2-20位字符") + @ApiModelProperty(value = "联系人姓名") + private String linkName; + + @NotBlank(message = "手机号不能为空") + @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误") + @ApiModelProperty(value = "联系人电话") + private String linkPhone; + + @Size(min = 18, max = 18, message = "营业执照长度为18位字符") + @ApiModelProperty(value = "营业执照号") + private String licenseNum; + + @Size(min = 1, max = 200, message = "法定经营范围长度为1-200位字符") + @ApiModelProperty(value = "法定经营范围") + private String scope; + + @NotBlank(message = "营业执照电子版不能为空") + @ApiModelProperty(value = "营业执照电子版") + private String licencePhoto; + + @NotBlank(message = "法人姓名不能为空") + @Size(min = 2, max = 20, message = "法人姓名长度为2-20位字符") + @ApiModelProperty(value = "法人姓名") + private String legalName; + + @NotBlank(message = "法人身份证不能为空") + @Size(min = 18, max = 18, message = "法人身份证号长度为18位") + @ApiModelProperty(value = "法人身份证") + private String legalId; + + @NotBlank(message = "法人身份证不能为空") + @ApiModelProperty(value = "法人身份证照片") + private String legalPhoto; + + @Size(min = 1, max = 200, message = "结算银行开户行名称长度为1-200位") + @NotBlank(message = "结算银行开户行名称不能为空") + @ApiModelProperty(value = "结算银行开户行名称") + private String settlementBankAccountName; + + @Size(min = 1, max = 200, message = "结算银行开户账号长度为1-200位") + @NotBlank(message = "结算银行开户账号不能为空") + @ApiModelProperty(value = "结算银行开户账号") + private String settlementBankAccountNum; + + @Size(min = 1, max = 200, message = "结算银行开户支行名称长度为1-200位") + @NotBlank(message = "结算银行开户支行名称不能为空") + @ApiModelProperty(value = "结算银行开户支行名称") + private String settlementBankBranchName; + + @Size(min = 1, max = 50, message = "结算银行支行联行号长度为1-200位") + @NotBlank(message = "结算银行支行联行号不能为空") + @ApiModelProperty(value = "结算银行支行联行号") + private String settlementBankJointName; + + @NotBlank(message = "店铺经营类目不能为空") + @ApiModelProperty(value = "店铺经营类目") + @Column(columnDefinition = "TEXT") + private String goodsManagementCategory; + + @ApiModelProperty(value = "结算周期") + private String settlementCycle; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "结算日", hidden = true) + private Date settlementDay; + + + @ApiModelProperty(value = "库存预警数量") + private Integer stockWarning; + + /** + * 同城配送达达店铺编码 + */ + @ApiModelProperty(value = "同城配送达达店铺编码") + @TableField(value = "dd_code") + private String ddCode; + + //店铺退货收件地址 + @ApiModelProperty(value = "收货人姓名") + private String salesConsigneeName; + + @ApiModelProperty(value = "收件人手机") + private String salesConsigneeMobile; + + @ApiModelProperty(value = "地址Id, ','分割") + private String salesConsigneeAddressId; + + @ApiModelProperty(value = "地址名称, ','分割") + private String salesConsigneeAddressPath; + + @ApiModelProperty(value = "详细地址") + private String salesConsigneeDetail; + + + public StoreDetail(Store store, AdminStoreApplyDTO adminStoreApplyDTO){ + this.storeId=store.getId(); + //设置店铺公司信息、设置店铺银行信息、设置店铺其他信息 + BeanUtil.copyProperties(adminStoreApplyDTO, this); + this.settlementDay= DateUtil.date(); + this.stockWarning=10; + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreGoodsLabel.java b/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreGoodsLabel.java new file mode 100644 index 00000000..25da3db9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreGoodsLabel.java @@ -0,0 +1,44 @@ +package cn.lili.modules.store.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import java.math.BigDecimal; + +/** + * 店铺商品分类 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@Data +@Entity +@Table(name = "li_store_goods_label") +@TableName("li_store_goods_label") +@ApiModel(value = "店铺商品分类") +public class StoreGoodsLabel extends BaseEntity { + + private static final long serialVersionUID = -5292518678940634419L; + + @ApiModelProperty("店铺ID") + private String storeId; + + @ApiModelProperty("店铺商品分类名称") + private String labelName; + + @ApiModelProperty("店铺商品分类排序") + private BigDecimal sortOrder; + + @ApiModelProperty(value = "父id, 根节点为0") + private String parentId; + + @ApiModelProperty(value = "层级, 从0开始") + private Integer level; + + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreLogistics.java b/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreLogistics.java new file mode 100644 index 00000000..b88b84b3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dos/StoreLogistics.java @@ -0,0 +1,40 @@ +package cn.lili.modules.store.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; + +/** + * 店铺-物流公司设置 + * + * @author Chopper + * @date 2020/11/17 8:01 下午 + */ +@Data +@Entity +@Table(name = "li_store_logistics") +@TableName("li_store_logistics") +@ApiModel(value = "店铺-物流公司") +@AllArgsConstructor +@NoArgsConstructor +public class StoreLogistics extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @ApiModelProperty(value = "物流公司ID") + @NotNull + private String LogisticsId; + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/AdminStoreApplyDTO.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/AdminStoreApplyDTO.java new file mode 100644 index 00000000..a8d63597 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/AdminStoreApplyDTO.java @@ -0,0 +1,170 @@ +package cn.lili.modules.store.entity.dto; + +import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.*; + +/** + * 后台添加店铺信息DTO + * + * @author Bulbasaur + * @date 2020/12/12 11:35 + */ +@Data +public class AdminStoreApplyDTO { + + /****店铺基本信息***/ + @ApiModelProperty(value = "会员ID") + public String memberId; + + @Size(min = 2, max = 200, message = "店铺名称长度为2-200位") + @NotBlank(message = "店铺名称不能为空") + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "店铺logo") + private String storeLogo; + + @Size(min = 6, max = 200, message = "店铺简介需在6-200字符之间") + @NotBlank(message = "店铺简介不能为空") + @ApiModelProperty(value = "店铺简介") + private String storeDesc; + + @ApiModelProperty(value = "经纬度") + private String storeCenter; + + @ApiModelProperty(value = "店铺经营类目") + private String goodsManagementCategory; + + @ApiModelProperty(value = "是否自营") + private boolean selfOperated; + + @ApiModelProperty(value = "地址名称, ','分割") + private String storeAddressPath; + + @ApiModelProperty(value = "地址id,','分割 ") + private String storeAddressIdPath; + + @ApiModelProperty(value = "详细地址") + private String storeAddressDetail; + + /****公司基本信息***/ + @NotBlank(message = "公司名称不能为空") + @Size(min = 2, max = 100, message = "公司名称错误") + @ApiModelProperty(value = "公司名称") + private String companyName; + + @ApiModelProperty(value = "公司电话") + private String companyPhone; + + @NotBlank(message = "公司地址不能为空") + @Size(min = 1, max = 200, message = "公司地址,长度为1-200字符") + @ApiModelProperty(value = "公司地址") + private String companyAddress; + + @ApiModelProperty(value = "公司地址地区Id") + private String companyAddressIdPath; + + @ApiModelProperty(value = "公司地址地区") + private String companyAddressPath; + + @ApiModelProperty(value = "员工总数") + private String employeeNum; + + @Min(value = 1, message = "注册资金,至少一位") + @ApiModelProperty(value = "注册资金") + private Double registeredCapital; + + @NotBlank(message = "联系人姓名为空") + @Length(min = 2, max = 20, message = "联系人长度为:2-20位字符") + @ApiModelProperty(value = "联系人姓名") + private String linkName; + + @NotBlank(message = "手机号不能为空") + @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误") + @ApiModelProperty(value = "联系人电话") + private String linkPhone; + + @Email + @ApiModelProperty(value = "电子邮箱") + private String companyEmail; + + + /****营业执照信息***/ + @Size(min = 18, max = 18, message = "营业执照长度为18位字符") + @ApiModelProperty(value = "营业执照号") + private String licenseNum; + + @Size(min = 1, max = 200, message = "法定经营范围长度为1-200位字符") + @ApiModelProperty(value = "法定经营范围") + private String scope; + + @NotBlank(message = "营业执照电子版不能为空") + @ApiModelProperty(value = "营业执照电子版") + private String licencePhoto; + + /****法人信息***/ + @NotBlank(message = "法人姓名不能为空") + @Size(min = 2, max = 20, message = "法人姓名长度为2-20位字符") + @ApiModelProperty(value = "法人姓名") + private String legalName; + + @NotBlank(message = "法人身份证不能为空") + @Size(min = 18, max = 18, message = "法人身份证号长度为18位") + @ApiModelProperty(value = "法人身份证") + private String legalId; + + @NotBlank(message = "法人身份证不能为空") + @ApiModelProperty(value = "法人身份证照片") + private String legalPhoto; + + /****结算银行信息***/ + @Size(min = 1, max = 200, message = "结算银行开户行名称长度为1-200位") + @NotBlank(message = "结算银行开户行名称不能为空") + @ApiModelProperty(value = "结算银行开户行名称") + private String settlementBankAccountName; + + @Size(min = 1, max = 200, message = "结算银行开户账号长度为1-200位") + @NotBlank(message = "结算银行开户账号不能为空") + @ApiModelProperty(value = "结算银行开户账号") + private String settlementBankAccountNum; + + @Size(min = 1, max = 200, message = "结算银行开户支行名称长度为1-200位") + @NotBlank(message = "结算银行开户支行名称不能为空") + @ApiModelProperty(value = "结算银行开户支行名称") + private String settlementBankBranchName; + + @Size(min = 1, max = 50, message = "结算银行支行联行号长度为1-200位") + @NotBlank(message = "结算银行支行联行号不能为空") + @ApiModelProperty(value = "结算银行支行联行号") + private String settlementBankJointName; + + /****店铺退货收件地址***/ + @ApiModelProperty(value = "收货人姓名") + private String salesConsigneeName; + + @ApiModelProperty(value = "收件人手机") + private String salesConsigneeMobile; + + @ApiModelProperty(value = "地址Id, ','分割") + private String salesConsigneeAddressId; + + @ApiModelProperty(value = "地址名称, ','分割") + private String salesConsigneeAddressPath; + + @ApiModelProperty(value = "详细地址") + private String salesConsigneeDetail; + + + /****配送信息***/ + @ApiModelProperty(value = "同城配送达达店铺编码") + private String ddCode; + + /****结算周期***/ + @ApiModelProperty(value = "结算周期") + private String settlementCycle; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/BillDTO.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/BillDTO.java new file mode 100644 index 00000000..36e0c602 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/BillDTO.java @@ -0,0 +1,60 @@ +package cn.lili.modules.store.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Column; +import java.io.Serializable; + +/** + * 结算单传输对象 + * + * @author Chopper + * @date 2020/11/17 4:26 下午 + */ +@Data +public class BillDTO implements Serializable { + + private static final long serialVersionUID = 4441580387361184989L; + + + @Column(name = "order_price") + @ApiModelProperty(value = "结算周期内订单付款总金额") + private Double orderPrice; + + @Column(name = "refund_price") + @ApiModelProperty(value = "退单金额") + private Double refundPrice; + + @Column(name = "commission_price") + @ApiModelProperty(value = "平台收取佣金") + private Double commissionPrice; + + @Column(name = "refund_commission_price") + @ApiModelProperty(value = "退单产生退还佣金金额") + private Double refundCommissionPrice; + + @Column(name = "distribution_commission") + @ApiModelProperty(value = "分销返现支出") + private Double distributionCommission; + + @Column(name = "distribution_refund_commission") + @ApiModelProperty(value = "分销订单退还,返现佣金返还") + private Double distributionRefundCommission; + + @Column(name = "site_coupon_commission") + @ApiModelProperty(value = "平台优惠券补贴") + private Double siteCouponCommission; + + @Column(name = "site_coupon_refund_commission") + @ApiModelProperty(value = "退货平台优惠券补贴返还") + private Double siteCouponRefundCommission; + + @Column(name = "site_coupon_price") + @ApiModelProperty(value = "平台优惠券 使用金额") + private Double siteCouponPrice; + @Column(name = "site_coupon_point") + @ApiModelProperty(value = "平台优惠券 返点") + private Double siteCouponPoint; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/BillSearchParams.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/BillSearchParams.java new file mode 100644 index 00000000..1ca3b421 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/BillSearchParams.java @@ -0,0 +1,67 @@ +package cn.lili.modules.store.entity.dto; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.store.entity.enums.BillStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * 结算单搜索参数 + * + * @author Chopper + * @date 2021/3/17 6:08 下午 + */ +@Data +public class BillSearchParams extends PageVO { + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "起始日期") + private String startDate; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "结束日期") + private String endDate; + + @ApiModelProperty(value = "账单号") + private String sn; + + /** + * @see BillStatusEnum + */ + @ApiModelProperty(value = "状态:OUT(已出账),CHECK(已对账),EXAMINE(已审核),PAY(已付款)") + private String billStatus; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "店铺ID", hidden = true) + private String storeId; + + public QueryWrapper queryWrapper() { + QueryWrapper wrapper = new QueryWrapper<>(); + + //创建时间 + if (StringUtils.isNotEmpty(startDate) && StringUtils.isNotEmpty(endDate)) { + wrapper.between("create_time", startDate, endDate); + } + //账单号 + wrapper.eq(StringUtils.isNotEmpty(sn), "sn", sn); + //结算状态 + wrapper.eq(StringUtils.isNotEmpty(billStatus), "bill_status", billStatus); + //店铺名称 + wrapper.eq(StringUtils.isNotEmpty(storeName), "store_name", storeName); + // 按卖家查询 + wrapper.eq(StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), + "store_id", UserContext.getCurrentUser().getStoreId()); + return wrapper; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/FreightTemplateChildDTO.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/FreightTemplateChildDTO.java new file mode 100644 index 00000000..ae6bc9b1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/FreightTemplateChildDTO.java @@ -0,0 +1,37 @@ +package cn.lili.modules.store.entity.dto; + +import cn.lili.modules.store.entity.dos.FreightTemplateChild; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.beans.BeanUtils; + +import javax.validation.constraints.NotEmpty; + + +/** + * 模版详细配置 + * + * @author pikachu + * @date 2018-08-22 15:10:51 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class FreightTemplateChildDTO extends FreightTemplateChild { + + private static final long serialVersionUID = -4143478496868965214L; + + + /** + * @see cn.lili.modules.store.entity.enums.FreightTemplateEnum + */ + @NotEmpty(message = "计价方式不能为空") + @ApiModelProperty(value = "计价方式:按件、按重量", allowableValues = "WEIGHT, NUM") + private String pricingMethod; + + public FreightTemplateChildDTO(FreightTemplateChild freightTemplateChild) { + BeanUtils.copyProperties(freightTemplateChild, this); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreAfterSaleAddressDTO.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreAfterSaleAddressDTO.java new file mode 100644 index 00000000..a6c48a8f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreAfterSaleAddressDTO.java @@ -0,0 +1,29 @@ +package cn.lili.modules.store.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 店铺售后收件地址 + * + * @author pikachu + * @date 2020-08-22 15:10:51 + */ +@Data +public class StoreAfterSaleAddressDTO { + + @ApiModelProperty(value = "收货人姓名") + private String salesConsigneeName; + + @ApiModelProperty(value = "收件人手机") + private String salesConsigneeMobile; + + @ApiModelProperty(value = "地址Id, ','分割") + private String salesConsigneeAddressId; + + @ApiModelProperty(value = "地址名称, ','分割") + private String salesConsigneeAddressPath; + + @ApiModelProperty(value = "详细地址") + private String salesConsigneeDetail; +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreBankDTO.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreBankDTO.java new file mode 100644 index 00000000..f846dcf5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreBankDTO.java @@ -0,0 +1,43 @@ +package cn.lili.modules.store.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +/** + * 店铺-银行信息 + * + * @author Bulbasaur + * @date 2020/12/7 15:54 + */ +@Data +public class StoreBankDTO { + + //结算银行信息 + @Size(min = 1, max = 200) + @NotBlank(message = "结算银行开户行名称不能为空") + @ApiModelProperty(value = "结算银行开户行名称") + private String settlementBankAccountName; + + @Size(min = 1, max = 200) + @NotBlank(message = "结算银行开户账号不能为空") + @ApiModelProperty(value = "结算银行开户账号") + private String settlementBankAccountNum; + + @Size(min = 1, max = 200) + @NotBlank(message = "结算银行开户支行名称不能为空") + @ApiModelProperty(value = "结算银行开户支行名称") + private String settlementBankBranchName; + + @Size(min = 1, max = 50) + @NotBlank(message = "结算银行支行联行号不能为空") + @ApiModelProperty(value = "结算银行支行联行号") + private String settlementBankJointName; + + @NotBlank(message = "开户银行许可证电子版不能为空") + @ApiModelProperty(value = "开户银行许可证电子版") + private String settlementBankLicencePhoto; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreCompanyDTO.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreCompanyDTO.java new file mode 100644 index 00000000..d2d48d31 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreCompanyDTO.java @@ -0,0 +1,89 @@ +package cn.lili.modules.store.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.*; + +/** + * 店铺-公司信息 + * + * @author Bulbasaur + * @date 2020/12/7 15:50 + */ +@Data +public class StoreCompanyDTO { + + //公司基本信息 + @Size(min = 2, max = 100) + @NotBlank(message = "公司名称不能为空") + @ApiModelProperty(value = "公司名称") + private String companyName; + + @Size(min = 2, max = 100) + @NotBlank(message = "地址不能为空") + @ApiModelProperty(value = "地址名称, ','分割") + private String addressPath; + + @Size(min = 2, max = 100) + @NotBlank(message = "地址ID不能为空") + @ApiModelProperty(value = "地址id,','分割 ") + private String addressIdPath; + + @Size(min = 1, max = 200) + @NotBlank(message = "公司地址不能为空") + @ApiModelProperty(value = "公司地址") + private String companyAddress; + + @Email + @ApiModelProperty(value = "电子邮箱") + private String companyEmail; + + @Min(1) + @ApiModelProperty(value = "员工总数") + private Integer employeeNum; + + @Min(1) + @ApiModelProperty(value = "注册资金") + private Double registeredCapital; + + @Length(min = 2, max = 20) + @NotBlank(message = "联系人姓名为空") + @ApiModelProperty(value = "联系人姓名") + private String linkName; + + @NotBlank(message = "手机号不能为空") + @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式有误") + @ApiModelProperty(value = "联系人电话") + private String linkPhone; + + //营业执照信息 + @Size(min = 18, max = 18) + @ApiModelProperty(value = "营业执照号") + private String licenseNum; + + @Size(min = 1, max = 200) + @ApiModelProperty(value = "法定经营范围") + private String scope; + + @NotBlank(message = "营业执照电子版不能为空") + @ApiModelProperty(value = "营业执照电子版") + private String licencePhoto; + + //法人信息 + @Size(min = 2, max = 20) + @NotBlank(message = "法人姓名不能为空") + @ApiModelProperty(value = "法人姓名") + private String legalName; + + @Size(min = 18, max = 18) + @NotBlank(message = "法人身份证不能为空") + @ApiModelProperty(value = "法人身份证") + private String legalId; + + @NotBlank(message = "法人身份证不能为空") + @ApiModelProperty(value = "法人身份证照片") + private String legalPhoto; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreEditDTO.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreEditDTO.java new file mode 100644 index 00000000..16273681 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreEditDTO.java @@ -0,0 +1,45 @@ +package cn.lili.modules.store.entity.dto; + +import cn.lili.modules.store.entity.dos.StoreDetail; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +/** + * 店铺修改DTO + * + * @author pikachu + * @date 2020-08-22 15:10:51 + */ +@Data +public class StoreEditDTO extends StoreDetail { + + @ApiModelProperty(value = "店铺状态") + private String storeDisable; + + @ApiModelProperty(value = "是否自营", required = true) + private Integer selfOperated; + + @ApiModelProperty(value = "经纬度") + private String storeCenter; + + @ApiModelProperty(value = "店铺logo") + private String storeLogo; + + @Size(min = 6, max = 200, message = "店铺简介个数需要在6-200位") + @NotBlank(message = "店铺简介不能为空") + @ApiModelProperty(value = "店铺简介") + private String storeDesc; + + @ApiModelProperty(value = "地址名称, ','分割") + private String storeAddressPath; + + @ApiModelProperty(value = "地址id,','分割 ") + private String storeAddressIdPath; + + @ApiModelProperty(value = "详细地址") + private String storeAddressDetail; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreOtherInfoDTO.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreOtherInfoDTO.java new file mode 100644 index 00000000..db5e79cd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreOtherInfoDTO.java @@ -0,0 +1,40 @@ +package cn.lili.modules.store.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; + +/** + * 店铺入驻其他信息 + * + * @author Bulbasaur + * @date 2020/12/7 16:16 + */ +@Data +public class StoreOtherInfoDTO { + + @Size(min = 2, max = 200) + @NotBlank(message = "店铺名称不能为空") + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "店铺logo") + private String storeLogo; + + @Size(min = 6, max = 200) + @NotBlank(message = "店铺简介不能为空") + @ApiModelProperty(value = "店铺简介") + private String storeDesc; + + @ApiModelProperty(value = "经纬度") + @NotEmpty + private String storeCenter; + + @NotBlank(message = "店铺经营类目不能为空") + @ApiModelProperty(value = "店铺经营类目") + private String goodsManagementCategory; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreSettingDTO.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreSettingDTO.java new file mode 100644 index 00000000..c24b13de --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreSettingDTO.java @@ -0,0 +1,36 @@ +package cn.lili.modules.store.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +/** + * 店铺设置 + * + * @author Bulbasaur + * @date 2020/12/16 15:15 + */ +@Data +public class StoreSettingDTO { + + @ApiModelProperty(value = "店铺logo") + private String storeLogo; + + @ApiModelProperty(value = "店铺简介") + private String storeDesc; + + @ApiModelProperty(value = "地址id,','分割 ") + private String storeAddressIdPath; + + @ApiModelProperty(value = "地址名称, ','分割") + private String storeAddressPath; + + @ApiModelProperty(value = "详细地址") + private String storeAddressDetail; + + @NotEmpty + @ApiModelProperty(value = "经纬度") + private String center; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreSettlementDay.java b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreSettlementDay.java new file mode 100644 index 00000000..eda6eba8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/dto/StoreSettlementDay.java @@ -0,0 +1,22 @@ +package cn.lili.modules.store.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 店铺结算日 + * + * @author Chopper + * @date 2021/2/20 3:24 下午 + */ +@Data +public class StoreSettlementDay { + + @ApiModelProperty(value = "店铺ID") + private String storeId; + + @ApiModelProperty(value = "结算日") + private Date settlementDay; +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/enums/BillStatusEnum.java b/framework/src/main/java/cn/lili/modules/store/entity/enums/BillStatusEnum.java new file mode 100644 index 00000000..627c0368 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/enums/BillStatusEnum.java @@ -0,0 +1,24 @@ +package cn.lili.modules.store.entity.enums; + +/** + * 结算单状态 + * + * @author Chopper + * @date 2020/11/17 4:27 下午 + */ +public enum BillStatusEnum { + + OUT("已出账"), + CHECK("已核对"), + COMPLETE("已完成"); + private final String description; + + BillStatusEnum(String description) { + this.description = description; + } + + public String description() { + return description; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/enums/FreightTemplateEnum.java b/framework/src/main/java/cn/lili/modules/store/entity/enums/FreightTemplateEnum.java new file mode 100644 index 00000000..d4a3c720 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/enums/FreightTemplateEnum.java @@ -0,0 +1,19 @@ +package cn.lili.modules.store.entity.enums; + +/** + * 模版枚举 + * + * @author Chopper + * @date 2020/12/23 15:59 + */ + +public enum FreightTemplateEnum { + /** + * 重量 + * 件数 + */ + WEIGHT, NUM + + +} + diff --git a/framework/src/main/java/cn/lili/modules/store/entity/enums/StoreStatusEnum.java b/framework/src/main/java/cn/lili/modules/store/entity/enums/StoreStatusEnum.java new file mode 100644 index 00000000..c50aef58 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/enums/StoreStatusEnum.java @@ -0,0 +1,44 @@ +package cn.lili.modules.store.entity.enums; + +/** + * 店铺状态枚举 + * + * @author pikachu + * @date 2020年3月07日 上午11:04:25 + */ +public enum StoreStatusEnum { + /** + * 开启中 + */ + OPEN("开启中"), + /** + * 店铺关闭 + */ + CLOSED("店铺关闭"), + /** + * 申请开店 + */ + APPLY("申请开店,只要完成第一步骤就是申请"), + /** + * 审核拒绝 + */ + REFUSED("审核拒绝"), + /** + * 申请中 + */ + APPLYING("申请中,提交审核"); + + private final String description; + + StoreStatusEnum(String des) { + this.description = des; + } + + public String description() { + return this.description; + } + + public String value() { + return this.name(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/vos/BillListVO.java b/framework/src/main/java/cn/lili/modules/store/entity/vos/BillListVO.java new file mode 100644 index 00000000..ab7d25f6 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/vos/BillListVO.java @@ -0,0 +1,48 @@ +package cn.lili.modules.store.entity.vos; + +import cn.lili.modules.store.entity.enums.BillStatusEnum; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * 结算单VO + * + * @author pikachu + * @date 2020年3月07日 上午11:04:25 + */ +@Data +public class BillListVO { + + @ApiModelProperty(value = "账单ID") + private String id; + + @ApiModelProperty(value = "账单号") + private String sn; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "结算开始时间") + private Date startTime; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "结算结束时间") + private Date endTime; + + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd") + @ApiModelProperty(value = "出账时间") + private Date createTime; + + /** + * @see BillStatusEnum + */ + @ApiModelProperty(value = "状态:OUT(已出账),RECON(已对账),PASS(已审核),PAY(已付款)") + private String billStatus; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + @ApiModelProperty(value = "最终结算金额") + private Double billPrice; +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/vos/FreightTemplateVO.java b/framework/src/main/java/cn/lili/modules/store/entity/vos/FreightTemplateVO.java new file mode 100644 index 00000000..fc9ab7a7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/vos/FreightTemplateVO.java @@ -0,0 +1,25 @@ +package cn.lili.modules.store.entity.vos; + +import cn.lili.modules.store.entity.dos.FreightTemplate; +import cn.lili.modules.store.entity.dos.FreightTemplateChild; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 店铺运费模板 + * + * @author Bulbasaur + * @date 2020/11/24 14:29 + */ +@Data +public class FreightTemplateVO extends FreightTemplate { + + private static final long serialVersionUID = 2422138942308945537L; + + @ApiModelProperty(value = "运费详细规则") + private List freightTemplateChildList; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreBasicInfoVO.java b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreBasicInfoVO.java new file mode 100644 index 00000000..ffbcae0c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreBasicInfoVO.java @@ -0,0 +1,45 @@ +package cn.lili.modules.store.entity.vos; + +import cn.lili.modules.store.entity.enums.StoreStatusEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 店铺基本信息DTO + * + * @author Bulbasaur + * @date 2020/12/7 14:43 + */ +@Data +public class StoreBasicInfoVO { + + @ApiModelProperty(value = "店铺ID") + private Long storeId; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + + /** + * @see StoreStatusEnum + */ + @ApiModelProperty(value = "店铺状态") + private String storeDisable; + + @ApiModelProperty(value = "地址名称, ','分割") + private String companyAddressPath; + + @ApiModelProperty(value = "店铺logo") + private String storeLogo; + + @ApiModelProperty(value = "店铺简介") + private String storeDesc; + + @ApiModelProperty(value = "PC端页面") + private String pcPageData; + + @ApiModelProperty(value = "移动端页面") + private String mobilePageData; + + @ApiModelProperty(value = "是否自营") + private String selfOperated; +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreDetailVO.java b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreDetailVO.java new file mode 100644 index 00000000..8fd392bd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreDetailVO.java @@ -0,0 +1,19 @@ +package cn.lili.modules.store.entity.vos; + +import cn.lili.modules.store.entity.dto.StoreEditDTO; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 店铺详细VO + * + * @author pikachu + * @date 2020-03-09 21:53:20 + */ +@Data +public class StoreDetailVO extends StoreEditDTO { + + @ApiModelProperty(value = "会员名称") + private String memberName; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreGoodsLabelVO.java b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreGoodsLabelVO.java new file mode 100644 index 00000000..5ab532fe --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreGoodsLabelVO.java @@ -0,0 +1,37 @@ +package cn.lili.modules.store.entity.vos; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 店铺商品分类VO + * + * @author Bulbasaur + * @date 2020/12/11 16:18 + */ +@Data +@NoArgsConstructor +public class StoreGoodsLabelVO { + + @ApiModelProperty("店铺商品分类ID") + private String id; + @ApiModelProperty("店铺商品分类名称") + private String labelName; + @ApiModelProperty(value = "层级, 从0开始") + private Integer level; + @ApiModelProperty("店铺商品分类排序") + private BigDecimal sortOrder; + @ApiModelProperty("下级分类列表") + private List children; + + public StoreGoodsLabelVO(String id, String labelName, Integer level, BigDecimal sortOrder) { + this.id = id; + this.labelName = labelName; + this.level = level; + this.sortOrder = sortOrder; + } +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreManagementCategoryVO.java b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreManagementCategoryVO.java new file mode 100644 index 00000000..e7277c8d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreManagementCategoryVO.java @@ -0,0 +1,25 @@ +package cn.lili.modules.store.entity.vos; + +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.goods.entity.dos.Category; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 店铺经营范围 + * @author Bulbasaur + * @date 2020/12/11 16:18 + */ +@Data +@NoArgsConstructor +public class StoreManagementCategoryVO extends Category { + + @ApiModelProperty(value = "已选择") + private boolean selected; + + public StoreManagementCategoryVO(Category category) { + BeanUtil.copyProperties(this, category); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreSearchParams.java b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreSearchParams.java new file mode 100644 index 00000000..b9cabbc3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreSearchParams.java @@ -0,0 +1,62 @@ +package cn.lili.modules.store.entity.vos; + +import cn.hutool.core.date.DateUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.store.entity.enums.StoreStatusEnum; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 店铺搜索参数VO + * + * @author pikachu + * @date 2020-03-07 17:02:05 + */ +@Data +public class StoreSearchParams implements Serializable { + + private static final long serialVersionUID = 6916054310764833369L; + + @ApiModelProperty(value = "会员名称") + private String memberName; + + @ApiModelProperty(value = "店铺名称") + private String storeName; + /** + * @see StoreStatusEnum + */ + @ApiModelProperty(value = "店铺状态") + private String storeDisable; + + @ApiModelProperty(value = "开始时间") + private String startDate; + + @ApiModelProperty(value = "结束时间") + private String endDate; + + public QueryWrapper queryWrapper() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + if (StringUtils.isNotEmpty(storeName)) { + queryWrapper.like("store_name", storeName); + } + if (StringUtils.isNotEmpty(memberName)) { + queryWrapper.like("member_name", memberName); + } + if (StringUtils.isNotEmpty(storeDisable)) { + queryWrapper.eq("store_disable", storeDisable); + } else { + queryWrapper.eq("store_disable", StoreStatusEnum.OPEN.name()).or().eq("store_disable", StoreStatusEnum.CLOSED.name()); + } + // 按时间查询 + if (StringUtils.isNotEmpty(startDate)) { + queryWrapper.ge("create_time", DateUtil.parse(startDate)); + } + if (StringUtils.isNotEmpty(endDate)) { + queryWrapper.le("create_time", DateUtil.parse(endDate)); + } + return queryWrapper; + } +} diff --git a/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreVO.java b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreVO.java new file mode 100644 index 00000000..cf45db28 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/entity/vos/StoreVO.java @@ -0,0 +1,22 @@ +package cn.lili.modules.store.entity.vos; + +import cn.lili.modules.store.entity.dos.Store; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 店铺VO + * + * @author pikachu + * @date 2020-03-07 17:02:05 + */ +@Data +public class StoreVO extends Store { + + @ApiModelProperty(value = "库存预警数量") + private Integer stockWarning; + + @ApiModelProperty(value = "登录用户的昵称") + private String nickName; + +} diff --git a/framework/src/main/java/cn/lili/modules/store/mapper/BillMapper.java b/framework/src/main/java/cn/lili/modules/store/mapper/BillMapper.java new file mode 100644 index 00000000..cc14d1e7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/mapper/BillMapper.java @@ -0,0 +1,33 @@ +package cn.lili.modules.store.mapper; + + +import cn.lili.modules.store.entity.dos.Bill; +import cn.lili.modules.store.entity.vos.BillListVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 结算单数据处理层 + * + * @author Chopper + * @date 2020/11/17 4:27 下午 + */ +public interface BillMapper extends BaseMapper { + + @Select("select b.id,b.sn,b.start_time,b.end_time,b.bill_status,b.store_name,b.bill_price,b.create_time from li_bill as b ${ew.customSqlSegment}") + IPage queryBillPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Select("SELECT SUM( final_price ) AS orderPrice,SUM( commission_price ) AS commissionPrice" + + ",SUM( distribution_rebate ) AS distributionCommission,SUM( site_coupon_commission ) AS siteCouponCommission" + + ",SUM( bill_price ) AS billPrice FROM li_store_flow WHERE store_id=#{storeId} AND flow_type=#{flowType}") + Bill getOrderBill(String storeId, String flowType); + + @Select("SELECT SUM( final_price ) AS refundPrice,SUM( commission_price ) AS refundCommissionPrice" + + ",SUM( distribution_rebate ) AS distributionRefundCommission,SUM( site_coupon_commission ) AS siteCouponRefundCommission" + + ",SUM( bill_price ) AS billPrice FROM li_store_flow WHERE store_id=#{storeId} AND flow_type=#{flowType}") + Bill getRefundBill(String storeId, String flowType); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/mapper/FreightTemplateChildMapper.java b/framework/src/main/java/cn/lili/modules/store/mapper/FreightTemplateChildMapper.java new file mode 100644 index 00000000..6b1fba54 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/mapper/FreightTemplateChildMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.store.mapper; + +import cn.lili.modules.store.entity.dos.FreightTemplateChild; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 店铺配送子模板数据处理层 + * + * @author Bulbasaur + * @date 2020-03-07 09:18:56 + */ +public interface FreightTemplateChildMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/mapper/FreightTemplateMapper.java b/framework/src/main/java/cn/lili/modules/store/mapper/FreightTemplateMapper.java new file mode 100644 index 00000000..2b612969 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/mapper/FreightTemplateMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.store.mapper; + +import cn.lili.modules.store.entity.dos.FreightTemplate; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 店铺配送模板数据处理层 + * + * @author Bulbasaur + * @date 2020-03-07 09:18:56 + */ +public interface FreightTemplateMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/mapper/StoreAddressMapper.java b/framework/src/main/java/cn/lili/modules/store/mapper/StoreAddressMapper.java new file mode 100644 index 00000000..a0b7f31c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/mapper/StoreAddressMapper.java @@ -0,0 +1,15 @@ +package cn.lili.modules.store.mapper; + +import cn.lili.modules.store.entity.dos.StoreAddress; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 店铺地址(自提点)数据处理层 + * + * @author Bulbasaur + * @date 2020-03-07 09:18:56 + */ +public interface StoreAddressMapper extends BaseMapper { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/mapper/StoreDetailMapper.java b/framework/src/main/java/cn/lili/modules/store/mapper/StoreDetailMapper.java new file mode 100644 index 00000000..0b91d1fe --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/mapper/StoreDetailMapper.java @@ -0,0 +1,46 @@ +package cn.lili.modules.store.mapper; + +import cn.hutool.core.date.DateTime; +import cn.lili.modules.store.entity.dos.StoreDetail; +import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO; +import cn.lili.modules.store.entity.dto.StoreSettlementDay; +import cn.lili.modules.store.entity.vos.StoreBasicInfoVO; +import cn.lili.modules.store.entity.vos.StoreDetailVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +import java.util.List; + +/** + * 店铺详细数据处理层 + * + * @author pikachu + * @date 2020-03-07 09:18:56 + */ +public interface StoreDetailMapper extends BaseMapper { + + @Select("select s.store_logo,s.member_name,s.store_name,s.store_disable,s.self_operated,s.store_address_detail,s.store_address_path,s.store_address_id_path,s.store_center,s.store_desc,d.* " + + "from li_store s inner join li_store_detail d on s.id=d.store_id where s.id=#{storeId}") + StoreDetailVO getStoreDetail(String storeId); + + @Select("select s.member_name,s.store_name,s.store_disable,s.self_operated,s.store_center,s.store_logo,s.store_desc,d.* " + + "from li_store s inner join li_store_detail d on s.id=d.store_id where s.member_id=#{memberId}") + StoreDetailVO getStoreDetailByMemberId(String memberId); + + @Select("SELECT s.id as storeId,s.* FROM li_store s LEFT JOIN li_store_detail sd ON s.id=sd.store_id WHERE s.id=#{storeId}") + StoreBasicInfoVO getStoreBasicInfoDTO(String storeId); + + @Select("select s.sales_consignee_name,s.sales_consignee_mobile,s.sales_consignee_address_id,s.sales_consignee_address_path,s.sales_consignee_detail " + + "from li_store_detail s where s.store_id=#{storeId}") + StoreAfterSaleAddressDTO getStoreAfterSaleAddressDTO(String storeId); + + @Select("SELECT store_id,settlement_day FROM li_store_detail ${ew.customSqlSegment}") + List getSettlementStore(@Param(Constants.WRAPPER) Wrapper queryWrapper); + + @Update("UPDATE li_store_detail SET settlement_day=#{dateTime} WHERE store_id=#{storeId}") + void updateSettlementDay(String storeId, DateTime dateTime); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/mapper/StoreGoodsLabelMapper.java b/framework/src/main/java/cn/lili/modules/store/mapper/StoreGoodsLabelMapper.java new file mode 100644 index 00000000..c59c46bc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/mapper/StoreGoodsLabelMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.store.mapper; + +import cn.lili.modules.store.entity.dos.StoreGoodsLabel; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 店铺商品分类数据处理层 + * + * @author pikachu + * @date 2020-03-07 09:18:56 + */ +public interface StoreGoodsLabelMapper extends BaseMapper { +} diff --git a/framework/src/main/java/cn/lili/modules/store/mapper/StoreMapper.java b/framework/src/main/java/cn/lili/modules/store/mapper/StoreMapper.java new file mode 100644 index 00000000..af894ec2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/mapper/StoreMapper.java @@ -0,0 +1,40 @@ +package cn.lili.modules.store.mapper; + +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.entity.vos.StoreVO; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 店铺数据处理层 + * + * @author pikachu + * @date2020-03-07 09:18:56 + */ +public interface StoreMapper extends BaseMapper { + + /** + * 获取店铺详细 + * + * @param id 店铺ID + * @return 店铺VO + */ + @Select("select s.*,d.* from li_store s inner join li_store_detail d on s.id=d.store_id where s.id=#{id} ") + StoreVO getStoreDetail(String id); + + /** + * 获取店铺分页列表 + * + * @param page 分页 + * @param queryWrapper 查询条件 + * @return 店铺VO分页列表 + */ + @Select("select s.* from li_store as s ${ew.customSqlSegment}") + IPage getStoreList(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/service/BillService.java b/framework/src/main/java/cn/lili/modules/store/service/BillService.java new file mode 100644 index 00000000..2d0e40ee --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/service/BillService.java @@ -0,0 +1,91 @@ +package cn.lili.modules.store.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.store.entity.dos.Bill; +import cn.lili.modules.store.entity.dto.BillSearchParams; +import cn.lili.modules.store.entity.enums.BillStatusEnum; +import cn.lili.modules.store.entity.vos.BillListVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.cache.annotation.CacheConfig; + +import java.util.Date; + +/** + * 结算单业务层 + * + * @author Chopper + * @date 2020/11/17 4:28 下午 + */ +@CacheConfig(cacheNames = "bill") +public interface BillService extends IService { + + /** + * 生成结算单 + * + * @param storeId 商家ID + * @param startTime 开始时间 + */ + void createBill(String storeId, Date startTime); + + + /** + * 立即结算 + * 用于关闭商家,立即结算使用 + * + * @param storeId + * @param endTime 结束时间 + */ + void immediatelyBill(String storeId, Long endTime); + + /** + * 根据结算单ID获取商家流水 + * + * @param pageVO 分页 + * @param id 结算单ID + * @return 商家流水 + */ + IPage getStoreFlow(String id, String type, PageVO pageVO); + + /** + * 根据结算单ID获取商家流水 + * + * @param pageVO 分页 + * @param id 结算单ID + * @return 商家流水 + */ + IPage getDistributionFlow(String id, PageVO pageVO); + + /** + * 获取结算单分页 + * + * @param billSearchParams 结算单搜索条件 + * @return 结算单分页 + */ + IPage billPage(BillSearchParams billSearchParams); + + /** + * 商家核对结算单 + * + * @param id 结算单ID + * @return 操作状态 + */ + boolean check(String id); + + /** + * 平台结算 + * + * @param id 结算单ID + * @return 操作状态 + */ + boolean complete(String id); + + /** + * 商家待结算数量 + * + * @return 待结算商家数量 + */ + Integer billNum(BillStatusEnum billStatusEnum); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/service/FreightTemplateChildService.java b/framework/src/main/java/cn/lili/modules/store/service/FreightTemplateChildService.java new file mode 100644 index 00000000..12aaf05f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/service/FreightTemplateChildService.java @@ -0,0 +1,40 @@ +package cn.lili.modules.store.service; + +import cn.lili.modules.store.entity.dos.FreightTemplateChild; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 配送子模板业务层 + * + * @author Bulbasaur + * @date 2020-03-07 09:24:33 + */ +public interface FreightTemplateChildService extends IService { + + /** + * 获取当前商家的运费模板列表 + * + * @return 运费模板列表 + */ + List getFreightTemplateChild(String freightTemplateId); + + /** + * 添加商家运费模板 + * + * @param freightTemplateChildren 子模板信息 + * @return 运费模板 + */ + boolean addFreightTemplateChild(List freightTemplateChildren); + + + /** + * 删除商家运费模板 + * + * @param freightTemplateId 运费模板ID + * @return 操作状态 + */ + boolean removeFreightTemplate(String freightTemplateId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/service/FreightTemplateService.java b/framework/src/main/java/cn/lili/modules/store/service/FreightTemplateService.java new file mode 100644 index 00000000..cecc0ba2 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/service/FreightTemplateService.java @@ -0,0 +1,67 @@ +package cn.lili.modules.store.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.store.entity.dos.FreightTemplate; +import cn.lili.modules.store.entity.vos.FreightTemplateVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 店铺地址(自提点)详细业务层 + * + * @author Bulbasaur + * @date 2020-03-07 09:24:33 + */ +public interface FreightTemplateService extends IService { + + /** + * 获取当前商家的运费模板列表 + * + * @return 运费模板列表 + */ + IPage getFreightTemplate(PageVO pageVo); + + /** + * 获取商家的运费模板 + * + * @param storeId + * @return 运费模板列表 + */ + List getFreightTemplateList(String storeId); + + /** + * 获取运费模板详细信息 + * + * @param id 运费模板ID + * @return 运费模板 + */ + FreightTemplateVO getFreightTemplate(String id); + + /** + * 添加商家运费模板 + * + * @param freightTemplateVO 运费模板 + * @return 运费模板 + */ + FreightTemplateVO addFreightTemplate(FreightTemplateVO freightTemplateVO); + + /** + * 修改商家运费模板 + * + * @param freightTemplateVO 运费模板 + * @return 运费模板 + */ + FreightTemplateVO editFreightTemplate(FreightTemplateVO freightTemplateVO); + + /** + * 删除商家运费模板 + * 删除模板并删除模板的配置内容 + * + * @param id 运费模板ID + * @return 操作状态 + */ + boolean removeFreightTemplate(String id); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/service/StoreAddressService.java b/framework/src/main/java/cn/lili/modules/store/service/StoreAddressService.java new file mode 100644 index 00000000..3c808f46 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/service/StoreAddressService.java @@ -0,0 +1,47 @@ +package cn.lili.modules.store.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.store.entity.dos.StoreAddress; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 店铺地址(自提点)详细业务层 + * + * @author Bulbasaur + * @date 2020-03-07 09:24:33 + */ +public interface StoreAddressService extends IService { + + /** + * 获取当前商家的自提点列表 + * + * @return 自提点列表 + */ + IPage getStoreAddress(PageVO pageVo); + + /** + * 添加商家自提点 + * + * @param storeAddress 自提点 + * @return 自提点 + */ + StoreAddress addStoreAddress(StoreAddress storeAddress); + + /** + * 修改商家自提点 + * + * @param storeAddress 自提点 + * @return 自提点 + */ + StoreAddress editStoreAddress(StoreAddress storeAddress); + + /** + * 删除商家自提点 + * + * @param id 自提点ID + * @return 操作状态 + */ + Boolean removeStoreAddress(String id); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/service/StoreDetailService.java b/framework/src/main/java/cn/lili/modules/store/service/StoreDetailService.java new file mode 100644 index 00000000..8f01b0e8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/service/StoreDetailService.java @@ -0,0 +1,89 @@ +package cn.lili.modules.store.service; + +import cn.lili.modules.store.entity.dos.StoreDetail; +import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO; +import cn.lili.modules.store.entity.dto.StoreSettingDTO; +import cn.lili.modules.store.entity.vos.StoreBasicInfoVO; +import cn.lili.modules.store.entity.vos.StoreDetailVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 店铺详细业务层 + * + * @author pikachu + * @date 2020-03-07 09:24:33 + */ +public interface StoreDetailService extends IService { + /** + * 根据店铺ID获取店铺信息VO + * + * @param storeId 店铺ID + * @return 店铺信息VO + */ + StoreDetailVO getStoreDetailVO(String storeId); + + /** + * 根据会员ID获取店铺信息VO + * + * @param memberId 会员ID + * @return 店铺信息VO + */ + StoreDetailVO getStoreDetailVOByMemberId(String memberId); + + /** + * 根据店铺ID获取店铺信息DO + * + * @param storeId 店铺ID + * @return 店铺信息DO + */ + StoreDetail getStoreDetail(String storeId); + + /** + * 修改商家设置 + * + * @param storeSettingDTO 店铺设置信息 + * @return 店铺详情 + */ + Boolean editStoreSetting(StoreSettingDTO storeSettingDTO); + + /** + * 获取店铺基本信息 + * 用于前端店铺信息展示 + * + * @param storeId 店铺ID + * @return 店铺基本信息 + */ + StoreBasicInfoVO getStoreBasicInfoDTO(String storeId); + + /** + * 获取当前登录店铺售后收件地址 + * + * @return 店铺售后收件地址 + */ + StoreAfterSaleAddressDTO getStoreAfterSaleAddressDTO(); + + /** + * 获取某一个店铺的退货收件地址信息 + * + * @return 店铺售后收件地址 + */ + StoreAfterSaleAddressDTO getStoreAfterSaleAddressDTO(String id); + + /** + * 修改当前登录店铺售后收件地址 + * + * @return 店铺售后收件地址 + */ + boolean editStoreAfterSaleAddressDTO(StoreAfterSaleAddressDTO storeAfterSaleAddressDTO); + + /** + * 修改店铺库存预警数量 + * @param stockWarning 库存预警数量 + * @return 操作状态 + */ + boolean updateStockWarning(Integer stockWarning); + + List goodsManagementCategory(String storeId); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/service/StoreGoodsLabelService.java b/framework/src/main/java/cn/lili/modules/store/service/StoreGoodsLabelService.java new file mode 100644 index 00000000..7ba4c0f9 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/service/StoreGoodsLabelService.java @@ -0,0 +1,55 @@ +package cn.lili.modules.store.service; + +import cn.lili.modules.store.entity.dos.StoreGoodsLabel; +import cn.lili.modules.store.entity.vos.StoreGoodsLabelVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 店铺商品分类业务层 + * + * @author Bulbasaur + * @date 2020-03-07 09:24:33 + */ +public interface StoreGoodsLabelService extends IService { + + /** + * 根据商家ID获取店铺分类列表 + * + * @param storeId 商家ID + * @return 店铺分类列表 + */ + List listByStoreId(String storeId); + + /** + * 获取当前店铺的店铺分类列表 + * + * @return 店铺分类列表 + */ + List listByStore(); + + /** + * 添加商品分类 + * + * @param storeGoodsLabel 店铺商品分类 + * @return 店铺商品分类 + */ + StoreGoodsLabel addStoreGoodsLabel(StoreGoodsLabel storeGoodsLabel); + + /** + * 修改商品分类 + * + * @param storeGoodsLabel 店铺商品分类 + * @return 店铺商品分类 + */ + StoreGoodsLabel editStoreGoodsLabel(StoreGoodsLabel storeGoodsLabel); + + /** + * 删除商品分类 + * + * @param storeLabelId 店铺 分类 ID + */ + void removeStoreGoodsLabel(String storeLabelId); + +} diff --git a/framework/src/main/java/cn/lili/modules/store/service/StoreService.java b/framework/src/main/java/cn/lili/modules/store/service/StoreService.java new file mode 100644 index 00000000..8731846a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/service/StoreService.java @@ -0,0 +1,132 @@ +package cn.lili.modules.store.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.entity.dto.*; +import cn.lili.modules.store.entity.vos.StoreSearchParams; +import cn.lili.modules.store.entity.vos.StoreVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 店铺业务层 + * + * @author pikachu + * @date 2020/11/18 11:45 上午 + */ +public interface StoreService extends IService { + + /** + * 分页条件查询 + * 用于展示店铺列表 + * + * @param entity + * @param page + * @return + */ + IPage findByConditionPage(StoreSearchParams entity, PageVO page); + + /** + * 获取当前登录店铺信息 + * + * @return 店铺信息DO + */ + StoreVO getStoreDetail(); + + /** + * 增加店铺 + * 用于后台添加店铺 + * + * @param adminStoreApplyDTO 后台添加店铺信息 + */ + Store add(AdminStoreApplyDTO adminStoreApplyDTO); + + /** + * 编辑店铺 + * + * @param storeEditDTO 店铺修改信息 + */ + Store edit(StoreEditDTO storeEditDTO); + + /** + * 审核店铺 + * + * @param id 店铺ID + * @param passed 审核结果 + */ + boolean audit(String id, Integer passed); + + /** + * 关闭店铺 + * + * @param id 店铺ID + * @return 店铺 + */ + boolean disable(String id); + + /** + * 开启店铺 + * + * @param id 店铺ID + * @return 操作状态 + */ + boolean enable(String id); + + /** + * 申请店铺第一步 + * 设置店铺公司信息,如果没有店铺新建店铺 + * + * @param storeCompanyDTO 店铺公司信息 + * @return 店铺 + */ + boolean applyFirstStep(StoreCompanyDTO storeCompanyDTO); + + /** + * 申请店铺第二步 + * + * @param storeBankDTO 店铺银行信息 + * @return 店铺 + */ + boolean applySecondStep(StoreBankDTO storeBankDTO); + + /** + * 申请店铺第三步 + * 设置店铺信息,经营范围 + * + * @param storeOtherInfoDTO 店铺其他信息 + * @return 店铺 + */ + boolean applyThirdStep(StoreOtherInfoDTO storeOtherInfoDTO); + + /** + * 获取待审核店铺数量 + * @return 待审核店铺数量 + */ + Integer auditNum(); + + /** + * 获取所有店铺数量 + * + * @return 店铺总数 + */ + Integer storeNum(); + + /** + * 获取今天的店铺数量 + * + * @return 今天的店铺数量 + */ + Integer todayStoreNum(); + + /** + * 更新店铺商品数量 + * @param storeId 店铺ID + */ + void updateStoreGoodsNum(String storeId); + + /** + * 更新店铺收藏数量 + * @param goodsId 店铺ID + */ + void updateStoreCollectionNum(String goodsId); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/serviceimpl/BillServiceImpl.java b/framework/src/main/java/cn/lili/modules/store/serviceimpl/BillServiceImpl.java new file mode 100644 index 00000000..e3e6f5ce --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/serviceimpl/BillServiceImpl.java @@ -0,0 +1,204 @@ +package cn.lili.modules.store.serviceimpl; + +import cn.hutool.core.date.DateUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.utils.CurrencyUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.SnowFlake; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.order.order.entity.enums.FlowTypeEnum; +import cn.lili.modules.order.order.service.StoreFlowService; +import cn.lili.modules.store.entity.dos.Bill; +import cn.lili.modules.store.entity.dto.BillSearchParams; +import cn.lili.modules.store.entity.enums.BillStatusEnum; +import cn.lili.modules.store.entity.vos.BillListVO; +import cn.lili.modules.store.entity.vos.StoreDetailVO; +import cn.lili.modules.store.mapper.BillMapper; +import cn.lili.modules.store.service.BillService; +import cn.lili.modules.store.service.StoreDetailService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; + +/** + * 结算单业务层实现 + * + * @author Chopper + * @date 2020/11/17 4:28 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BillServiceImpl extends ServiceImpl implements BillService { + + //店铺详情 + private final StoreDetailService storeDetailService; + //商家流水 + private StoreFlowService storeFlowService; + //结算单 + private final BillMapper billMapper; + + @Override + public void createBill(String storeId, Date startTime) { + + //获取结算店铺 + StoreDetailVO store = storeDetailService.getStoreDetailVO(storeId); + Bill bill = new Bill(); + + //结算基础信息 + bill.setStartTime(startTime); + bill.setEndTime(DateUtil.yesterday()); + bill.setBillStatus(BillStatusEnum.OUT.name()); + bill.setStoreId(storeId); + bill.setStoreName(store.getStoreName()); + + //设置结算信息 + bill.setBankAccountName(store.getSettlementBankAccountName()); + bill.setBankAccountNumber(store.getSettlementBankAccountNum()); + bill.setBankCode(store.getSettlementBankJointName()); + bill.setBankName(store.getSettlementBankBranchName()); + + //店铺结算单号 + bill.setSn(SnowFlake.createStr("B")); + + //入账结算信息 + Bill orderBill = billMapper.getOrderBill(storeId, FlowTypeEnum.PAY.name()); + Double orderPrice = 0D; + if (orderBill != null) { + bill.setOrderPrice(orderBill.getOrderPrice()); + bill.setCommissionPrice(orderBill.getCommissionPrice()); + bill.setDistributionCommission(orderBill.getDistributionCommission()); + bill.setSiteCouponCommission(orderBill.getSiteCouponCommission()); + orderPrice=orderBill.getBillPrice(); + } + + + //退款结算信息 + Bill refundBill = billMapper.getRefundBill(storeId, FlowTypeEnum.REFUND.name()); + Double refundPrice = 0D; + if(refundBill!=null){ + bill.setRefundPrice(refundBill.getRefundPrice()); + bill.setRefundCommissionPrice(refundBill.getRefundCommissionPrice()); + bill.setDistributionRefundCommission(refundBill.getDistributionRefundCommission()); + bill.setSiteCouponRefundCommission(refundBill.getSiteCouponRefundCommission()); + refundPrice=refundBill.getBillPrice(); + } + + //最终结算金额=入款结算金额-退款结算金额 + Double finalPrice = CurrencyUtil.sub(orderPrice, refundPrice); + bill.setBillPrice(finalPrice); + + //添加结算单 + this.save(bill); + + } + + /** + * 立即结算 + * + * @param storeId + * @param endTime 结束时间 + */ + @Override + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) + public void immediatelyBill(String storeId, Long endTime) { + +// Long now = DateUtil.getDateline(); +// //TODO 需要获取真实店铺 +// StoreDetailVO store = new StoreDetailVO(); +// Long startTime = store.getLastBillTime().getTime(); +// +// store.setLastBillTime(new Date(now)); +//// TODO store.save 保存新的结束时间 +// +// // TODO 获取结算周期内的结算详情 +// BillDTO billDTO = new BillDTO(); +// +// //如果没有需要结算单,那么就可以直接返回,也不需要保存新的结算单 +// if (billDTO.getOrderPrice() == 0 && billDTO.getRefundPrice() == 0) { +// return; +// } +// +// this.createBill(storeId, startTime, endTime); + } + + @Override + public IPage getStoreFlow(String id, String type, PageVO pageVO) { + Bill bill = this.getById(id); + return storeFlowService.getStoreFlow(bill.getStoreId(), type, false, pageVO, bill.getStartTime(), bill.getCreateTime()); + } + + @Override + public IPage getDistributionFlow(String id, PageVO pageVO) { + Bill bill = this.getById(id); + return storeFlowService.getStoreFlow(bill.getStoreId(), null, true, pageVO, bill.getStartTime(), bill.getCreateTime()); + } + + @Override + public IPage billPage(BillSearchParams billSearchParams) { + QueryWrapper queryWrapper = billSearchParams.queryWrapper(); + return billMapper.queryBillPage(PageUtil.initPage(billSearchParams), queryWrapper); + } + + @Override + public boolean check(String id) { + Bill bill = this.getById(id); + //判断当前结算单状态为:出账 + if (!bill.getBillStatus().equals(BillStatusEnum.OUT.name())) { + throw new ServiceException(ResultCode.BILL_CHECK_ERROR); + } + //判断操作人员为商家 + if (!UserContext.getCurrentUser().getRole().equals(UserEnums.STORE)) { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.eq(Bill::getId, id); + lambdaUpdateWrapper.set(Bill::getBillStatus, BillStatusEnum.CHECK.name()); + return this.update(lambdaUpdateWrapper); + } + + @Override + public boolean complete(String id) { + Bill bill = this.getById(id); + //判断当前结算单状态为:已核对 + if (!bill.getBillStatus().equals(BillStatusEnum.CHECK.name())) { + throw new ServiceException(ResultCode.BILL_COMPLETE_ERROR); + } + //判断操作人员为后台管理员 + if (!UserContext.getCurrentUser().getRole().equals(UserEnums.MANAGER)) { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.eq(Bill::getId, id); + lambdaUpdateWrapper.set(Bill::getBillStatus, BillStatusEnum.COMPLETE.name()); + return this.update(lambdaUpdateWrapper); + } + + public Integer billNum(BillStatusEnum billStatusEnum) { + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.eq(Bill::getBillStatus, billStatusEnum.name()); + lambdaUpdateWrapper.eq(StringUtils.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), + Bill::getStoreId, UserContext.getCurrentUser().getStoreId()); + return this.count(lambdaUpdateWrapper); + } + + @Autowired + public void setStoreFlowService(StoreFlowService storeFlowService) { + this.storeFlowService = storeFlowService; + } + +} diff --git a/framework/src/main/java/cn/lili/modules/store/serviceimpl/FreightTemplateServiceChildImpl.java b/framework/src/main/java/cn/lili/modules/store/serviceimpl/FreightTemplateServiceChildImpl.java new file mode 100644 index 00000000..b36965cb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/serviceimpl/FreightTemplateServiceChildImpl.java @@ -0,0 +1,52 @@ +package cn.lili.modules.store.serviceimpl; + +import cn.lili.modules.store.entity.dos.FreightTemplateChild; +import cn.lili.modules.store.mapper.FreightTemplateChildMapper; +import cn.lili.modules.store.service.FreightTemplateChildService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 配送子模板业务层实现 + * + * @author Bulbasaur + * @date 2020-03-07 09:24:33 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FreightTemplateServiceChildImpl extends ServiceImpl implements FreightTemplateChildService { + + //配送子模板数据层 + private final FreightTemplateChildMapper freightTemplateChildMapper; + + @Override + public List getFreightTemplateChild(String freightTemplateId) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(FreightTemplateChild::getFreightTemplateId, freightTemplateId); + return freightTemplateChildMapper.selectList(lambdaQueryWrapper); + } + + @Override + @Transactional + public boolean addFreightTemplateChild(List freightTemplateChildren) { + return this.saveBatch(freightTemplateChildren); + } + + @Override + @Transactional + public boolean removeFreightTemplate(String freightTemplateId) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(FreightTemplateChild::getFreightTemplateId, freightTemplateId); + return this.remove(lambdaQueryWrapper); + } + + +} diff --git a/framework/src/main/java/cn/lili/modules/store/serviceimpl/FreightTemplateServiceImpl.java b/framework/src/main/java/cn/lili/modules/store/serviceimpl/FreightTemplateServiceImpl.java new file mode 100644 index 00000000..49136efa --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/serviceimpl/FreightTemplateServiceImpl.java @@ -0,0 +1,171 @@ +package cn.lili.modules.store.serviceimpl; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.store.entity.dos.FreightTemplate; +import cn.lili.modules.store.entity.dos.FreightTemplateChild; +import cn.lili.modules.store.entity.vos.FreightTemplateVO; +import cn.lili.modules.store.mapper.FreightTemplateMapper; +import cn.lili.modules.store.service.FreightTemplateChildService; +import cn.lili.modules.store.service.FreightTemplateService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 店铺运费模板业务层实现 + * + * @author Bulbasaur + * @date 2020/11/22 16:00 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FreightTemplateServiceImpl extends ServiceImpl implements FreightTemplateService { + + //配送模板 + private final FreightTemplateMapper freightTemplateMapper; + //配送子模板 + private final FreightTemplateChildService freightTemplateChildService; + //缓存 + private final Cache cache; + + + @Override + public List getFreightTemplateList(String storeId) { + //先从缓存中获取运费模板,如果有则直接返回,如果没有则查询数据后再返回 + List list = (List) cache.get(CachePrefix.SHIP_TEMPLATE.getPrefix() + storeId); + if (list != null) { + return list; + } + list = new ArrayList<>(); + //查询运费模板 + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(FreightTemplate::getStoreId, UserContext.getCurrentUser().getId()); + List freightTemplates = freightTemplateMapper.selectList(lambdaQueryWrapper); + if (!freightTemplates.isEmpty()) { + //如果模板不为空则查询子模板信息 + for (FreightTemplate freightTemplate : freightTemplates) { + FreightTemplateVO freightTemplateVO = new FreightTemplateVO(); + BeanUtil.copyProperties(freightTemplate, freightTemplateVO); + List freightTemplateChildren = freightTemplateChildService.getFreightTemplateChild(freightTemplate.getId()); + if (!freightTemplateChildren.isEmpty()) { + freightTemplateVO.setFreightTemplateChildList(freightTemplateChildren); + } + list.add(freightTemplateVO); + } + } + cache.put(CachePrefix.SHIP_TEMPLATE.getPrefix() + storeId, list); + return list; + + } + + @Override + public IPage getFreightTemplate(PageVO pageVo) { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(FreightTemplate::getStoreId, UserContext.getCurrentUser().getId()); + return freightTemplateMapper.selectPage(PageUtil.initPage(pageVo), lambdaQueryWrapper); + } + + @Override + public FreightTemplateVO getFreightTemplate(String id) { + FreightTemplateVO freightTemplateVO = new FreightTemplateVO(); + //获取运费模板 + FreightTemplate freightTemplate = freightTemplateMapper.selectById(id); + if (freightTemplate != null) { + //复制属性 + BeanUtils.copyProperties(freightTemplate, freightTemplateVO); + //填写运费模板子内容 + List freightTemplateChildList = freightTemplateChildService.getFreightTemplateChild(id); + freightTemplateVO.setFreightTemplateChildList(freightTemplateChildList); + } + return freightTemplateVO; + } + + @Override + public FreightTemplateVO addFreightTemplate(FreightTemplateVO freightTemplateVO) { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + FreightTemplate freightTemplate = new FreightTemplate(); + //设置店铺ID + freightTemplateVO.setStoreId(tokenUser.getId()); + //复制属性 + BeanUtils.copyProperties(freightTemplateVO, freightTemplate); + //添加运费模板 + freightTemplateMapper.insert(freightTemplate); + //给子模板赋父模板的id + List list = new ArrayList<>(); + for (FreightTemplateChild freightTemplateChild : freightTemplateVO.getFreightTemplateChildList()) { + freightTemplateChild.setFreightTemplateId(freightTemplate.getId()); + list.add(freightTemplateChild); + } + //添加运费模板子内容 + freightTemplateChildService.addFreightTemplateChild(list); + //更新缓存 + cache.remove(CachePrefix.SHIP_TEMPLATE.getPrefix() + tokenUser.getStoreId()); + return freightTemplateVO; + } + + @Override + @Transactional + public FreightTemplateVO editFreightTemplate(FreightTemplateVO freightTemplateVO) { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + if (freightTemplateVO.getId().equals(tokenUser.getStoreId())) { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + FreightTemplate freightTemplate = new FreightTemplate(); + //复制属性 + BeanUtils.copyProperties(freightTemplateVO, freightTemplate); + //修改运费模板 + freightTemplateMapper.updateById(freightTemplate); + //删除模板子内容 + freightTemplateChildService.removeFreightTemplate(freightTemplateVO.getId()); + //给子模板赋父模板的id + List list = new ArrayList<>(); + for (FreightTemplateChild freightTemplateChild : freightTemplateVO.getFreightTemplateChildList()) { + freightTemplateChild.setFreightTemplateId(freightTemplate.getId()); + list.add(freightTemplateChild); + } + //添加模板子内容 + freightTemplateChildService.addFreightTemplateChild(list); + //更新缓存 + cache.remove(CachePrefix.SHIP_TEMPLATE.getPrefix() + tokenUser.getStoreId()); + return null; + } + + + @Override + @Transactional + public boolean removeFreightTemplate(String id) { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(FreightTemplate::getStoreId, tokenUser.getStoreId()); + lambdaQueryWrapper.eq(FreightTemplate::getId, id); + //如果删除成功则删除运费模板子项 + if (this.remove(lambdaQueryWrapper)) { + cache.remove(CachePrefix.SHIP_TEMPLATE.getPrefix() + tokenUser.getStoreId()); + return freightTemplateChildService.removeFreightTemplate(id); + } + return false; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreAddressServiceImpl.java b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreAddressServiceImpl.java new file mode 100644 index 00000000..9a9258ae --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreAddressServiceImpl.java @@ -0,0 +1,66 @@ +package cn.lili.modules.store.serviceimpl; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.store.entity.dos.StoreAddress; +import cn.lili.modules.store.mapper.StoreAddressMapper; +import cn.lili.modules.store.service.StoreAddressService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 店铺地址(自提点)业务层实现 + * + * @author Bulbasaur + * @date 2020/11/22 16:00 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreAddressServiceImpl extends ServiceImpl implements StoreAddressService { + + private final StoreAddressMapper storeAddressMapper; + + @Override + public IPage getStoreAddress(PageVO pageVo) { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(StoreAddress::getStoreId, UserContext.getCurrentUser().getStoreId()); + return storeAddressMapper.selectPage(PageUtil.initPage(pageVo), lambdaQueryWrapper); + } + + @Override + public StoreAddress addStoreAddress(StoreAddress storeAddress) { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + storeAddress.setStoreId(tokenUser.getId()); + //添加自提点 + storeAddressMapper.insert(storeAddress); + return storeAddress; + } + + @Override + public StoreAddress editStoreAddress(StoreAddress storeAddress) { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + storeAddress.setStoreId(tokenUser.getId()); + //添加自提点 + storeAddressMapper.updateById(storeAddress); + return storeAddress; + } + + @Override + public Boolean removeStoreAddress(String id) { + storeAddressMapper.deleteById(id); + return true; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreDetailServiceImpl.java b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreDetailServiceImpl.java new file mode 100644 index 00000000..8eb75ddb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreDetailServiceImpl.java @@ -0,0 +1,135 @@ +package cn.lili.modules.store.serviceimpl; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.goods.entity.dos.Category; +import cn.lili.modules.goods.service.CategoryService; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.entity.dos.StoreDetail; +import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO; +import cn.lili.modules.store.entity.dto.StoreSettingDTO; +import cn.lili.modules.store.entity.vos.StoreBasicInfoVO; +import cn.lili.modules.store.entity.vos.StoreDetailVO; +import cn.lili.modules.store.entity.vos.StoreManagementCategoryVO; +import cn.lili.modules.store.mapper.StoreDetailMapper; +import cn.lili.modules.store.service.StoreDetailService; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 店铺详细业务层实现 + * + * @author pikachu + * @date 2020-03-07 16:18:56 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreDetailServiceImpl extends ServiceImpl implements StoreDetailService { + + //店铺详情数据层 + private final StoreDetailMapper storeDetailMapper; + //店铺 + private final StoreService storeService; + //分类 + private final CategoryService categoryService; + + @Override + public StoreDetailVO getStoreDetailVO(String storeId) { + return storeDetailMapper.getStoreDetail(storeId); + } + + @Override + public StoreDetailVO getStoreDetailVOByMemberId(String memberId) { + return storeDetailMapper.getStoreDetailByMemberId(memberId); + } + + @Override + public StoreDetail getStoreDetail(String storeId) { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(StoreDetail::getStoreId, storeId); + return this.getOne(lambdaQueryWrapper); + } + + @Override + public Boolean editStoreSetting(StoreSettingDTO storeSettingDTO) { + AuthUser tokenUser = UserContext.getCurrentUser(); + //修改店铺 + Store store = storeService.getById(tokenUser.getStoreId()); + BeanUtil.copyProperties(storeSettingDTO, store); + return storeService.updateById(store); + } + + @Override + public StoreBasicInfoVO getStoreBasicInfoDTO(String storeId) { + return storeDetailMapper.getStoreBasicInfoDTO(storeId); + } + + @Override + public StoreAfterSaleAddressDTO getStoreAfterSaleAddressDTO() { + return storeDetailMapper.getStoreAfterSaleAddressDTO(UserContext.getCurrentUser().getStoreId()); + } + + @Override + public StoreAfterSaleAddressDTO getStoreAfterSaleAddressDTO(String id) { + StoreAfterSaleAddressDTO storeAfterSaleAddressDTO = storeDetailMapper.getStoreAfterSaleAddressDTO(id); + if (storeAfterSaleAddressDTO == null) { + storeAfterSaleAddressDTO = new StoreAfterSaleAddressDTO(); + } + return storeAfterSaleAddressDTO; + } + + @Override + public boolean editStoreAfterSaleAddressDTO(StoreAfterSaleAddressDTO storeAfterSaleAddressDTO) { + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.set(StoreDetail::getSalesConsigneeName, storeAfterSaleAddressDTO.getSalesConsigneeName()); + lambdaUpdateWrapper.set(StoreDetail::getSalesConsigneeAddressId, storeAfterSaleAddressDTO.getSalesConsigneeAddressId()); + lambdaUpdateWrapper.set(StoreDetail::getSalesConsigneeAddressPath, storeAfterSaleAddressDTO.getSalesConsigneeAddressPath()); + lambdaUpdateWrapper.set(StoreDetail::getSalesConsigneeDetail, storeAfterSaleAddressDTO.getSalesConsigneeDetail()); + lambdaUpdateWrapper.set(StoreDetail::getSalesConsigneeMobile, storeAfterSaleAddressDTO.getSalesConsigneeMobile()); + lambdaUpdateWrapper.eq(StoreDetail::getStoreId, UserContext.getCurrentUser().getStoreId()); + return this.update(lambdaUpdateWrapper); + } + + @Override + public boolean updateStockWarning(Integer stockWarning) { + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.set(StoreDetail::getStockWarning, stockWarning); + lambdaUpdateWrapper.eq(StoreDetail::getStoreId, UserContext.getCurrentUser().getStoreId()); + return this.update(lambdaUpdateWrapper); + } + + @Override + public List goodsManagementCategory(String storeId) { + + //获取顶部分类列表 + List categoryList = categoryService.firstCategory(); + //获取店铺信息 + StoreDetail storeDetail = this.getById(storeId); + //获取店铺分类 + String[] storeCategoryList = storeDetail.getGoodsManagementCategory().split(","); + List list = new ArrayList<>(); + for (Category category : categoryList) { + StoreManagementCategoryVO storeManagementCategoryVO = new StoreManagementCategoryVO(category); + for (String storeCategory:storeCategoryList) { + if (storeCategory.equals(category.getId())) { + storeManagementCategoryVO.setSelected(true); + } + } + list.add(storeManagementCategoryVO); + } + return list; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreGoodsLabelServiceImpl.java b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreGoodsLabelServiceImpl.java new file mode 100644 index 00000000..7b5398d4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreGoodsLabelServiceImpl.java @@ -0,0 +1,98 @@ +package cn.lili.modules.store.serviceimpl; + +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.modules.store.entity.dos.StoreGoodsLabel; +import cn.lili.modules.store.entity.vos.StoreGoodsLabelVO; +import cn.lili.modules.store.mapper.StoreGoodsLabelMapper; +import cn.lili.modules.store.service.StoreGoodsLabelService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/** + * 店铺商品分类业务层实现 + * + * @author Bulbasaur + * @date 2020-03-07 16:18:56 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreGoodsLabelServiceImpl extends ServiceImpl implements StoreGoodsLabelService { + + private final StoreGoodsLabelMapper storeGoodsLabelMapper; + + @Override + public List listByStoreId(String storeId) { + //TODO 从缓存获取店铺商品分类列表 + List list = list(storeId); + List storeGoodsLabelVOList = new ArrayList<>(); + //循环列表判断是否为顶级,如果为顶级获取下级数据 + list.stream() + .filter(storeGoodsLabel -> storeGoodsLabel.getLevel() == 0) + .forEach(storeGoodsLabel -> { + StoreGoodsLabelVO storeGoodsLabelVO = new StoreGoodsLabelVO(storeGoodsLabel.getId(), storeGoodsLabel.getLabelName(), storeGoodsLabel.getLevel(), storeGoodsLabel.getSortOrder()); + List storeGoodsLabelVOChildList = new ArrayList<>(); + list.stream() + .filter(label -> label.getParentId().equals(storeGoodsLabel.getId())) + .forEach(storeGoodsLabelChild -> storeGoodsLabelVOChildList.add(new StoreGoodsLabelVO(storeGoodsLabelChild.getId(), storeGoodsLabelChild.getLabelName(), storeGoodsLabelChild.getLevel(), storeGoodsLabelChild.getSortOrder()))); + storeGoodsLabelVO.setChildren(storeGoodsLabelVOChildList); + storeGoodsLabelVOList.add(storeGoodsLabelVO); + }); + return storeGoodsLabelVOList; + } + + @Override + public List listByStore() { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + //返回列表 + return list(tokenUser.getId()); + } + + /** + * 获取店铺商品分类列表 + * + * @param storeId 店铺ID + * @return 店铺商品分类列表 + */ + private List list(String storeId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(StoreGoodsLabel::getStoreId, storeId); + return storeGoodsLabelMapper.selectList(queryWrapper); + } + + @Override + public StoreGoodsLabel addStoreGoodsLabel(StoreGoodsLabel storeGoodsLabel) { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + storeGoodsLabel.setStoreId(tokenUser.getStoreId()); + storeGoodsLabelMapper.insert(storeGoodsLabel); + return storeGoodsLabel; + } + + @Override + public StoreGoodsLabel editStoreGoodsLabel(StoreGoodsLabel storeGoodsLabel) { + //修改当前店铺的商品分类 + AuthUser tokenUser = UserContext.getCurrentUser(); + LambdaUpdateWrapper lambdaUpdateWrapper = Wrappers.lambdaUpdate(); + lambdaUpdateWrapper.eq(StoreGoodsLabel::getStoreId, tokenUser.getStoreId()); + lambdaUpdateWrapper.eq(StoreGoodsLabel::getId, storeGoodsLabel.getId()); + storeGoodsLabelMapper.update(storeGoodsLabel, lambdaUpdateWrapper); + return storeGoodsLabel; + } + + @Override + public void removeStoreGoodsLabel(String storeLabelId) { + storeGoodsLabelMapper.deleteById(storeLabelId); + } +} diff --git a/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreServiceImpl.java b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreServiceImpl.java new file mode 100644 index 00000000..2583ee8b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/store/serviceimpl/StoreServiceImpl.java @@ -0,0 +1,336 @@ +package cn.lili.modules.store.serviceimpl; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.service.GoodsService; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dos.StoreCollection; +import cn.lili.modules.member.service.MemberService; +import cn.lili.modules.member.service.StoreCollectionService; +import cn.lili.modules.page.service.PageDataService; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.entity.dos.StoreDetail; +import cn.lili.modules.store.entity.dto.*; +import cn.lili.modules.store.entity.enums.StoreStatusEnum; +import cn.lili.modules.store.entity.vos.StoreSearchParams; +import cn.lili.modules.store.entity.vos.StoreVO; +import cn.lili.modules.store.mapper.StoreMapper; +import cn.lili.modules.store.service.StoreDetailService; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +/** + * 店铺业务层实现 + * + * @author pikachu + * @date 2020-03-07 16:18:56 + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreServiceImpl extends ServiceImpl implements StoreService { + + //店铺 + private final StoreMapper storeMapper; + //会员 + private MemberService memberService; + //商品 + private GoodsService goodsService; + //商品SKU + private GoodsSkuService goodsSkuService; + //店铺详情 + private StoreDetailService storeDetailService; + //页面 + private PageDataService pageDataService; + //店铺收藏 + private final StoreCollectionService storeCollectionService; + + @Override + public IPage findByConditionPage(StoreSearchParams storeSearchParams, PageVO page) { + return storeMapper.getStoreList(PageUtil.initPage(page), storeSearchParams.queryWrapper()); + } + + @Override + public StoreVO getStoreDetail() { + StoreVO storeVO = storeMapper.getStoreDetail(UserContext.getCurrentUser().getStoreId()); + storeVO.setNickName(UserContext.getCurrentUser().getNickName()); + return storeVO; + } + + @Override + public Store add(AdminStoreApplyDTO adminStoreApplyDTO) { + + //判断店铺名称是否存在 + QueryWrapper queryWrapper = Wrappers.query(); + queryWrapper.eq("store_name", adminStoreApplyDTO.getStoreName()); + if (this.getOne(queryWrapper) != null) { + throw new ServiceException(ResultCode.STORE_NAME_EXIST_ERROR); + } + + Member member = memberService.getById(adminStoreApplyDTO.getMemberId()); + //判断用户是否存在 + if (member == null) { + throw new ServiceException(ResultCode.USER_NOT_EXIST); + } + //判断是否拥有店铺 + if (SwitchEnum.OPEN.name().equals(member.getHaveStore())) { + throw new ServiceException(ResultCode.STORE_APPLY_DOUBLE_ERROR); + } + + //添加店铺 + Store store=new Store(member,adminStoreApplyDTO); + this.save(store); + + //判断是否存在店铺详情,如果没有则进行新建,如果存在则进行修改 + StoreDetail storeDetail = new StoreDetail(store,adminStoreApplyDTO); + + storeDetailService.save(storeDetail); + + //设置会员-店铺信息 + memberService.update(new LambdaUpdateWrapper() + .eq(Member::getId,member.getId()) + .set(Member::getHaveStore,SwitchEnum.OPEN.name()) + .set(Member::getStoreId,store.getId())); + return store; + + } + + @Override + public Store edit(StoreEditDTO storeEditDTO) { + if (storeEditDTO != null) { + //判断店铺名是否唯一 + Store storeTmp = getOne(new QueryWrapper().eq("store_name", storeEditDTO.getStoreName())); + if (storeTmp != null && !StringUtils.equals(storeTmp.getId(), storeEditDTO.getStoreId())) { + throw new ServiceException(ResultCode.STORE_NAME_EXIST_ERROR); + } + //修改店铺详细信息 + updateStoreDetail(storeEditDTO); + //修改店铺信息 + return updateStore(storeEditDTO); + } else { + throw new ServiceException(ResultCode.STORE_NOT_EXIST); + } + } + + /** + * 修改店铺基本信息 + * + * @param storeEditDTO 修改店铺信息 + */ + private Store updateStore(StoreEditDTO storeEditDTO) { + Store store = this.getById(storeEditDTO.getStoreId()); + if (store != null) { + BeanUtil.copyProperties(storeEditDTO, store); + store.setId(storeEditDTO.getStoreId()); + this.updateById(store); + } + return store; + } + + /** + * 修改店铺详细细腻 + * + * @param storeEditDTO 修改店铺信息 + */ + private void updateStoreDetail(StoreEditDTO storeEditDTO) { + StoreDetail storeDetail = new StoreDetail(); + BeanUtil.copyProperties(storeEditDTO, storeDetail); + storeDetailService.update(storeDetail, new QueryWrapper().eq("store_id", storeEditDTO.getStoreId())); + } + + @Override + public boolean audit(String id, Integer passed) { + Store store = storeMapper.selectById(id); + if (store == null) { + throw new ServiceException(ResultCode.STORE_NOT_EXIST); + } + if (passed == 0) { + store.setStoreDisable(StoreStatusEnum.OPEN.value()); + //添加店铺页面 + pageDataService.addStorePageData(store.getId()); + } else { + store.setStoreDisable(StoreStatusEnum.REFUSED.value()); + } + + return this.updateById(store); + } + + @Override + public boolean disable(String id) { + Store store = this.getById(id); + if (store != null) { + store.setStoreDisable(StoreStatusEnum.CLOSED.value()); + + //下架所有此店铺商品 + goodsService.underStoreGoods(id); + return this.updateById(store); + } + throw new ServiceException(ResultCode.STORE_NOT_EXIST); + } + + @Override + public boolean enable(String id) { + Store store = this.getById(id); + if (store != null) { + store.setStoreDisable(StoreStatusEnum.OPEN.value()); + return this.updateById(store); + } + throw new ServiceException(ResultCode.STORE_NOT_EXIST); + } + + @Override + public boolean applyFirstStep(StoreCompanyDTO storeCompanyDTO) { + //获取当前操作的店铺 + Store store = getStoreByMember(); + //如果没有申请过店铺,新增店铺 + if (!Optional.ofNullable(store).isPresent()) { + Member member = memberService.getById(UserContext.getCurrentUser().getId()); + store = new Store(member); + this.save(store); + StoreDetail storeDetail = new StoreDetail(); + storeDetail.setStoreId(store.getId()); + BeanUtil.copyProperties(storeCompanyDTO, storeDetail); + return storeDetailService.save(storeDetail); + } + //判断是否存在店铺详情,如果没有则进行新建,如果存在则进行修改 + StoreDetail storeDetail = storeDetailService.getStoreDetail(store.getId()); + BeanUtil.copyProperties(storeCompanyDTO, storeDetail); + return storeDetailService.updateById(storeDetail); + } + + @Override + public boolean applySecondStep(StoreBankDTO storeBankDTO) { + + //获取当前操作的店铺 + Store store = getStoreByMember(); + StoreDetail storeDetail = storeDetailService.getStoreDetail(store.getId()); + //设置店铺的银行信息 + BeanUtil.copyProperties(storeBankDTO, storeDetail); + return storeDetailService.updateById(storeDetail); + } + + @Override + public boolean applyThirdStep(StoreOtherInfoDTO storeOtherInfoDTO) { + //获取当前操作的店铺 + Store store = getStoreByMember(); + StoreDetail storeDetail = storeDetailService.getStoreDetail(store.getId()); + //设置店铺的其他信息 + BeanUtil.copyProperties(storeOtherInfoDTO, storeDetail); + //设置店铺经营范围 + storeDetail.setGoodsManagementCategory(storeOtherInfoDTO.getGoodsManagementCategory()); + //最后一步申请,给予店铺设置库存预警默认值 + storeDetail.setStockWarning(10); + //修改店铺详细信息 + storeDetailService.updateById(storeDetail); + //设置店铺名称,修改店铺信息 + store.setStoreName(storeOtherInfoDTO.getStoreName()); + store.setStoreDisable(StoreStatusEnum.APPLYING.name()); + store.setStoreCenter(storeOtherInfoDTO.getStoreCenter()); + store.setStoreDesc(storeOtherInfoDTO.getStoreDesc()); + store.setStoreLogo(storeOtherInfoDTO.getStoreLogo()); + return this.updateById(store); + } + + @Override + public Integer auditNum() { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(Store::getStoreDisable, StoreStatusEnum.APPLYING.name()); + return this.count(queryWrapper); + } + + @Override + public Integer storeNum() { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(Store::getStoreDisable, StoreStatusEnum.OPEN.name()); + return this.count(queryWrapper); + } + + @Override + public Integer todayStoreNum() { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.eq(Store::getStoreDisable, StoreStatusEnum.OPEN.name()); + queryWrapper.gt(Store::getCreateTime, DateUtil.beginOfDay(new DateTime())); + return this.count(queryWrapper); + } + + @Override + public void updateStoreGoodsNum(String storeId) { + //获取店铺已上架已审核通过商品数量 + Integer goodsNum=goodsService.count(new LambdaQueryWrapper() + .eq(Goods::getStoreId,storeId) + .eq(Goods::getIsAuth, GoodsAuthEnum.PASS.name()) + .eq(Goods::getMarketEnable, GoodsStatusEnum.UPPER.name())); + //修改店铺商品数量 + this.update(new LambdaUpdateWrapper() + .set(Store::getGoodsNum,goodsNum) + .eq(Store::getId,storeId)); + } + + @Override + public void updateStoreCollectionNum(String goodsId) { + String storeId=goodsSkuService.getById(goodsId).getStoreId(); + //获取店铺收藏数量 + Integer collectionNum=storeCollectionService.count(new LambdaQueryWrapper() + .eq(StoreCollection::getStoreId,storeId)); + //修改店铺收藏数量 + this.update(new LambdaUpdateWrapper() + .set(Store::getCollectionNum,collectionNum) + .eq(Store::getId,storeId)); + } + + /** + * 获取当前登录操作的店铺 + * + * @return + */ + private Store getStoreByMember() { + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(Store::getMemberId, UserContext.getCurrentUser().getId()); + return this.getOne(lambdaQueryWrapper); + } + + @Autowired + public void setGoodsService(GoodsService goodsService) { + this.goodsService = goodsService; + } + @Autowired + public void setGoodsSkuService(GoodsSkuService goodsSkuService) { + this.goodsSkuService = goodsSkuService; + } + @Autowired + public void setPageDataService(PageDataService pageDataService) { + this.pageDataService = pageDataService; + } + @Autowired + public void setMemberService(MemberService memberService) { + this.memberService = memberService; + } + @Autowired + public void setStoreDetailService(StoreDetailService storeDetailService) { + this.storeDetailService = storeDetailService; + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dos/AppVersionDO.java b/framework/src/main/java/cn/lili/modules/system/entity/dos/AppVersionDO.java new file mode 100755 index 00000000..850ef3b5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dos/AppVersionDO.java @@ -0,0 +1,83 @@ +package cn.lili.modules.system.entity.dos; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + + +/** + * app历史版本维护 + * + * @author zh + * @date 2020-06-20 09:29:19 + */ +@Data +@Entity +@Table(name = "li_app_version") +@TableName("li_app_version") +@ApiModel(value = "app版本控制") +public class AppVersionDO{ + + private static final long serialVersionUID = 3034686331756935L; + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + + @CreatedBy + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建者", hidden = true) + private String createBy; + + @ApiModelProperty(value = "版本号") + private String version; + + @ApiModelProperty(value = "版本名称") + private String versionName; + + + @ApiModelProperty(value = "更新内容") + private String content; + + @ApiModelProperty(value = "是否强制更新") + private Boolean forceUpdate; + + @ApiModelProperty(value = "下载地址") + private String downloadUrl; + + /** + * @see cn.lili.modules.system.entity.enums.AppType + */ + @ApiModelProperty(value = "类型") + private String type; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value = "版本更新时间") + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date versionUpdateDate; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dos/InstantDelivery.java b/framework/src/main/java/cn/lili/modules/system/entity/dos/InstantDelivery.java new file mode 100644 index 00000000..324aaf6d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dos/InstantDelivery.java @@ -0,0 +1,73 @@ +package cn.lili.modules.system.entity.dos; + + +import cn.lili.base.BaseEntity; +import cn.lili.modules.system.entity.vo.InstantDeliveryVO; +import com.baomidou.mybatisplus.annotation.TableName; +import com.google.gson.Gson; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 即时配送 + * + * @author pikachu + * @date 2020/12/01 15:58 + */ +@Data +@Entity +@Table(name = "li_instant_delivery") +@TableName("li_instant_delivery") +@ApiModel(value = "即时配送") +@AllArgsConstructor +@NoArgsConstructor +public class InstantDelivery extends BaseEntity { + + + /** + * 即时配送名称 + */ + @ApiModelProperty(value = "即时配送名称") + private String deliveryName; + /** + * 是否开启即时配送,1开启,0未开启 + */ + @ApiModelProperty(value = "是否开启即时配送,1开启,0未开启") + private Integer deliveryOpen; + /** + * 即时配送配置 + */ + @ApiModelProperty(value = "即时配送配置") + @Column(columnDefinition = "TEXT") + private String deliveryConfig; + /** + * 即时配送bean + */ + @ApiModelProperty(value = "即时配送bean") + private String deliveryBean; + + @ApiModelProperty(value = "封面图片") + private String images; + + + /** + * 根据vo参数构建do + * + * @param instantDeliveryVO + */ + public InstantDelivery(InstantDeliveryVO instantDeliveryVO) { + this.setDeliveryName(instantDeliveryVO.getDeliveryName()); + this.setDeliveryOpen(instantDeliveryVO.getDeliveryOpen()); + this.setDeliveryBean(instantDeliveryVO.getDeliveryBean()); + Gson gson = new Gson(); + this.setDeliveryConfig(gson.toJson(instantDeliveryVO.getConfigItems())); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dos/InstantDeliveryLog.java b/framework/src/main/java/cn/lili/modules/system/entity/dos/InstantDeliveryLog.java new file mode 100644 index 00000000..0eef125b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dos/InstantDeliveryLog.java @@ -0,0 +1,111 @@ +package cn.lili.modules.system.entity.dos; + + +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.system.entity.plugin.InstantDelivery.dada.enums.DadaOrderStatusEnum; +import cn.lili.modules.system.entity.plugin.InstantDelivery.dada.vo.DdOrderBackVO; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; + +/** + * 即时配送日志 + * + * @author pikachu + * @date 2020/12/01 15:58 + */ + +@Data +@Entity +@Table(name = "li_instant_delivery_log") +@TableName("li_instant_delivery_log") +@ApiModel(value = "即时配送日志") +@AllArgsConstructor +@NoArgsConstructor +public class InstantDeliveryLog { + + @Id + @TableId + @TableField + @Column(columnDefinition = "bigint(20)") + @ApiModelProperty(value = "唯一标识", hidden = true) + private String id; + + @CreatedDate + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @TableField(fill = FieldFill.INSERT) + @ApiModelProperty(value = "创建时间", hidden = true) + private Date createTime; + /** + * 即时配送订单号 + */ + @ApiModelProperty(value = "即时配送订单号") + private String deliveryOrderSn; + /** + * 商城订单号 + */ + @ApiModelProperty(value = "商城订单号") + private String orderSn; + /** + * 即时配送订单状态 + * + * @see DadaOrderStatusEnum + */ + @ApiModelProperty(value = "即时配送订单状态") + private String deliveryOrderStatus; + /** + * 配送员ID + */ + @ApiModelProperty(value = "即时配送骑手编号") + private String distributorNumber; + /** + * 配送员姓名,接单以后会存储 + */ + @ApiModelProperty(value = "配送员姓名,接单以后会存储") + private String distributorName; + /** + * 配送员手机号,接单以后存储 + */ + @ApiModelProperty(value = "配送员手机号,接单以后存储") + private String distributorMobile; + + /** + * 订单取消原因,其他状态下默认值为空字符串 + */ + @ApiModelProperty(value = "订单取消原因,其他状态下默认值为空字符串") + private String cancelReason; + + public InstantDeliveryLog(DdOrderBackVO ddOrderBackVO) { + this.setCancelReason(ddOrderBackVO.getCancelReason()); + this.setOrderSn(ddOrderBackVO.getOrderId()); + this.setDeliveryOrderSn(ddOrderBackVO.getClientId()); + if (!StringUtils.isEmpty(ddOrderBackVO.getDmMobile())) { + this.setDistributorMobile(ddOrderBackVO.getDmMobile()); + } + if (!StringUtils.isEmpty(ddOrderBackVO.getDmName())) { + this.setDistributorName(ddOrderBackVO.getDmName()); + } + if (ddOrderBackVO.getDmId() != null) { + this.setDistributorNumber(ddOrderBackVO.getDmId().toString()); + } + this.setDeliveryOrderStatus(DadaOrderStatusEnum.getText(100)); + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dos/Logistics.java b/framework/src/main/java/cn/lili/modules/system/entity/dos/Logistics.java new file mode 100644 index 00000000..bdb99e35 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dos/Logistics.java @@ -0,0 +1,44 @@ +package cn.lili.modules.system.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; + +/** + * 物流公司设置 + * + * @author Chopper + * @date 2020/11/17 8:01 下午 + */ +@Data +@Entity +@Table(name = "li_logistics") +@TableName("li_logistics") +@ApiModel(value = "物流公司") +public class Logistics extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @NotEmpty(message = "物流公司名称必填") + @ApiModelProperty(value = "物流公司名称") + private String name; + + @NotEmpty(message = "物流公司code必填") + @ApiModelProperty(value = "物流公司code") + private String code; + + @ApiModelProperty(value = "支持电子面单") + private String standBy; + + @ApiModelProperty(value = "物流公司电子面单表单") + private String formItems; + + @ApiModelProperty(value = "禁用状态 OPEN:开启,CLOSE:禁用") + private String disabled; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dos/Region.java b/framework/src/main/java/cn/lili/modules/system/entity/dos/Region.java new file mode 100644 index 00000000..2b52d99e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dos/Region.java @@ -0,0 +1,65 @@ +package cn.lili.modules.system.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 行政地区 + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_region") +@TableName("li_region") +@ApiModel(value = "行政地区") +public class Region extends BaseEntity { + + private static final long serialVersionUID = 418341656517240988L; + + @NotEmpty(message = "父id不能为空") + @ApiModelProperty(value = "父id") + private String parentId; + + @NotEmpty(message = "区域编码不能为空") + @ApiModelProperty(value = "区域编码") + private String adCode; + + @ApiModelProperty(value = "城市代码") + private String cityCode; + + @NotEmpty(message = "区域中心点经纬度不能为空") + @ApiModelProperty(value = "区域中心点经纬度") + private String center; + + @ApiModelProperty(value = + "行政区划级别" + + "country:国家" + + "province:省份(直辖市会在province和city显示)" + + "city:市(直辖市会在province和city显示)" + + "district:区县" + + "street:街道") + @NotEmpty(message = "品牌名称不能为空") + private String level; + + @NotEmpty(message = "名称不能为空") + @ApiModelProperty(value = "名称") + private String name; + + @NotEmpty(message = "行政地区路径不能为空") + @ApiModelProperty(value = "行政地区路径,类似:1,2,3 ") + private String path; + + @NotNull(message = "排序不能为空") + @ApiModelProperty(value = "排序") + private Integer orderNum; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dos/SensitiveWords.java b/framework/src/main/java/cn/lili/modules/system/entity/dos/SensitiveWords.java new file mode 100644 index 00000000..dca87da8 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dos/SensitiveWords.java @@ -0,0 +1,34 @@ +package cn.lili.modules.system.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; + +/** + * 敏感词实体 + * @author Bulbasaur + * 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_sensitive_words") +@TableName("li_sensitive_words") +@ApiModel(value = "敏感词") +public class SensitiveWords extends BaseEntity { + + /** + * 敏感词名称 + */ + @ApiModelProperty(value = "敏感词名称") + @NotEmpty(message = "敏感词必填") + @Size(min = 2, max = 20) + private String sensitiveWord; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dos/ServiceNotice.java b/framework/src/main/java/cn/lili/modules/system/entity/dos/ServiceNotice.java new file mode 100644 index 00000000..6bc02c3a --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dos/ServiceNotice.java @@ -0,0 +1,44 @@ +package cn.lili.modules.system.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 服务订阅消息 + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_service_notice") +@TableName("li_service_notice") +@ApiModel(value = "服务订阅消息") +public class ServiceNotice extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "商家id,为-1时,代表是平台发布的消息") + private String storeId; + + @ApiModelProperty(value = "banner图") + private String bannerImage; + + @ApiModelProperty(value = "标题") + private String title; + + @ApiModelProperty(value = "副标题") + private String subTitle; + + @ApiModelProperty(value = "点击跳转(此内容与站内信内容只能有一个生效)") + private String toUrl; + + @ApiModelProperty(value = "站内信内容(富文本框编辑,可以上传图片的html)") + private String content; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dos/Setting.java b/framework/src/main/java/cn/lili/modules/system/entity/dos/Setting.java new file mode 100644 index 00000000..d1d71421 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dos/Setting.java @@ -0,0 +1,31 @@ +package cn.lili.modules.system.entity.dos; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * 设置 + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@Data +@Entity +@Table(name = "li_setting") +@TableName("li_setting") +@ApiModel(value = "配置") +@NoArgsConstructor +public class Setting extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "配置值value") + private String settingValue; + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/BaseSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/BaseSetting.java new file mode 100644 index 00000000..8f2a5026 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/BaseSetting.java @@ -0,0 +1,39 @@ +package cn.lili.modules.system.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 基础设置 + * + * @author Chopper + * @date 2020/11/17 7:58 下午 + */ +@Data +public class BaseSetting implements Serializable { + + private static final long serialVersionUID = -3138023944444671722L; + + @ApiModelProperty(value = "站点名称") + private String siteName; + + @ApiModelProperty(value = "icp") + private String icp; + + @ApiModelProperty(value = "后端logo") + private String domainLogo; + + @ApiModelProperty(value = "买家端logo") + private String buyerSideLogo; + + @ApiModelProperty(value = "商家端logo") + private String storeSideLogo; + + @ApiModelProperty(value = "站点地址") + private String staticPageAddress; + + @ApiModelProperty(value = "wap站点地址") + private String staticPageWapAddress; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/DistributionSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/DistributionSetting.java new file mode 100644 index 00000000..289d1b84 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/DistributionSetting.java @@ -0,0 +1,31 @@ +package cn.lili.modules.system.entity.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 分销配置 + * + * @author Chopper + * @date 2020/12/23 18:35 + */ +@Data +public class DistributionSetting implements Serializable { + + private static final long serialVersionUID = 2099524659914361438L; + + /** + * 是否开启分销 + */ + private Boolean isOpen; + /** + * 分销关系绑定天数 + */ + private Integer distributionDay; + /** + * 分销结算天数 + */ + private Integer cashDay; + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/EmailSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/EmailSetting.java new file mode 100644 index 00000000..c7657318 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/EmailSetting.java @@ -0,0 +1,26 @@ +package cn.lili.modules.system.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 邮箱设置 + * + * @author Chopper + * @date 2020/11/26 15:58 + */ +@Data +public class EmailSetting implements Serializable { + + private static final long serialVersionUID = 7261037221941716140L; + @ApiModelProperty(value = "邮箱服务器") + private String host; + + @ApiModelProperty(value = "发送者邮箱账号") + private String username; + + @ApiModelProperty(value = "邮箱授权码") + private String password; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/GoodsSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/GoodsSetting.java new file mode 100644 index 00000000..ed6c26d4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/GoodsSetting.java @@ -0,0 +1,39 @@ +package cn.lili.modules.system.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 商品设置 + * + * @author Chopper + * @date 2020/11/17 7:58 下午 + */ +@Data +public class GoodsSetting implements Serializable { + + private static final long serialVersionUID = -4132785717179910025L; + @ApiModelProperty(value = "是否开启商品审核") + private Boolean goodsCheck; + + @ApiModelProperty(value = "小图宽") + private Integer smallPictureWidth; + + @ApiModelProperty(value = "小图高") + private Integer smallPictureHeight; + + @ApiModelProperty(value = "缩略图宽") + private Integer abbreviationPictureWidth; + + @ApiModelProperty(value = "缩略图高") + private Integer abbreviationPictureHeight; + + @ApiModelProperty(value = "原图宽") + private Integer originalPictureWidth; + + @ApiModelProperty(value = "原图高") + private Integer originalPictureHeight; + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/KuaidiSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/KuaidiSetting.java new file mode 100644 index 00000000..3b300b02 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/KuaidiSetting.java @@ -0,0 +1,28 @@ +package cn.lili.modules.system.entity.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 快递设置 + * + * @author Chopper + * @date 2020-03-10 10:04 上午 + */ +@Data +public class KuaidiSetting implements Serializable { + private static final long serialVersionUID = 3520379500723173689L; + /** + * 企业id + */ + private String ebusinessID; + /** + * 密钥 + */ + private String appKey; + /** + * api地址 + */ + private String reqURL; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/OrderSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/OrderSetting.java new file mode 100644 index 00000000..1d8a571f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/OrderSetting.java @@ -0,0 +1,41 @@ +package cn.lili.modules.system.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 订单设置 + * + * @author Chopper + * @date 2020/11/17 7:59 下午 + */ +@Data +public class OrderSetting implements Serializable { + + private static final long serialVersionUID = -2628613596000114786L; + @ApiModelProperty(value = "自动取消 分钟") + private Integer autoCancel; + + @ApiModelProperty(value = "自动收货 天") + private Integer autoReceive; + + //已完成订单允许退单:X天内,允许客户发起退货退款申请,未发货订单随时可退,未发货订单随时可退。 + @ApiModelProperty(value = "已完成订单允许退单 天") + private Integer autoComplete; + + @ApiModelProperty(value = "自动评价 天") + private Integer autoEvaluation; + + @ApiModelProperty(value = "售后自动取消 天") + private Integer autoCancelAfterSale; + + //待审核退单自动审核:X天后,商家逾期未处理的待审核退单,将会自动审核通过。 + @ApiModelProperty(value = "待审核退单自动审核 天") + private Integer autoAfterSaleReview; + + //退单自动确认收货:X天后,商家逾期未处理的待收货退单,将会自动确认收货,非快递退货的退单,再审核通过后开始计时。 + @ApiModelProperty(value = "已完成订单允许退单 天") + private Integer autoAfterSaleComplete; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/OssSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/OssSetting.java new file mode 100644 index 00000000..83bcf735 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/OssSetting.java @@ -0,0 +1,32 @@ +package cn.lili.modules.system.entity.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * OSS设置 + * + * @author Chopper + * @date 2020/11/26 15:50 + */ + +@Data +public class OssSetting implements Serializable { + + private static final long serialVersionUID = 2975271656230801861L; + //域名 + private String endPoint = "oss-cn-beijing.aliyuncs.com"; + + //储存空间 + private String bucketName = "lilishop-oss"; + + //存放路径路径 + private String picLocation = "/template"; + + //密钥id + private String accessKeyId = "LTAI4G4deX59EyjpEULaJdsU"; + + //密钥 + private String accessKeySecret = "BlRBpl7WBman6GYYwLKMiKqMTXFhWf"; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/PointSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/PointSetting.java new file mode 100644 index 00000000..0e5d29dd --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/PointSetting.java @@ -0,0 +1,36 @@ +package cn.lili.modules.system.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 积分设置 + * + * @author Chopper + * @date 2020/11/17 7:59 下午 + */ +@Data +public class PointSetting implements Serializable { + + private static final long serialVersionUID = -4261856614779031745L; + @ApiModelProperty(value = "注册") + private Integer register; + + @ApiModelProperty(value = "1积分等于多少元") + private Integer money; + + @ApiModelProperty(value = "每日签到积分") + private Integer signIn; + + @ApiModelProperty(value = "订单评价赠送积分") + private Integer comment; + + @ApiModelProperty(value = "积分具体设置") + private List pointSettingItems = new ArrayList<>(); + + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/PointSettingItem.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/PointSettingItem.java new file mode 100644 index 00000000..5759d6e4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/PointSettingItem.java @@ -0,0 +1,27 @@ +package cn.lili.modules.system.entity.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 积分签到设置 + * + * @author Chopper + * @date 2021-02-26 11:48 + */ +@Data +public class PointSettingItem implements Comparable { + + + @ApiModelProperty(value = "签到天数") + private Integer day; + + + @ApiModelProperty(value = "赠送积分") + private Integer point; + + @Override + public int compareTo(PointSettingItem pointSettingItem) { + return this.day - pointSettingItem.getDay(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/SmsSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/SmsSetting.java new file mode 100644 index 00000000..4238b747 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/SmsSetting.java @@ -0,0 +1,29 @@ +package cn.lili.modules.system.entity.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 短信配置 + * + * @author Chopper + * @date 2020/11/30 15:23 + */ +@Data +public class SmsSetting implements Serializable { + /** + * 从上到下yi依次是 + * 节点地址 + * key + * 密钥 + * 签名,这里在前台不做调整,方便客户直接把服务商的内容配置在我们平台 + */ + private String regionId; + + private String accessKeyId; + + private String accessSecret; + + private String signName; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/WithdrawalSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/WithdrawalSetting.java new file mode 100644 index 00000000..baffc079 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/WithdrawalSetting.java @@ -0,0 +1,21 @@ +package cn.lili.modules.system.entity.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 提现配置 + * + * @author pikachu + * @date 2020/11/30 15:23 + */ +@Data +public class WithdrawalSetting implements Serializable { + + private static final long serialVersionUID = -3872782530832122976L; + /** + * 提现是否需要申请 + */ + private Boolean apply; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/QQConnectSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/QQConnectSetting.java new file mode 100644 index 00000000..fb0ed185 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/QQConnectSetting.java @@ -0,0 +1,22 @@ +package cn.lili.modules.system.entity.dto.connect; + +import cn.lili.modules.system.entity.dto.connect.dto.QQConnectSettingItem; +import lombok.Data; + +import java.util.List; + +/** + * QQ联合登录设置 + * + * @author Chopper + * @date 2020/11/17 7:59 下午 + */ +@Data +public class QQConnectSetting { + + /** + * qq联合登陆配置 + */ + List qqConnectSettingItemList; + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/WechatConnectSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/WechatConnectSetting.java new file mode 100644 index 00000000..9d4ddb64 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/WechatConnectSetting.java @@ -0,0 +1,23 @@ +package cn.lili.modules.system.entity.dto.connect; + +import cn.lili.modules.system.entity.dto.connect.dto.WechatConnectSettingItem; +import lombok.Data; + +import java.util.List; + +/** + * 微信设置 + * + * @author Chopper + * @date 2020/11/17 8:00 下午 + */ +@Data +public class WechatConnectSetting { + + + /** + * 微信联合登陆配置 + */ + List wechatConnectSettingItems; + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/dto/QQConnectSettingItem.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/dto/QQConnectSettingItem.java new file mode 100644 index 00000000..7a9da594 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/dto/QQConnectSettingItem.java @@ -0,0 +1,22 @@ +package cn.lili.modules.system.entity.dto.connect.dto; + + +import lombok.Data; + +/** + * QQ联合登录具体配置 + * + * @author Chopper + * @date 2020/11/17 7:59 下午 + */ +@Data +public class QQConnectSettingItem { + + private String clientType; + + private String appId; + + private String appKey; + + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/dto/WechatConnectSettingItem.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/dto/WechatConnectSettingItem.java new file mode 100644 index 00000000..9fdbc17f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/connect/dto/WechatConnectSettingItem.java @@ -0,0 +1,23 @@ +package cn.lili.modules.system.entity.dto.connect.dto; + +import lombok.Data; + +/** + * 微信设置 + * + * @author Chopper + * @date 2020/11/17 8:00 下午 + */ +@Data +public class WechatConnectSettingItem { + + + /** + * @See ClientType + */ + private String clientType; + + private String appId; + + private String appSecret; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/AlipayPaymentSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/AlipayPaymentSetting.java new file mode 100644 index 00000000..087639b0 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/AlipayPaymentSetting.java @@ -0,0 +1,31 @@ +package cn.lili.modules.system.entity.dto.payment; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 支付宝支付设置 + * + * @author Chopper + * @date 2020-12-02 10:09 + */ +@Data +@Accessors(chain = true) +public class AlipayPaymentSetting { + + //应用id + private String appId; + + //私钥 + private String privateKey; + + //应用证书 + private String certPath; + + //支付宝公钥 + private String alipayPublicCertPath; + + //支付宝根证书 + private String rootCertPath; + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/PaymentSupportSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/PaymentSupportSetting.java new file mode 100644 index 00000000..e5a512ea --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/PaymentSupportSetting.java @@ -0,0 +1,48 @@ +package cn.lili.modules.system.entity.dto.payment; + +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import cn.lili.modules.system.entity.dto.payment.dto.PaymentSupportForm; +import cn.lili.modules.system.entity.dto.payment.dto.PaymentSupportItem; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.List; + +/** + * 支持的支付方式 + * + * @author Chopper + * @date 2021-01-26 15:52 + */ +@Data +@Accessors(chain = true) +public class PaymentSupportSetting { + + private List paymentSupportItems; + + + public PaymentSupportSetting() { + + } + + public PaymentSupportSetting(PaymentSupportForm paymentSupportForm) { + + List paymentSupportItems = new ArrayList<>(); + + for (ClientTypeEnum client : paymentSupportForm.getClients()) { + PaymentSupportItem paymentSupportItem = new PaymentSupportItem(); + + List supports = new ArrayList<>(); + for (PaymentMethodEnum payment : paymentSupportForm.getPayments()) { + supports.add(payment.name()); + } + paymentSupportItem.setClient(client.name()); + paymentSupportItem.setSupports(supports); + paymentSupportItems.add(paymentSupportItem); + + } + this.paymentSupportItems = paymentSupportItems; + } +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/WechatPaymentSetting.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/WechatPaymentSetting.java new file mode 100644 index 00000000..b80ceb5d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/WechatPaymentSetting.java @@ -0,0 +1,31 @@ +package cn.lili.modules.system.entity.dto.payment; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * 微信支付设置 + * + * @author Chopper + * @date 2020-12-02 10:08 + */ +@Data +@Accessors(chain = true) +public class WechatPaymentSetting { + + + //应用id + private String appId; + //商户号 + private String mchId; + //私钥 + private String apiclient_key; + //pem 证书 + private String apiclient_cert_pem; + //p12 证书 + private String apiclient_cert_p12; + //商户证书序列号 + private String serialNumber; + //apiv3私钥 + private String apiKey3; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/dto/PaymentSupportForm.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/dto/PaymentSupportForm.java new file mode 100644 index 00000000..fe28379b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/dto/PaymentSupportForm.java @@ -0,0 +1,50 @@ +package cn.lili.modules.system.entity.dto.payment.dto; + +import cn.lili.modules.base.entity.enums.ClientTypeEnum; +import cn.lili.modules.payment.kit.enums.PaymentMethodEnum; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.List; + +/** + * 支持的支付方式表单 + * + * @author Chopper + * @version v1.0 + * 2021-01-26 15:52 + */ +@Data +@Accessors(chain = true) +public class PaymentSupportForm { + + /** + * 客户端类型列表 + * + * @return 客户端类型 + */ + public List getClients() { + List keys = new ArrayList<>(); + for (ClientTypeEnum clientTypeEnum : ClientTypeEnum.values()) { + if (clientTypeEnum.equals(ClientTypeEnum.UNKNOWN)) continue; + keys.add(clientTypeEnum); + } + return keys; + } + + /** + * 支付方式列表 + * + * @return 即支持的支付方式集合 + */ + public List getPayments() { + + List keys = new ArrayList<>(); + for (PaymentMethodEnum paymentMethodEnum : PaymentMethodEnum.values()) { + if (paymentMethodEnum.equals(PaymentMethodEnum.BANK_TRANSFER)) continue; + keys.add(paymentMethodEnum); + } + return keys; + } +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/dto/PaymentSupportItem.java b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/dto/PaymentSupportItem.java new file mode 100644 index 00000000..fb524766 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/dto/payment/dto/PaymentSupportItem.java @@ -0,0 +1,26 @@ +package cn.lili.modules.system.entity.dto.payment.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 支持的支付方式属性 + * + * @author Chopper + * @version v1.0 + * 2021-02-27 11:21 + */ +@Data +public class PaymentSupportItem { + + + @ApiModelProperty(value = "客户端 h5/app/小程序/pc") + private String client; + + @ApiModelProperty(value = "支持的支付方式") + private List supports; + + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/enums/AppType.java b/framework/src/main/java/cn/lili/modules/system/entity/enums/AppType.java new file mode 100644 index 00000000..86da8abe --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/enums/AppType.java @@ -0,0 +1,37 @@ +package cn.lili.modules.system.entity.enums; + + +/** + * app类型 安卓 IOS + * + * @author Chopper + * @date 2020/9/11 17:03 + */ +public enum AppType { + + /** + * IOS + */ + IOS("IOS"), + /** + * 安卓 + */ + ANDROID("安卓"); + + private final String description; + + AppType(String description) { + this.description = description; + + } + + public String getDescription() { + return description; + } + + public String description() { + return this.description; + } + +} + diff --git a/framework/src/main/java/cn/lili/modules/system/entity/enums/InstantDeliveryUrl.java b/framework/src/main/java/cn/lili/modules/system/entity/enums/InstantDeliveryUrl.java new file mode 100644 index 00000000..dbd5e25b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/enums/InstantDeliveryUrl.java @@ -0,0 +1,63 @@ +package cn.lili.modules.system.entity.enums; + + +import lombok.Getter; +import lombok.Setter; + +/** + * 即时配送url接口地址 + * + * @author pikachu + * @date 2020/9/11 17:03 + */ +public enum InstantDeliveryUrl { + + /** + * 订单推送 + */ + DD_ADD_ORDER("/api/order/addOrder"), + /** + * 订单重发 + */ + DD_RE_ADD_ORDER("/api/order/reAddOrder"), + /** + * 订单妥投异常后,商家确认收货 + */ + DD_CONFIRM_ORDER("/api/order/confirm/goods"), + /** + * 店铺添加 + */ + DD_ADD_SHOP("/api/store/add"), + /** + * 店铺修改 + */ + DD_UPDATE_SHOP("/api/store/update"), + + /** + * 订单详细信息 + */ + DD_QUERY_ORDER("/api/order/status/query"), + + /** + * 订单取消 + */ + DD_CANDLE_ORDER("/api/order/formalCancel"), + + /** + * 城市code获取 + */ + DD_CITY_CODE("/api/cityCode/list"); + + /** + * 类型 + */ + @Getter + @Setter + private final String url; + + InstantDeliveryUrl(String url) { + this.url = url; + } + +} + diff --git a/framework/src/main/java/cn/lili/modules/system/entity/enums/SettingEnum.java b/framework/src/main/java/cn/lili/modules/system/entity/enums/SettingEnum.java new file mode 100644 index 00000000..bffbae87 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/enums/SettingEnum.java @@ -0,0 +1,42 @@ +package cn.lili.modules.system.entity.enums; + +/** + * 系统设置常量 + * + * @author Chopper + * @date 2020/9/11 17:03 + */ +public enum SettingEnum { + //基础配置 + BASE_SETTING, + //提现设置 + WITHDRAWAL_SETTING, + //分销设置 + DISTRIBUTION_SETTING, + //邮箱配置 + EMAIL_SETTING, + //商品设置 + GOODS_SETTING, + //快递鸟设置 + KUAIDI_SETTING, + //订单配置 + ORDER_SETTING, + //阿里OSS配置 + OSS_SETTING, + //阿里短信配置 + SMS_SETTING, + //积分设置 + POINT_SETTING, + + //微信 联合登陆设置 + WECHAT_CONNECT, + //QQ 浏览器 联合登录设置 + QQ_CONNECT, + + //各端支持支付设置 + PAYMENT_SUPPORT, + //支付宝支付设置 + ALIPAY_PAYMENT, + //微信支付设置 + WECHAT_PAYMENT, +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/plugin/ConfigItem.java b/framework/src/main/java/cn/lili/modules/system/entity/plugin/ConfigItem.java new file mode 100644 index 00000000..598035d7 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/plugin/ConfigItem.java @@ -0,0 +1,41 @@ +package cn.lili.modules.system.entity.plugin; + +import lombok.Data; +import lombok.ToString; + +import java.util.List; + +/** + * 插件配置类 + * + * @author pikachu + * @version v4.0 + * @Description: + * @since 2020/12/01 15:58 + */ +@Data +@ToString +public class ConfigItem { + /** + * 配置文件name值 + */ + private String name; + /** + * 配置文件name映射文本值 + */ + private String text; + /** + * 配置文件显示在浏览器时,input的type属性 + */ + private String type; + /** + * 配置的值 + */ + private Object value; + /** + * 如果是select 是需要将可选项传递到前台 + */ + private List options; + + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/InstantDeliveryPlugin.java b/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/InstantDeliveryPlugin.java new file mode 100644 index 00000000..6ab8c062 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/InstantDeliveryPlugin.java @@ -0,0 +1,119 @@ +package cn.lili.modules.system.entity.plugin.InstantDelivery; + +import cn.lili.modules.member.entity.dos.MemberAddress; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.store.entity.vos.StoreDetailVO; +import cn.lili.modules.system.entity.plugin.ConfigItem; +import cn.lili.modules.system.entity.vo.InstantDeliveryResultVO; + +import java.util.List; +import java.util.Map; + +/** + * 即时配送插件方法 + * + * @author pikachu + * @version v1.0 + * @Description: + * @since 2020/12/01 15:58 + */ +public interface InstantDeliveryPlugin { + /** + * 获取即时配送的插件ID + * + * @return + */ + String getPluginId(); + + /** + * 获取即时配送的插件名称 + * + * @return 插件名称 + */ + String getPluginName(); + + /** + * 即时配送是否开启 + * + * @return 0 不开启 1 开启 + */ + Integer getOpen(); + + /** + * 获取取消原因id + * + * @return + */ + Integer cancelReasonId(); + + /** + * 配置各个即时配送的参数 + * + * @return 在页面加载的即时配送参数 + */ + List getDefaultConfigItem(); + + /** + * 同城配送新建店铺 + * + * @param storeDetailVO 店铺信息 + * @param config 配送参数 + * @return + */ + InstantDeliveryResultVO addStore(StoreDetailVO storeDetailVO, Map config); + + /** + * 同城配送新建店铺 + * + * @param storeDetailVO 店铺信息 + * @param config 配送参数 + * @return + */ + InstantDeliveryResultVO editStore(StoreDetailVO storeDetailVO, Map config); + + /** + * 查询订单详细 + * + * @param orderSn 传递到达达的订单sn,目前使用的是商城订单sn传递的所有只需要传递商城sn就可以 + * @param config 配置参数 + * @return + */ + InstantDeliveryResultVO getOrderDetail(String orderSn, Map config); + + /** + * 妥投异常之物品返回完成 + * + * @param orderSn 传递到达达的订单sn,目前使用的是商城订单sn传递的所有只需要传递商城sn就可以 + * @param config 配置参数 + * @return + */ + InstantDeliveryResultVO orderConfirm(String orderSn, Map config); + + /** + * 妥投异常之物品返回完成 + * + * @param orderSn 传递到达达的订单sn,目前使用的是商城订单sn传递的所有只需要传递商城sn就可以 + * @param cancelReason 取消原因 + * @param config 配置参数 + * @return + */ + InstantDeliveryResultVO orderCandle(String orderSn, String cancelReason, Map config); + + /** + * 发送同城配送订单 + * + * @param order 订单 + * @param memberAddress 会员地址 + * @param type 类型 + * @param config 配置 + * @return + */ + InstantDeliveryResultVO sendReOrder(Order order, StoreDetailVO storeDetailVO, MemberAddress memberAddress, Integer type, Map config); + + /** + * 即时配送回调 + * + * @param object 因为不同配送的返回对象不同,所以需要obj 去传递参数,在不同的插件中转换 + */ + void callBack(Object object); +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/dada/DadaPlugin.java b/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/dada/DadaPlugin.java new file mode 100644 index 00000000..9d1a913d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/dada/DadaPlugin.java @@ -0,0 +1,359 @@ +package cn.lili.modules.system.entity.plugin.InstantDelivery.dada; + +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.lili.common.utils.DateUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.member.entity.dos.MemberAddress; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.store.entity.enums.StoreStatusEnum; +import cn.lili.modules.store.entity.vos.StoreDetailVO; +import cn.lili.modules.store.service.StoreDetailService; +import cn.lili.modules.system.entity.dos.InstantDeliveryLog; +import cn.lili.modules.system.entity.enums.InstantDeliveryUrl; +import cn.lili.modules.system.entity.plugin.ConfigItem; +import cn.lili.modules.system.entity.plugin.InstantDelivery.InstantDeliveryPlugin; +import cn.lili.modules.system.entity.plugin.InstantDelivery.dada.vo.DdOrderBackVO; +import cn.lili.modules.system.entity.vo.CityResult; +import cn.lili.modules.system.entity.vo.InstantDeliveryResultVO; +import cn.lili.modules.system.service.InstantDeliveryLogService; +import cn.lili.modules.system.utils.HttpUtils; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 达达同城配送 + * + * @author pikachu + * @version v4.0 + * @Description: + * @since 2020/12/01 15:58 + */ +@Component("ddPlugin") +public class DadaPlugin implements InstantDeliveryPlugin { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private StoreDetailService storeDetailService; + @Autowired + private InstantDeliveryLogService instantDeliveryLogService; + + @Override + public String getPluginId() { + return "ddPlugin"; + } + + @Override + public String getPluginName() { + return "达达"; + } + + @Override + public Integer getOpen() { + return 0; + } + + @Override + public List getDefaultConfigItem() { + List list = new ArrayList(); + + ConfigItem url = new ConfigItem(); + url.setType("text"); + url.setName("url"); + url.setText("调用地址"); + + + ConfigItem appKey = new ConfigItem(); + appKey.setType("text"); + appKey.setName("app_key"); + appKey.setText("app_key"); + + ConfigItem appSecret = new ConfigItem(); + appSecret.setType("text"); + appSecret.setName("app_secret"); + appSecret.setText("app_secret"); + + ConfigItem merchantsId = new ConfigItem(); + merchantsId.setType("text"); + merchantsId.setName("merchants_id"); + merchantsId.setText("商户Id"); + list.add(url); + list.add(appKey); + list.add(appSecret); + list.add(merchantsId); + return list; + } + + @Override + public InstantDeliveryResultVO addStore(StoreDetailVO storeDetailVO, Map config) { + JSONArray jsonArray = new JSONArray(); + JSONObject jsonObject = new JSONObject(); +// //门店名称 +// jsonObject.put("station_name", storeDetailVO.getStoreName()); + //业务类型(食品小吃-1,饮料-2,鲜花-3,文印票务-8,便利店-9,水果生鲜-13,同城电商-19, 医药-20,蛋糕-21,酒品-24,小商品市场-25,服装-26,汽修零配-27,数码-28,小龙虾-29,火锅-51,其他-5) + jsonObject.set("business", 19); + //城市名称(如,上海) +// jsonObject.put("city_name", storeDetailVO.getCompanyCity()); + //区域名称(如,浦东新区) +// jsonObject.put("area_name", storeDetailVO.getCompanyCounty()); + //门店地址 + jsonObject.set("station_address", storeDetailVO.getCompanyAddress()); +// //门店经度 +// jsonObject.put("lng", storeDetailVO.getStoreLongitude()); +// //门店纬度 +// jsonObject.put("lat", storeDetailVO.getStoreLatitude()); + //联系人姓名 + jsonObject.set("contact_name", storeDetailVO.getLinkName()); + //联系人电话 + jsonObject.set("phone", storeDetailVO.getLinkPhone()); + //达达商家app账号 + jsonObject.set("username", storeDetailVO.getLinkPhone()); + //达达商家app密码 + jsonObject.set("password", "a" + storeDetailVO.getLinkPhone()); + jsonArray.add(jsonObject); + //发送请求的url + String url = StringUtils.toString(config.get("url")); + Map requestJson = getConfig(config, jsonArray.toString()); + String result = HttpUtils.doPostWithJson(url + InstantDeliveryUrl.DD_ADD_SHOP.getUrl(), requestJson); + //组织返回参数 + InstantDeliveryResultVO instantDeliveryResultVO = JSONUtil.toBean(result, InstantDeliveryResultVO.class); + Map map = (Map) instantDeliveryResultVO.getResult(); + List> successList = map.get("successList"); + if (successList.size() > 0) { + for (Map obj : successList) { + //如果成功调用店铺修改 将门店编码写入数据库 + UpdateWrapper updateWrapper = new UpdateWrapper(); + updateWrapper.in("id", storeDetailVO.getStoreId()); + updateWrapper.set("dd_code", obj.get("originStoreId").toString()); + storeDetailService.update(updateWrapper); + } + } + List> errorList = map.get("failedList"); + if (errorList != null && errorList.size() > 0) { + for (Map obj : errorList) { + throw new RuntimeException(obj.get("msg").toString()); + } + } + return instantDeliveryResultVO; + } + + @Override + public InstantDeliveryResultVO editStore(StoreDetailVO storeDetailVO, Map config) { + //如果达达code没有则不进行修改 + if (StringUtils.isEmpty(storeDetailVO.getDdCode())) { + return null; + } + JSONObject jsonObject = new JSONObject(); + //门店编号 + jsonObject.set("origin_store_id", storeDetailVO.getDdCode()); + //门店名称 +// jsonObject.put("station_name", storeDetailVO.getStoreName()); + //业务类型(食品小吃-1,饮料-2,鲜花-3,文印票务-8,便利店-9,水果生鲜-13,同城电商-19, 医药-20,蛋糕-21,酒品-24,小商品市场-25,服装-26,汽修零配-27,数码-28,小龙虾-29,火锅-51,其他-5) + jsonObject.set("business", 19); + //城市名称(如,上海) +// jsonObject.put("city_name", storeDetailVO.getCompanyCity()); + //区域名称(如,浦东新区) +// jsonObject.put("area_name", storeDetailVO.getCompanyCounty()); + //门店地址 + jsonObject.set("station_address", storeDetailVO.getCompanyAddress()); +// //门店经度 +// jsonObject.put("lng", storeDetailVO.getStoreLongitude()); +// //门店纬度 +// jsonObject.put("lat", storeDetailVO.getStoreLatitude()); + //联系人姓名 + jsonObject.set("contact_name", storeDetailVO.getLinkName()); + //联系人电话 + jsonObject.set("phone", storeDetailVO.getLinkPhone()); + if (storeDetailVO.getStoreDisable().equals(StoreStatusEnum.OPEN.value())) { + jsonObject.set("status", 1); + } else { + jsonObject.set("status", 0); + } + //发送请求的url + String url = StringUtils.toString(config.get("url")); + Map requestJson = getConfig(config, jsonObject.toString()); + String result = HttpUtils.doPostWithJson(url + InstantDeliveryUrl.DD_UPDATE_SHOP.getUrl(), requestJson); + //组织返回参数 + InstantDeliveryResultVO instantDeliveryResultVO = JSONUtil.toBean(result, InstantDeliveryResultVO.class); + if (instantDeliveryResultVO.getStatus().equals("fail")) { + logger.error("达达店铺信息修改失败," + instantDeliveryResultVO.getMsg()); + } + return instantDeliveryResultVO; + } + + @Override + public InstantDeliveryResultVO getOrderDetail(String orderSn, Map config) { + JSONObject jsonObject = new JSONObject(); + jsonObject.set("order_id", orderSn); + //发送请求的url + String url = StringUtils.toString(config.get("url")); + Map requestJson = getConfig(config, jsonObject.toString()); + String result = HttpUtils.doPostWithJson(url + InstantDeliveryUrl.DD_QUERY_ORDER.getUrl(), requestJson); + //组织返回参数 + InstantDeliveryResultVO instantDeliveryResultVO = JSONUtil.toBean(result, InstantDeliveryResultVO.class); + return instantDeliveryResultVO; + } + + @Override + public InstantDeliveryResultVO orderConfirm(String orderSn, Map config) { + JSONObject jsonObject = new JSONObject(); + jsonObject.set("order_id", orderSn); + //发送请求的url + String url = StringUtils.toString(config.get("url")); + Map requstJson = getConfig(config, jsonObject.toString()); + String result = HttpUtils.doPostWithJson(url + InstantDeliveryUrl.DD_CONFIRM_ORDER.getUrl(), requstJson); + //组织返回参数 + InstantDeliveryResultVO instantDeliveryResultVO = JSONUtil.toBean(result, InstantDeliveryResultVO.class); + return instantDeliveryResultVO; + } + + @Override + public InstantDeliveryResultVO orderCandle(String orderSn, String cancelReason, Map config) { + JSONObject jsonObject = new JSONObject(); + jsonObject.set("order_id", orderSn); + jsonObject.set("cancel_reason_id", this.cancelReasonId()); + jsonObject.set("cancel_reason", cancelReason); + //发送请求的url + String url = StringUtils.toString(config.get("url")); + Map requstJson = getConfig(config, jsonObject.toString()); + String result = HttpUtils.doPostWithJson(url + InstantDeliveryUrl.DD_CANDLE_ORDER.getUrl(), requstJson); + //组织返回参数 + InstantDeliveryResultVO instantDeliveryResultVO = JSONUtil.toBean(result, InstantDeliveryResultVO.class); + return instantDeliveryResultVO; + } + + @Override + public InstantDeliveryResultVO sendReOrder(Order order, StoreDetailVO storeDetailVO, MemberAddress memberAddress, Integer type, Map config) { + JSONObject jsonObject = new JSONObject(); + //门店编号,门店创建后可在门店列表和单页查看 + jsonObject.set("store_no", storeDetailVO.getDdCode()); + //第三方订单ID + jsonObject.set("origin_id", order.getSn()); + //订单所在城市的code http://newopen.imdada.cn/#/development/file/cityList?_k=l76a7s + String city = memberAddress.getConsigneeAddressPath().substring(memberAddress.getConsigneeAddressPath().indexOf(",") + 1, memberAddress.getConsigneeAddressPath().indexOf(",", memberAddress.getConsigneeAddressPath().indexOf(",") + 1)); + jsonObject.set("city_code", this.getCityCode(city, config)); + //订单金额 + jsonObject.set("cargo_price", order.getFlowPrice()); + //是否需要垫付 1:是 0:否 (垫付订单金额,非运费) + jsonObject.set("is_prepay", 0); + //收货人姓名 + jsonObject.set("receiver_name", memberAddress.getName()); + //收货人地址 + jsonObject.set("receiver_address", memberAddress.getDetail()); + //收货人地址纬度(高德坐标系,若是其他地图经纬度需要转化成高德地图经纬度 + jsonObject.set("receiver_lat", memberAddress.getLat()); + //收货人地址经度(高德坐标系,若是其他地图经纬度需要转化成高德地图经纬度 + jsonObject.set("receiver_lng", memberAddress.getLat()); + //回调URL + //jsonObject.put("callback", domainHelper.getCallback() + "/trade/delivery/order/call-back"); + //收货人手机号(手机号和座机号必填一项) + jsonObject.set("receiver_phone", memberAddress.getMobile()); + //是否使用保价费(0:不使用保价,1:使用保价; 同时,请确保填写了订单金额(cargo_price)) + jsonObject.set("is_use_insurance", 0); + //订单重量(单位:Kg) + jsonObject.set("cargo_weight", order.getWeight()); + Map requstJson = getConfig(config, jsonObject.toString()); + //发送请求的url + String url = StringUtils.toString(config.get("url")); + String result = null; + if (type == 0) { + result = HttpUtils.doPostWithJson(url + InstantDeliveryUrl.DD_ADD_ORDER.getUrl(), requstJson); + } else { + result = HttpUtils.doPostWithJson(url + InstantDeliveryUrl.DD_RE_ADD_ORDER.getUrl(), requstJson); + } + InstantDeliveryResultVO instantDeliveryResultVO = JSONUtil.toBean(result, InstantDeliveryResultVO.class); + if (instantDeliveryResultVO.getStatus().equals("fail")) { + logger.error("达达订单发送失败,订单号为" + order.getSn() + "," + instantDeliveryResultVO.getMsg()); + //如果发送失败择等待一秒重新发送,如果失败择记录日志 + try { + Thread.sleep(1000); + } catch (Exception e) { + e.printStackTrace(); + } + result = HttpUtils.doPostWithJson(url + InstantDeliveryUrl.DD_RE_ADD_ORDER.getUrl(), requstJson); + InstantDeliveryResultVO instantDeliveryResResultVO = JSONUtil.toBean(result, InstantDeliveryResultVO.class); + if (instantDeliveryResResultVO.getStatus().equals("fail")) { + logger.error("达达订单重试发送失败,订单号为" + order.getSn() + "," + instantDeliveryResultVO.getMsg()); + } + } + return instantDeliveryResultVO; + } + + @Override + public Integer cancelReasonId() { + return 4; + } + + /** + * 达达配送统一参数整合 + * + * @param config + * @param json + * @return + */ + private Map getConfig(Map config, String json) { + //组织参数 + String appKey = StringUtils.toString(config.get("app_key")); + String appSecret = StringUtils.toString(config.get("app_secret")); + String merchantsId = StringUtils.toString(config.get("merchants_id")); + //签名发送请求 + String mysing = appSecret + "app_key" + appKey + "body" + json + "formatjsonsource_id" + merchantsId + "timestamp" + DateUtil.getDateline() + "v1.0" + appSecret; + String signature = StringUtils.md5(mysing).toUpperCase(); + Map requstJson = new HashMap<>(); + requstJson.put("source_id", merchantsId); + requstJson.put("app_key", appKey); + requstJson.put("format", "json"); + requstJson.put("timestamp", StringUtils.toString(DateUtil.getDateline())); + requstJson.put("signature", signature); + requstJson.put("body", json); + requstJson.put("v", "1.0"); + return requstJson; + } + + /** + * 获取城市code + * + * @param cityName 城市名称 + * @return 城市编码 + */ + private String getCityCode(String cityName, Map config) { + //获取参数 + String url = StringUtils.toString(config.get("url")); + JSONObject jsonObject = new JSONObject(); + Map requstJson = getConfig(config, jsonObject.toString()); + //获取所有城市编码 + String result = HttpUtils.doPostWithJson(url + InstantDeliveryUrl.DD_CITY_CODE.getUrl(), requstJson); + InstantDeliveryResultVO resultDO = JSONUtil.toBean(result, InstantDeliveryResultVO.class); + //对数据进行格式化 + + List list = JSONUtil.toList(JSONUtil.parseArray(resultDO.getResult()), CityResult.class); + for (CityResult cityResult : list) { + if (cityName.contains(cityResult.getCityName())) { + return cityResult.getCityCode(); + } + } + //如果找不到默认哈尔滨 + return "0451"; + } + + @Override + public void callBack(Object object) { + //强制将obj转换成达达对应的参数对象 + DdOrderBackVO ddOrderBackVO = (DdOrderBackVO) object; + //数据类型转换 + InstantDeliveryLog instantDeliveryLog = new InstantDeliveryLog(ddOrderBackVO); + //保存数据 + instantDeliveryLogService.save(instantDeliveryLog); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/dada/enums/DadaOrderStatusEnum.java b/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/dada/enums/DadaOrderStatusEnum.java new file mode 100644 index 00000000..6468baf5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/dada/enums/DadaOrderStatusEnum.java @@ -0,0 +1,61 @@ +package cn.lili.modules.system.entity.plugin.InstantDelivery.dada.enums; + +import lombok.Getter; +import lombok.Setter; + +/** + * 达达配送订单状态 + * + * @author pikachu + */ +public enum DadaOrderStatusEnum { + //待接单 + WAIT_RECEIVING(1, "待接单"), + //待取货 + WAIT_PICK_UP(2, "待取货"), + //配送中 + DELIVERY_IN_PROGRESS(3, "配送中"), + //已完成 + COMPLETED(4, "已完成"), + //已取消 + CANCELLED(5, "已取消"), + //派单中 + DISTRIBUTION_LEAFLETS(8, "派单中"), + //妥投异常之物品返回中 + ABNORMAL_BACK(9, "妥投异常之物品返回中"), + //妥投异常之物品返回完成 + ABNORMAL_COMPLETED(10, "妥投异常之物品返回完成"), + //骑士到店 + TO_IN_STORE(100, "骑士到店"); + + /** + * 状态 + */ + @Setter + @Getter + private Integer status; + /** + * 状态文本 + */ + @Setter + @Getter + private String text; + + + DadaOrderStatusEnum(Integer status, String text) { + this.status = status; + this.text = text; + } + + public static String getText(Integer status) { + if (status != null) { + return null; + } + for (DadaOrderStatusEnum dadaOrderStatusEnum : DadaOrderStatusEnum.values()) { + if (status.equals(dadaOrderStatusEnum.getStatus())) { + return dadaOrderStatusEnum.getText(); + } + } + return null; + } +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/dada/vo/DdOrderBackVO.java b/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/dada/vo/DdOrderBackVO.java new file mode 100644 index 00000000..4ce90bb1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/plugin/InstantDelivery/dada/vo/DdOrderBackVO.java @@ -0,0 +1,50 @@ +package cn.lili.modules.system.entity.plugin.InstantDelivery.dada.vo; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.ToString; + +/** + * @author 86133 + * @description: pikachu + * @date 2020/9/1215:35 + */ +@ApiModel(description = "达达订单回调参数") +@Data +@ToString +@JsonNaming(value = PropertyNamingStrategy.SnakeCaseStrategy.class) +public class DdOrderBackVO { + @ApiModelProperty(value = "达达运单号", required = false) + private String clientId; + + @ApiModelProperty(value = "交易编号", required = true) + private String orderId; + + @ApiModelProperty(value = "订单状态 待接单=1,待取货=2,配送中=3,已完成=4,已取消=5, 指派单=8,妥投异常之物品返回中=9, 妥投异常之物品返回完成=10, 骑士到店=100,创建达达运单失败=1000", required = true) + private Integer orderStatus; + + @ApiModelProperty(value = "订单取消原因,其他状态下默认值为空字符串", required = true) + private String cancelReason; + + @ApiModelProperty(value = "订单取消原因来源(1:达达配送员取消;2:商家主动取消;3:系统或客服取消;0:默认值)", required = true) + private Integer cancelFrom; + + @ApiModelProperty(value = "更新时间", required = true) + private Long updateTime; + + @ApiModelProperty(value = "加密串", required = true) + private String signature; + + @ApiModelProperty(value = "达达配送员id,接单以后会传", required = false) + private Integer dmId; + + @ApiModelProperty(value = "配送员姓名,接单以后会传", required = false) + private String dmName; + + @ApiModelProperty(value = "配送员手机号,接单以后会传", required = false) + private String dmMobile; + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/plugin/RadioOption.java b/framework/src/main/java/cn/lili/modules/system/entity/plugin/RadioOption.java new file mode 100644 index 00000000..0c558303 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/plugin/RadioOption.java @@ -0,0 +1,28 @@ +package cn.lili.modules.system.entity.plugin; + +import lombok.Data; +import lombok.ToString; + +/** + * 单选配置类 + * + * @author pikachu + * @version v4.0 + * @Description:单选配置类 + * @since 2020/12/01 15:58 + */ +@Data +@ToString +public class RadioOption { + + /** + * 选项 + */ + private String label; + + /** + * 选项值 + */ + private Object value; + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/vo/CityResult.java b/framework/src/main/java/cn/lili/modules/system/entity/vo/CityResult.java new file mode 100644 index 00000000..07d45bbc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/vo/CityResult.java @@ -0,0 +1,22 @@ +package cn.lili.modules.system.entity.vo; + +import lombok.Data; + +/** + * 获取城市结果 + * + * @author pikachu + * @date 18/6/27 上午9:28 + */ +@Data +public class CityResult { + /** + * 城市名称 + */ + private String cityName; + /** + * 城市code + */ + private String cityCode; + +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/vo/InstantDeliveryResultVO.java b/framework/src/main/java/cn/lili/modules/system/entity/vo/InstantDeliveryResultVO.java new file mode 100644 index 00000000..9b774068 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/vo/InstantDeliveryResultVO.java @@ -0,0 +1,24 @@ +package cn.lili.modules.system.entity.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 达达返回值 + * + * @author Chopper + * @date 2020/9/12 14:05 + */ +@ApiModel +@Data +public class InstantDeliveryResultVO { + @ApiModelProperty(value = "响应状态,成功为success,失败为fail", required = false) + private String status; + @ApiModelProperty(value = "响应返回码, 0 成功 其他失败", required = false) + private Integer code; + @ApiModelProperty(value = "响应描述", required = false) + private String msg; + @ApiModelProperty(value = "响应结果", required = false) + private Object result; +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/vo/InstantDeliveryVO.java b/framework/src/main/java/cn/lili/modules/system/entity/vo/InstantDeliveryVO.java new file mode 100644 index 00000000..40773a3b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/vo/InstantDeliveryVO.java @@ -0,0 +1,64 @@ +package cn.lili.modules.system.entity.vo; + + +import cn.lili.modules.system.entity.dos.InstantDelivery; +import cn.lili.modules.system.entity.plugin.ConfigItem; +import cn.lili.modules.system.entity.plugin.InstantDelivery.InstantDeliveryPlugin; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 即时配送VO + * + * @author pikachu + * @since 2020/12/01 15:58 + */ +@Data +@ApiModel(value = "即时配送VO") +@AllArgsConstructor +@NoArgsConstructor +public class InstantDeliveryVO extends InstantDelivery { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "即时配送配置项", required = true) + private List configItems; + /** + * 构建新的vo参数 + * + * @param instantDelivery + */ + public InstantDeliveryVO(InstantDelivery instantDelivery) { + this.setCreateTime(instantDelivery.getCreateTime()); + this.setDeleteFlag(instantDelivery.getDeleteFlag()); + this.setId(instantDelivery.getId()); + this.setDeliveryName(instantDelivery.getDeliveryName()); + this.setDeliveryOpen(instantDelivery.getDeliveryOpen()); + this.setDeliveryBean(instantDelivery.getDeliveryBean()); + this.setImages(instantDelivery.getImages()); + Gson gson = new Gson(); + this.setConfigItems(gson.fromJson(instantDelivery.getDeliveryConfig(), new TypeToken>() { + }.getType())); + } + + /** + * 根据插件构建默认参数 + * + * @param instantDeliveryPlugin + */ + public InstantDeliveryVO(InstantDeliveryPlugin instantDeliveryPlugin) { + this.setId("0"); + this.setDeliveryName(instantDeliveryPlugin.getPluginName()); + this.setDeliveryOpen(instantDeliveryPlugin.getOpen()); + this.setDeliveryBean(instantDeliveryPlugin.getPluginId()); + this.setConfigItems(instantDeliveryPlugin.getDefaultConfigItem()); + } + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/vo/RegionVO.java b/framework/src/main/java/cn/lili/modules/system/entity/vo/RegionVO.java new file mode 100644 index 00000000..e2a60641 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/vo/RegionVO.java @@ -0,0 +1,30 @@ +package cn.lili.modules.system.entity.vo; + +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.system.entity.dos.Region; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * 地区VO + * + * @author Chopper + * @date 2021-02-08 09:49 + */ +@Data +@NoArgsConstructor +public class RegionVO extends Region { + + /** + * 子信息 + */ + private List children; + + public RegionVO(Region region) { + BeanUtil.copyProperties(region, this); + this.children = new ArrayList<>(); + } +} diff --git a/framework/src/main/java/cn/lili/modules/system/entity/vo/StoreLogisticsVO.java b/framework/src/main/java/cn/lili/modules/system/entity/vo/StoreLogisticsVO.java new file mode 100644 index 00000000..9b5ce5d1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/vo/StoreLogisticsVO.java @@ -0,0 +1,27 @@ +package cn.lili.modules.system.entity.vo; + +import cn.lili.base.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 物流公司设置 + * + * @author Chopper + * @date 2020/11/17 8:01 下午 + */ + +@Data +@ApiModel(value = "物流公司VO") +public class StoreLogisticsVO extends BaseEntity { + + @ApiModelProperty(value = "物流公司ID") + private String LogisticsId; + + @ApiModelProperty(value = "物流公司名称") + private String name; + + @ApiModelProperty(value = "已选择", notes = "如果已选择则有值,没有选择则无值") + private String selected; +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/entity/vo/Traces.java b/framework/src/main/java/cn/lili/modules/system/entity/vo/Traces.java new file mode 100644 index 00000000..23fe19dc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/entity/vo/Traces.java @@ -0,0 +1,35 @@ +package cn.lili.modules.system.entity.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; + +/** + * 物流信息 + * + * @author Chopper + * @date 2021/1/18 3:28 下午 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Traces { + + /** + * 物流公司 + */ + private String shipper; + + /** + * 物流单号 + */ + private String LogisticCode; + + /** + * 物流详细信息 + */ + private List traces; +} diff --git a/framework/src/main/java/cn/lili/modules/system/mapper/AppVersionMapper.java b/framework/src/main/java/cn/lili/modules/system/mapper/AppVersionMapper.java new file mode 100644 index 00000000..2f48917c --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/mapper/AppVersionMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.system.mapper; + +import cn.lili.modules.system.entity.dos.AppVersionDO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * app版本控制数据处理层 + * + * @author Chopper + * @date 2020/11/17 8:01 下午 + */ +public interface AppVersionMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/mapper/InstantDeliveryLogMapper.java b/framework/src/main/java/cn/lili/modules/system/mapper/InstantDeliveryLogMapper.java new file mode 100644 index 00000000..979588be --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/mapper/InstantDeliveryLogMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.system.mapper; + +import cn.lili.modules.system.entity.dos.InstantDeliveryLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 即时配送日志数据处理层 + * + * @author pikachu + * @date 2020/11/17 8:01 下午 + */ +public interface InstantDeliveryLogMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/mapper/InstantDeliveryMapper.java b/framework/src/main/java/cn/lili/modules/system/mapper/InstantDeliveryMapper.java new file mode 100644 index 00000000..563c4d36 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/mapper/InstantDeliveryMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.system.mapper; + +import cn.lili.modules.system.entity.dos.InstantDelivery; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 即时配送数据处理层 + * + * @author pikachu + * @date 2020/11/17 8:01 下午 + */ +public interface InstantDeliveryMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/mapper/LogisticsMapper.java b/framework/src/main/java/cn/lili/modules/system/mapper/LogisticsMapper.java new file mode 100644 index 00000000..677820ae --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/mapper/LogisticsMapper.java @@ -0,0 +1,14 @@ +package cn.lili.modules.system.mapper; + +import cn.lili.modules.system.entity.dos.Logistics; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 物流公司数据处理层 + * + * @author Chopper + * @date 2020/11/17 8:01 下午 + */ +public interface LogisticsMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/mapper/SensitiveWordsMapper.java b/framework/src/main/java/cn/lili/modules/system/mapper/SensitiveWordsMapper.java new file mode 100644 index 00000000..94091bfb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/mapper/SensitiveWordsMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.system.mapper; + +import cn.lili.modules.system.entity.dos.SensitiveWords; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 敏感词数据处理层 + * @author Bulbasaur + * @date 2020/11/17 8:01 下午 + */ +public interface SensitiveWordsMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/mapper/ServiceNoticeMapper.java b/framework/src/main/java/cn/lili/modules/system/mapper/ServiceNoticeMapper.java new file mode 100644 index 00000000..433cf38e --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/mapper/ServiceNoticeMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import cn.lili.modules.system.entity.dos.ServiceNotice; + +/** + * 服务订阅消息数据处理层 + * @author Chopper + * @date 2020/11/17 8:01 下午 + */ +public interface ServiceNoticeMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/mapper/SettingMapper.java b/framework/src/main/java/cn/lili/modules/system/mapper/SettingMapper.java new file mode 100644 index 00000000..e1c8bba5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/mapper/SettingMapper.java @@ -0,0 +1,13 @@ +package cn.lili.modules.system.mapper; + +import cn.lili.modules.system.entity.dos.Setting; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 配置数据处理层 + * @author Chopper + * @date 2020/11/17 8:01 下午 + */ +public interface SettingMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/mapper/StoreLogisticsMapper.java b/framework/src/main/java/cn/lili/modules/system/mapper/StoreLogisticsMapper.java new file mode 100644 index 00000000..d441d321 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/mapper/StoreLogisticsMapper.java @@ -0,0 +1,24 @@ +package cn.lili.modules.system.mapper; + +import cn.lili.modules.store.entity.dos.StoreLogistics; +import cn.lili.modules.system.entity.vo.StoreLogisticsVO; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * 物流公司数据处理层 + * + * @author Chopper + * @date 2020/11/17 8:01 下午 + */ +public interface StoreLogisticsMapper extends BaseMapper { + + @Select("SELECT l.* FROM li_logistics l RIGHT JOIN li_store_logistics sl ON l.id=sl.logistics_id WHERE sl.store_id=#{storeId}") + List getSelectedStoreLogistics(String storeId); + + @Select("SELECT *, ( SELECT sl.id FROM li_store_logistics sl WHERE l.id = sl.logistics_id AND sl.store_id=#{storeId} ) AS selected FROM li_logistics l;") + List getStoreLogistics(String storeId); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/service/AppVersionService.java b/framework/src/main/java/cn/lili/modules/system/service/AppVersionService.java new file mode 100644 index 00000000..46352afb --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/service/AppVersionService.java @@ -0,0 +1,15 @@ +package cn.lili.modules.system.service; + +import cn.lili.modules.system.entity.dos.AppVersionDO; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 物流公司业务层 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +public interface AppVersionService extends IService { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/service/InstantDeliveryLogService.java b/framework/src/main/java/cn/lili/modules/system/service/InstantDeliveryLogService.java new file mode 100644 index 00000000..fd4e5ce1 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/service/InstantDeliveryLogService.java @@ -0,0 +1,15 @@ +package cn.lili.modules.system.service; + +import cn.lili.modules.system.entity.dos.InstantDeliveryLog; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 即时配送日志业务层 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +public interface InstantDeliveryLogService extends IService { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/service/InstantDeliveryService.java b/framework/src/main/java/cn/lili/modules/system/service/InstantDeliveryService.java new file mode 100644 index 00000000..97f983f4 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/service/InstantDeliveryService.java @@ -0,0 +1,50 @@ +package cn.lili.modules.system.service; + +import cn.lili.common.vo.PageVO; +import cn.lili.modules.system.entity.dos.InstantDelivery; +import cn.lili.modules.system.entity.vo.InstantDeliveryVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 即时配送业务层 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +public interface InstantDeliveryService extends IService { + + /** + * 获取即时配送方案 + * + * @param page 数据库即时配送方案 + * @param pageVO 分页数据 + * @return + */ + IPage getInstantDeliveryPage(IPage page, PageVO pageVO); + + /** + * 根据beanId查询即时配送方案 + * + * @param bean beanId + * @return + */ + InstantDeliveryVO getInstantDeliveryConfig(String bean); + + /** + * 开启某一个即时配送方案 + * + * @param bean bean + * @return + */ + void openInstantDelivery(String bean); + + /** + * 修改即时配送方案 + * + * @param instantDeliveryVO 即时配送方案 + * @return + */ + InstantDeliveryVO edit(InstantDeliveryVO instantDeliveryVO); + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/service/LogisticsService.java b/framework/src/main/java/cn/lili/modules/system/service/LogisticsService.java new file mode 100644 index 00000000..ba46d90d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/service/LogisticsService.java @@ -0,0 +1,32 @@ +package cn.lili.modules.system.service; + +import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.entity.vo.Traces; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 物流公司业务层 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +public interface LogisticsService extends IService { + + /** + * 查询物流信息 + * + * @param logisticsId 物流公司ID + * @param logisticsNo 单号 + * @return + */ + Traces getLogistic(String logisticsId, String logisticsNo); + + /** + * 获取已开启的物流公司列表 + * + * @return 物流公司列表 + */ + List getOpenLogistics(); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/service/SensitiveWordsService.java b/framework/src/main/java/cn/lili/modules/system/service/SensitiveWordsService.java new file mode 100644 index 00000000..9ef46972 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/service/SensitiveWordsService.java @@ -0,0 +1,15 @@ +package cn.lili.modules.system.service; + +import cn.lili.modules.system.entity.dos.SensitiveWords; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 敏感词业务层 + * + * @author Bulbasaur + * @date 2020/11/17 8:02 下午 + */ +public interface SensitiveWordsService extends IService { + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/service/ServiceNoticeService.java b/framework/src/main/java/cn/lili/modules/system/service/ServiceNoticeService.java new file mode 100644 index 00000000..f23d7753 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/service/ServiceNoticeService.java @@ -0,0 +1,14 @@ +package cn.lili.modules.system.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import cn.lili.modules.system.entity.dos.ServiceNotice; + +/** + * 服务订阅消息业务层 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +public interface ServiceNoticeService extends IService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/service/SettingService.java b/framework/src/main/java/cn/lili/modules/system/service/SettingService.java new file mode 100644 index 00000000..50f33937 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/service/SettingService.java @@ -0,0 +1,35 @@ +package cn.lili.modules.system.service; + +import cn.lili.modules.system.entity.dos.Setting; +import com.baomidou.mybatisplus.extension.service.IService; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; + +/** + * 配置业务层 + * + * @author Chopper + * @date 2020/11/17 3:46 下午 + */ +@CacheConfig(cacheNames = "{setting}") +public interface SettingService extends IService { + + /** + * 通过key获取 + * + * @param key + * @return + */ + @Cacheable(key = "#key") + Setting get(String key); + + /** + * 修改 + * + * @param setting + * @return + */ + @CacheEvict(key = "#setting.id") + boolean saveUpdate(Setting setting); +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/service/StoreLogisticsService.java b/framework/src/main/java/cn/lili/modules/system/service/StoreLogisticsService.java new file mode 100644 index 00000000..0fb6adc3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/service/StoreLogisticsService.java @@ -0,0 +1,40 @@ +package cn.lili.modules.system.service; + +import cn.lili.modules.store.entity.dos.StoreLogistics; +import cn.lili.modules.system.entity.vo.StoreLogisticsVO; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 店铺-物流公司业务层 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +public interface StoreLogisticsService extends IService { + + /** + * 获取当前店铺的物流公司列表 + * + * @return 物流公司列表 + */ + List getStoreLogistics(); + + /** + * 获取当前店铺已选择的物流公司列表 + * + * @return 物流公司列表 + */ + List getStoreSelectedLogistics(); + + /** + * 添加店铺-物流公司 + * + * @param logisticsId + * @return 店铺物流公司 + */ + StoreLogistics add(String logisticsId); + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/serviceimpl/AppVersionServiceImpl.java b/framework/src/main/java/cn/lili/modules/system/serviceimpl/AppVersionServiceImpl.java new file mode 100644 index 00000000..3bb17469 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/serviceimpl/AppVersionServiceImpl.java @@ -0,0 +1,24 @@ +package cn.lili.modules.system.serviceimpl; + +import cn.lili.modules.system.entity.dos.AppVersionDO; +import cn.lili.modules.system.mapper.AppVersionMapper; +import cn.lili.modules.system.service.AppVersionService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +/** + * APP版本控制业务层实现 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AppVersionServiceImpl extends ServiceImpl implements AppVersionService { + +} diff --git a/framework/src/main/java/cn/lili/modules/system/serviceimpl/InstantDeliveryLogServiceImpl.java b/framework/src/main/java/cn/lili/modules/system/serviceimpl/InstantDeliveryLogServiceImpl.java new file mode 100644 index 00000000..1580e791 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/serviceimpl/InstantDeliveryLogServiceImpl.java @@ -0,0 +1,23 @@ +package cn.lili.modules.system.serviceimpl; + +import cn.lili.modules.system.entity.dos.InstantDeliveryLog; +import cn.lili.modules.system.mapper.InstantDeliveryLogMapper; +import cn.lili.modules.system.service.InstantDeliveryLogService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 即时配送业务层实现 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class InstantDeliveryLogServiceImpl extends ServiceImpl implements InstantDeliveryLogService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/serviceimpl/InstantDeliveryServiceImpl.java b/framework/src/main/java/cn/lili/modules/system/serviceimpl/InstantDeliveryServiceImpl.java new file mode 100644 index 00000000..d6e91ccc --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/serviceimpl/InstantDeliveryServiceImpl.java @@ -0,0 +1,126 @@ +package cn.lili.modules.system.serviceimpl; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.system.entity.dos.InstantDelivery; +import cn.lili.modules.system.entity.plugin.InstantDelivery.InstantDeliveryPlugin; +import cn.lili.modules.system.entity.vo.InstantDeliveryVO; +import cn.lili.modules.system.mapper.InstantDeliveryMapper; +import cn.lili.modules.system.service.InstantDeliveryService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.gson.Gson; +import lombok.RequiredArgsConstructor; +import org.elasticsearch.ResourceNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 即时配送业务层实现 + * + * @author pikachu + * @date 2020/11/17 8:02 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class InstantDeliveryServiceImpl extends ServiceImpl implements InstantDeliveryService { + + @Autowired + private List instantDeliveryPlugins; + + @Override + public IPage getInstantDeliveryPage(IPage page, PageVO pageVO) { + //获取插件和数据库中所有的就是配送方案 + List resultList = this.getInstantDeliveryVOList(page); + //循环数据,对未入库的数据进行入库操作 + for (InstantDeliveryVO instantDeliveryVO : resultList) { + //根据id是否为0校验 如果为0则不在数据中,进行入库操作 + if (instantDeliveryVO.getId().equals("0")) { + //入库 + InstantDelivery instantDelivery = new InstantDelivery(instantDeliveryVO); + this.baseMapper.insert(instantDelivery); + } + } + IPage iPage = new Page<>(pageVO.getPageNumber(), pageVO.getPageSize(), resultList.size()); + iPage.setRecords(PageUtil.listToPage(pageVO, resultList)); + return iPage; + } + + /** + * 获取即时配送的方案 + * + * @return 即时配送 + */ + private List getInstantDeliveryVOList(IPage page) { + //用来构建新的即时配送数据 + List resultList = new ArrayList<>(); + //获取即时配送数据 + List list = page.getRecords(); + Map map = new HashMap<>(16); + for (InstantDelivery instantDelivery : list) { + map.put(instantDelivery.getDeliveryBean(), instantDelivery); + } + //循环检查是否有新的即时配送方式,识别插入数据库 + for (InstantDeliveryPlugin plugin : instantDeliveryPlugins) { + InstantDelivery instantDelivery = map.get(plugin.getPluginId()); + InstantDeliveryVO result; + //如果不为空则构建vo参数,否则的话根据插件属性构建vo参数 + if (instantDelivery != null) { + result = new InstantDeliveryVO(instantDelivery); + } else { + result = new InstantDeliveryVO(plugin); + } + resultList.add(result); + } + return resultList; + } + + + @Override + public InstantDeliveryVO getInstantDeliveryConfig(String bean) { + //根据bean获取即时配送方案 + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("delivery_bean", bean); + InstantDelivery instantDelivery = this.baseMapper.selectOne(queryWrapper); + if (instantDelivery == null) { + throw new ResourceNotFoundException("该即时配送方案不存在"); + } + return new InstantDeliveryVO(instantDelivery); + } + + @Override + public void openInstantDelivery(String bean) { + //关闭所有配送方案 + UpdateWrapper updateWrapper = new UpdateWrapper(); + updateWrapper.set("delivery_open", 0); + this.update(updateWrapper); + //开启当前配送方案 + updateWrapper = new UpdateWrapper(); + updateWrapper.eq("delivery_bean", bean); + updateWrapper.set("delivery_open", 1); + this.update(updateWrapper); + } + + @Override + public InstantDeliveryVO edit(InstantDeliveryVO instantDeliveryVO) { + //校验此方案是否存在 + this.getInstantDeliveryConfig(instantDeliveryVO.getDeliveryBean()); + //修改即时配送方案 + Gson gson = new Gson(); + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.set("delivery_config", gson.toJson(instantDeliveryVO.getConfigItems())); + updateWrapper.eq("delivery_bean", instantDeliveryVO.getDeliveryBean()); + this.update(updateWrapper); + return instantDeliveryVO; + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/serviceimpl/LogisticsServiceImpl.java b/framework/src/main/java/cn/lili/modules/system/serviceimpl/LogisticsServiceImpl.java new file mode 100644 index 00000000..9f00615f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/serviceimpl/LogisticsServiceImpl.java @@ -0,0 +1,277 @@ +package cn.lili.modules.system.serviceimpl; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.vo.Traces; +import cn.lili.modules.system.entity.dto.KuaidiSetting; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.mapper.LogisticsMapper; +import cn.lili.modules.system.service.LogisticsService; +import cn.lili.modules.system.service.SettingService; +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.gson.Gson; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 物流公司业务层实现 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class LogisticsServiceImpl extends ServiceImpl implements LogisticsService { + + private final SettingService settingService; + + @Override + public Traces getLogistic(String logisticsId, String logisticsNo) { + try { + return getOrderTracesByJson(logisticsId, logisticsNo); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + public List getOpenLogistics() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(Logistics::getDisabled, SwitchEnum.OPEN.name()); + return this.list(queryWrapper); + } + + /** + * 获取物流信息 + * + * @param logisticsId 物流公司ID + * @param expNo 物流单号 + * @return 物流信息 + * @throws Exception + */ + private Traces getOrderTracesByJson(String logisticsId, String expNo) throws Exception { + Setting setting = settingService.getById(SettingEnum.KUAIDI_SETTING.name()); + if (StrUtil.isBlank(setting.getSettingValue())) { + throw new ServiceException("您还未配置快递查询"); + } + KuaidiSetting kuaidiSetting = new Gson().fromJson(setting.getSettingValue(), KuaidiSetting.class); + + //ID + String EBusinessID = kuaidiSetting.getEbusinessID(); + + //KEY + String AppKey = kuaidiSetting.getAppKey(); + + //请求url + String ReqURL = kuaidiSetting.getReqURL(); + + Logistics logistics = this.getById(logisticsId); + + if (logistics != null) { + String requestData = "{'OrderCode':'','ShipperCode':'" + logistics.getCode() + "','LogisticCode':'" + expNo + "'}"; + Map params = new HashMap(); + params.put("RequestData", urlEncoder(requestData, "UTF-8")); + params.put("EBusinessID", EBusinessID); + params.put("RequestType", "8001"); + String dataSign = encrypt(requestData, AppKey, "UTF-8"); + params.put("DataSign", urlEncoder(dataSign, "UTF-8")); + params.put("DataType", "2"); + + String result = sendPost(ReqURL, params); + Map map = (Map) JSON.parse(result); + return new Traces(logistics.getName(), expNo, (List) map.get("Traces")); + } + return null; + } + + /** + * MD5加密 + * + * @param str 内容 + * @param charset 编码方式 + * @throws Exception + */ + @SuppressWarnings("unused") + private String MD5(String str, String charset) throws Exception { + MessageDigest md = MessageDigest.getInstance("MD5"); + md.update(str.getBytes(charset)); + byte[] result = md.digest(); + StringBuffer sb = new StringBuffer(32); + for (int i = 0; i < result.length; i++) { + int val = result[i] & 0xff; + if (val <= 0xf) { + sb.append("0"); + } + sb.append(Integer.toHexString(val)); + } + return sb.toString().toLowerCase(); + } + + /** + * base64编码 + * + * @param str 内容 + * @param charset 编码方式di + * @throws UnsupportedEncodingException + */ + private String base64(String str, String charset) throws UnsupportedEncodingException { + String encoded = base64Encode(str.getBytes(charset)); + return encoded; + } + + @SuppressWarnings("unused") + private String urlEncoder(String str, String charset) throws UnsupportedEncodingException { + String result = URLEncoder.encode(str, charset); + return result; + } + + /** + * 电商Sign签名生成 + * + * @param content 内容 + * @param keyValue Appkey + * @param charset 编码方式 + * @return DataSign签名 + * @throws UnsupportedEncodingException ,Exception + */ + @SuppressWarnings("unused") + private String encrypt(String content, String keyValue, String charset) throws Exception { + if (keyValue != null) { + return base64(MD5(content + keyValue, charset), charset); + } + return base64(MD5(content, charset), charset); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param params 请求的参数集合 + * @return 远程资源的响应结果 + */ + @SuppressWarnings("unused") + private String sendPost(String url, Map params) { + OutputStreamWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try { + URL realUrl = new URL(url); + HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); + // 发送POST请求必须设置如下两行 + conn.setDoOutput(true); + conn.setDoInput(true); + // POST方法 + conn.setRequestMethod("POST"); + // 设置通用的请求属性 + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", + "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + conn.connect(); + // 获取URLConnection对象对应的输出流 + out = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8); + // 发送请求参数 + if (params != null) { + StringBuilder param = new StringBuilder(); + for (Map.Entry entry : params.entrySet()) { + if (param.length() > 0) { + param.append("&"); + } + param.append(entry.getKey()); + param.append("="); + param.append(entry.getValue()); + //System.out.println(entry.getKey()+":"+entry.getValue()); + } + //System.out.println("param:"+param.toString()); + out.write(param.toString()); + } + // flush输出流的缓冲 + out.flush(); + // 定义BufferedReader输入流来读取URL的响应 + in = new BufferedReader( + new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) { + result.append(line); + } + } catch (Exception e) { + e.printStackTrace(); + } + //使用finally块来关闭输出流、输入流 + finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + return result.toString(); + } + + + private static final char[] base64EncodeChars = new char[]{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/'}; + + public static String base64Encode(byte[] data) { + StringBuffer sb = new StringBuffer(); + int len = data.length; + int i = 0; + int b1, b2, b3; + while (i < len) { + b1 = data[i++] & 0xff; + if (i == len) { + sb.append(base64EncodeChars[b1 >>> 2]); + sb.append(base64EncodeChars[(b1 & 0x3) << 4]); + sb.append("=="); + break; + } + b2 = data[i++] & 0xff; + if (i == len) { + sb.append(base64EncodeChars[b1 >>> 2]); + sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); + sb.append(base64EncodeChars[(b2 & 0x0f) << 2]); + sb.append("="); + break; + } + b3 = data[i++] & 0xff; + sb.append(base64EncodeChars[b1 >>> 2]); + sb.append(base64EncodeChars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]); + sb.append(base64EncodeChars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]); + sb.append(base64EncodeChars[b3 & 0x3f]); + } + return sb.toString(); + } + +} diff --git a/framework/src/main/java/cn/lili/modules/system/serviceimpl/SensitiveWordsServiceImpl.java b/framework/src/main/java/cn/lili/modules/system/serviceimpl/SensitiveWordsServiceImpl.java new file mode 100644 index 00000000..f5c4bd4f --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/serviceimpl/SensitiveWordsServiceImpl.java @@ -0,0 +1,23 @@ +package cn.lili.modules.system.serviceimpl; + +import cn.lili.modules.system.entity.dos.SensitiveWords; +import cn.lili.modules.system.mapper.SensitiveWordsMapper; +import cn.lili.modules.system.service.SensitiveWordsService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 敏感词业务层实现 + * + * @author Bulbasaur + * @date 2020/11/17 8:02 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SensitiveWordsServiceImpl extends ServiceImpl implements SensitiveWordsService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/serviceimpl/ServiceNoticeServiceImpl.java b/framework/src/main/java/cn/lili/modules/system/serviceimpl/ServiceNoticeServiceImpl.java new file mode 100644 index 00000000..1f2a18ae --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/serviceimpl/ServiceNoticeServiceImpl.java @@ -0,0 +1,22 @@ +package cn.lili.modules.system.serviceimpl; + +import cn.lili.modules.system.entity.dos.ServiceNotice; +import cn.lili.modules.system.mapper.ServiceNoticeMapper; +import cn.lili.modules.system.service.ServiceNoticeService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 服务订阅消息业务层实现 + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ServiceNoticeServiceImpl extends ServiceImpl implements ServiceNoticeService { + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/serviceimpl/SettingServiceImpl.java b/framework/src/main/java/cn/lili/modules/system/serviceimpl/SettingServiceImpl.java new file mode 100644 index 00000000..795b5c4d --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/serviceimpl/SettingServiceImpl.java @@ -0,0 +1,32 @@ +package cn.lili.modules.system.serviceimpl; + +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.mapper.SettingMapper; +import cn.lili.modules.system.service.SettingService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 配置业务层实现 + * + * @author Chopper + * @date 2020/11/17 3:52 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SettingServiceImpl extends ServiceImpl implements SettingService { + + @Override + public Setting get(String key) { + return this.getById(key); + } + + @Override + public boolean saveUpdate(Setting setting) { + return this.saveOrUpdate(setting); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/serviceimpl/StoreLogisticsServiceImpl.java b/framework/src/main/java/cn/lili/modules/system/serviceimpl/StoreLogisticsServiceImpl.java new file mode 100644 index 00000000..ce392b03 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/serviceimpl/StoreLogisticsServiceImpl.java @@ -0,0 +1,57 @@ +package cn.lili.modules.system.serviceimpl; + +import cn.lili.common.security.context.UserContext; +import cn.lili.modules.store.entity.dos.StoreLogistics; +import cn.lili.modules.system.entity.vo.StoreLogisticsVO; +import cn.lili.modules.system.mapper.StoreLogisticsMapper; +import cn.lili.modules.system.service.StoreLogisticsService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 物流公司业务层实现 + * + * @author Chopper + * @date 2020/11/17 8:02 下午 + */ +@Service +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreLogisticsServiceImpl extends ServiceImpl implements StoreLogisticsService { + + private final StoreLogisticsMapper storeLogisticsMapper; + + @Override + public List getStoreLogistics() { + return storeLogisticsMapper.getStoreLogistics(UserContext.getCurrentUser().getStoreId()); + } + + @Override + public List getStoreSelectedLogistics() { + return storeLogisticsMapper.getSelectedStoreLogistics(UserContext.getCurrentUser().getStoreId()); + + } + + @Override + public StoreLogistics add(String logisticsId) { + //判断是否已经选择过,如果没有选择则进行添加 + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery(); + lambdaQueryWrapper.eq(StoreLogistics::getLogisticsId, logisticsId); + lambdaQueryWrapper.eq(StoreLogistics::getStoreId, UserContext.getCurrentUser().getStoreId()); + if (this.getOne(lambdaQueryWrapper) == null) { + StoreLogistics storeLogistics = new StoreLogistics(UserContext.getCurrentUser().getStoreId(), logisticsId); + this.save(storeLogistics); + return storeLogistics; + } + return null; + } + + +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/utils/CharacterConstant.java b/framework/src/main/java/cn/lili/modules/system/utils/CharacterConstant.java new file mode 100644 index 00000000..95030afa --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/utils/CharacterConstant.java @@ -0,0 +1,20 @@ +package cn.lili.modules.system.utils; + +/** + * + * @Description: 字符常量 + * @author Bulbasaur + * @version v1.0 + * @since v1.0 + * 2020-02-25 14:10:16 + */ +public class CharacterConstant { + + + /** + * 字符* + */ + public final static char WILDCARD_STAR = '*'; + + +} diff --git a/framework/src/main/java/cn/lili/modules/system/utils/HttpUtils.java b/framework/src/main/java/cn/lili/modules/system/utils/HttpUtils.java new file mode 100644 index 00000000..43d8ce41 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/utils/HttpUtils.java @@ -0,0 +1,362 @@ +package cn.lili.modules.system.utils; + +import com.alibaba.fastjson.JSONObject; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicHeader; +import org.apache.http.protocol.HTTP; + +import java.io.*; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Http工具 + * + * @author pikachu + * @date 2018/3/13 + */ +public final class HttpUtils { + + public static final int HTTP_CONN_TIMEOUT = 100000; + public static final int HTTP_SOCKET_TIMEOUT = 100000; + + public static String doPost(String reqUrl, Map parameters) { + return doPost(reqUrl, parameters, "UTF-8", HTTP_CONN_TIMEOUT, HTTP_SOCKET_TIMEOUT); + } + + public static String doPost(String reqUrl, Map parameters, String encoding) { + return doPost(reqUrl, parameters, encoding, HTTP_CONN_TIMEOUT, HTTP_SOCKET_TIMEOUT); + } + + public static String doPost(String reqUrl, Map parameters, String encoding, int connectTimeout, + int readTimeout) { + HttpURLConnection urlConn = null; + try { + urlConn = sendPost(reqUrl, parameters, encoding, connectTimeout, readTimeout); + String responseContent = getContent(urlConn, encoding); + return responseContent.trim(); + } finally { + if (urlConn != null) { + urlConn.disconnect(); + + } + } + } + + /** + * post携带json请求 + * + * @param reqUrl + * @param jsonParameters + * @return + */ + public static String doPostWithJson(String reqUrl, Map jsonParameters) { + + BufferedReader reader = null; + try { + URL url = new URL(reqUrl);// 创建连接 + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setInstanceFollowRedirects(true); + connection.setRequestMethod("POST"); // 设置请求方式 + // connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式 + connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式 + connection.connect(); + //一定要用BufferedReader 来接收响应, 使用字节来接收响应的方法是接收不到内容的 + OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8); // utf-8编码 + out.append(JSONObject.toJSONString(jsonParameters)); + out.flush(); + out.close(); + // 读取响应 + reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)); + String line; + String res = ""; + while ((line = reader.readLine()) != null) { + res += line; + } + reader.close(); + + return res; + } catch (IOException e) { + e.printStackTrace(); + } + return "error"; // 自定义错误信息 + + } + + private static HttpURLConnection sendPost(String reqUrl, + Map parameters, String encoding, int connectTimeout, int readTimeout) { + HttpURLConnection urlConn = null; + try { + String params = generatorParamString(parameters, encoding); + URL url = new URL(reqUrl); + urlConn = (HttpURLConnection) url.openConnection(); + urlConn.setRequestMethod("POST"); + // urlConn + // .setRequestProperty( + // "User-Agent", + // "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3"); + // (单位:毫秒)jdk + urlConn.setConnectTimeout(connectTimeout); + // (单位:毫秒)jdk 1.5换成这个,读操作超时 + urlConn.setReadTimeout(readTimeout); + urlConn.setDoOutput(true); + // String按照字节处理是一个好方法 + byte[] b = params.getBytes(encoding); + urlConn.getOutputStream().write(b, 0, b.length); + urlConn.getOutputStream().flush(); + urlConn.getOutputStream().close(); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + return urlConn; + } + + private static String getContent(HttpURLConnection urlConn, String encoding) { + try { + String responseContent = null; + InputStream in = urlConn.getInputStream(); + BufferedReader rd = new BufferedReader(new InputStreamReader(in, encoding)); + String tempLine = rd.readLine(); + StringBuffer tempStr = new StringBuffer(); + String crlf = System.getProperty("line.separator"); + while (tempLine != null) { + tempStr.append(tempLine); + tempStr.append(crlf); + tempLine = rd.readLine(); + } + responseContent = tempStr.toString(); + rd.close(); + in.close(); + return responseContent; + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + /** + * @param link + * @param encoding + * @return + */ + public static String doGet(String link, String encoding, int connectTimeout, int readTimeout) { + HttpURLConnection conn = null; + try { + URL url = new URL(link); + conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setConnectTimeout(connectTimeout); + conn.setReadTimeout(readTimeout); + // conn.setRequestProperty("User-Agent", "Mozilla/5.0"); + BufferedInputStream in = new BufferedInputStream( + conn.getInputStream()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + for (int i = 0; (i = in.read(buf)) > 0; ) { + out.write(buf, 0, i); + } + out.flush(); + String s = out.toString(encoding); + return s; + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } finally { + if (conn != null) { + conn.disconnect(); + conn = null; + } + } + } + + /** + * UTF-8编码 + * + * @param link + * @return + */ + public static String doGet(String link) { + return doGet(link, "UTF-8", HTTP_CONN_TIMEOUT, HTTP_SOCKET_TIMEOUT); + } + + /** + * 将parameters中数据转换成用"&"链接的http请求参数形式 + * + * @param parameters + * @return + */ + private static String generatorParamString(Map parameters, String encoding) { + StringBuffer params = new StringBuffer(); + if (parameters != null) { + for (Iterator iter = parameters.keySet().iterator(); iter + .hasNext(); ) { + String name = iter.next(); + String value = parameters.get(name); + params.append(name + "="); + try { + params.append(URLEncoder.encode(value, encoding)); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e.getMessage(), e); + } catch (Exception e) { + String message = String.format("'%s'='%s'", name, value); + throw new RuntimeException(message, e); + } + if (iter.hasNext()) { + params.append("&"); + } + } + } + return params.toString(); + } + + /** + * post请求封装 参数为{"a":1,"b":2,"c":3} + * + * @param path 接口地址 + * @param Info 参数 + * @return + * @throws IOException + */ + public static String postResponse(String path, JSONObject Info) { + HttpClient client = new DefaultHttpClient(); + HttpPost post = new HttpPost(path); + + post.setHeader("Content-Type", "application/json"); + post.addHeader("Authorization", "Basic YWRtaW46"); + String result = ""; + + try { + StringEntity s = new StringEntity(Info.toString(), "utf-8"); + System.out.println("<-------------------->"); + System.out.println(s); + System.out.println("<-------------------->"); + s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json")); + post.setEntity(s); + // 发送请求 + HttpResponse httpResponse = client.execute(post); + + // 获取响应输入流 + InputStream inStream = httpResponse.getEntity().getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, StandardCharsets.UTF_8)); + StringBuilder strber = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + strber.append(line + "\n"); + } + inStream.close(); + + result = strber.toString(); + System.out.println(result); + + if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + System.out.println("请求服务器成功,做相应处理"); + } else { + System.out.println("请求服务端失败"); + } + + } catch (Exception e) { + System.out.println("请求异常"); + throw new RuntimeException(e); + } + + return result; + } + + public static String http(String url, String proxyUrl, int proxyPort, Map params, String chartSet) + throws Exception { + URL u = null; + HttpURLConnection con = null; + // 构建请求参数 + StringBuffer sb = new StringBuffer(); + OutputStreamWriter osw = null; + BufferedReader br = null; + if (params != null) { + int i = 0; + for (Entry e : params.entrySet()) { + if (i != 0) { + sb.append("&"); + } else { + i++; + } + sb.append(e.getKey()); + if (e.getValue() != null && !e.getValue().equals("")) { + sb.append("="); + sb.append(e.getValue());///URLEncoder.encode(sign, "UTF-8") + } + } + } + System.out.println("连接:" + url); + System.out.println("发送:" + sb.toString()); + try { + u = new URL(url); + if (null != proxyUrl && !proxyUrl.equals("")) { + System.out.println("代理的IP是:" + proxyUrl + ",代理端口:" + proxyPort); + Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyUrl, proxyPort)); + con = (HttpURLConnection) u.openConnection(proxy); + } else { + con = (HttpURLConnection) u.openConnection(); + } + con.setConnectTimeout(30000); + con.setReadTimeout(700000); + con.setRequestMethod("POST"); + con.setDoOutput(true); + con.setDoInput(true); + con.setUseCaches(false); + con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + con.setRequestProperty("Charset", "UTF-8"); + osw = new OutputStreamWriter(con.getOutputStream(), StandardCharsets.UTF_8); + osw.write(sb.toString()); + osw.flush(); + } catch (SocketTimeoutException e) { + throw new Exception(); + } catch (Exception e) { + throw new Exception(); + } finally { + try { + osw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + StringBuffer buffer = new StringBuffer(); + try { + br = new BufferedReader(new InputStreamReader(con.getInputStream(), + StandardCharsets.UTF_8)); + String temp; + while ((temp = br.readLine()) != null) { + buffer.append(temp); + buffer.append("\n"); + } + } catch (SocketTimeoutException e) { + throw new Exception(); + } catch (FileNotFoundException e) { + throw new Exception(); + } catch (Exception e) { + throw new Exception(); + } finally { + try { + if (osw != null) { + osw.close(); + } + if (br != null) { + br.close(); + } + if (con != null) { + con.disconnect(); + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + return buffer.toString(); + } +} \ No newline at end of file diff --git a/framework/src/main/java/cn/lili/modules/system/utils/SensitiveWordsFilter.java b/framework/src/main/java/cn/lili/modules/system/utils/SensitiveWordsFilter.java new file mode 100644 index 00000000..42e422fa --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/utils/SensitiveWordsFilter.java @@ -0,0 +1,253 @@ +package cn.lili.modules.system.utils; + +import cn.lili.modules.system.entity.dos.SensitiveWords; +import cn.lili.modules.system.service.SensitiveWordsService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; +import org.thymeleaf.util.ListUtils; + +import java.io.Serializable; +import java.util.List; +import java.util.NavigableSet; + +/** + * 敏感词过滤器 + * + * @author Bulbasaur + * @version v1.0 + * @since v1.0 + * 2020-02-25 14:10:16 + */ +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SensitiveWordsFilter implements Serializable, ApplicationRunner { + + + /** + * 为2的n次方,考虑到敏感词大概在10k左右, + * 这个数量应为词数的数倍,使得桶很稀疏 + * 提高不命中时hash指向null的概率, + * 加快访问速度。 + */ + static final int DEFAULT_INITIAL_CAPACITY = 131072; + + /** + * 类似HashMap的桶,比较稀疏。 + * 使用2个字符的hash定位。 + */ + protected static SensitiveWordsNode[] nodes; + + private final SensitiveWordsService sensitiveWordsService; + + + /** + * 增加一个敏感词,如果词的长度(trim后)小于2,则丢弃
+ * 此方法(构建)并不是主要的性能优化点。 + * + * @param word + * @return + */ + public static boolean put(String word) { + + // 长度小于2的不加入 + if (word == null || word.trim().length() < 2) { + return false; + } + // 两个字符的不考虑 + if (word.length() == 2 && word.matches("\\w\\w")) { + return false; + } + StringPointer sp = new StringPointer(word.trim()); + // 计算头两个字符的hash + int hash = sp.nextTwoCharHash(0); + // 计算头两个字符的mix表示(mix相同,两个字符相同) + int mix = sp.nextTwoCharMix(0); + // 转为在hash桶中的位置 + int index = hash & (nodes.length - 1); + + // 从桶里拿第一个节点 + SensitiveWordsNode node = nodes[index]; + if (node == null) { + // 如果没有节点,则放进去一个 + node = new SensitiveWordsNode(mix); + // 并添加词 + node.words.add(sp); + // 放入桶里 + nodes[index] = node; + } else { + // 如果已经有节点(1个或多个),找到正确的节点 + for (; node != null; node = node.next) { + // 匹配节点 + if (node.headTwoCharMix == mix) { + node.words.add(sp); + return true; + } + // 如果匹配到最后仍然不成功,则追加一个节点 + if (node.next == null) { + new SensitiveWordsNode(mix, node).words.add(sp); + return true; + } + } + } + return true; + } + + /** + * 移除敏感词 + * + * @param word + * @return + */ + public static boolean remove(String word) { + + StringPointer sp = new StringPointer(word.trim()); + // 计算头两个字符的hash + int hash = sp.nextTwoCharHash(0); + // 计算头两个字符的mix表示(mix相同,两个字符相同) + int mix = sp.nextTwoCharMix(0); + // 转为在hash桶中的位置 + int index = hash & (nodes.length - 1); + SensitiveWordsNode node = nodes[index]; + + for (; node != null; node = node.next) { + // 匹配节点 + if (node.headTwoCharMix == mix) { + node.words.remove(sp); + return true; + } + + } + return true; + + } + + /** + * 对句子进行敏感词过滤
+ * 如果无敏感词返回输入的sentence对象,即可以用下面的方式判断是否有敏感词:
+ * String result = filter.filter(sentence, CharacterConstant.WILDCARD_STAR);
+ * if(result != sentence){
+ *   // 有敏感词
+ * } + *
+ * + * @param sentence 句子 + * @param replace 敏感词的替换字符 + * @return 过滤后的句子 + */ + public static String filter(String sentence, char replace) { + // 先转换为StringPointer + StringPointer sp = new StringPointer(sentence + " "); + + // 标示是否替换 + boolean replaced = false; + + // 匹配的起始位置 + int i = 0; + while (i < sp.length - 2) { + /* + * 移动到下一个匹配位置的步进: + * 如果未匹配为1,如果匹配是匹配的词长度 + */ + int step = 1; + // 计算此位置开始2个字符的hash + int hash = sp.nextTwoCharHash(i); + /* + * 根据hash获取第一个节点, + * 真正匹配的节点可能不是第一个, + * 所以有后面的for循环。 + */ + SensitiveWordsNode node = nodes[hash & (nodes.length - 1)]; + /* + * 如果非敏感词,node基本为null。 + * 这一步大幅提升效率 + */ + if (node != null) { + /* + * 如果能拿到第一个节点, + * 才计算mix(mix相同表示2个字符相同)。 + * mix的意义和HashMap先hash再equals的equals部分类似。 + */ + int mix = sp.nextTwoCharMix(i); + /* + * 循环所有的节点,如果非敏感词, + * mix相同的概率非常低,提高效率 + */ + outer: + for (; node != null; node = node.next) { + /* + * 对于一个节点,先根据头2个字符判断是否属于这个节点。 + * 如果属于这个节点,看这个节点的词库是否命中。 + * 此代码块中访问次数已经很少,不是优化重点 + */ + if (node.headTwoCharMix == mix) { + /* + * 查出比剩余sentence小的最大的词。 + * 例如剩余sentence为"色情电影哪家强?", + * 这个节点含三个词从小到大为:"色情"、"色情电影"、"色情信息"。 + * 则从“色情电影”开始向前匹配 + */ + NavigableSet desSet = node.words.headSet(sp.substring(i), true); + if (desSet != null) { + for (StringPointer word : desSet.descendingSet()) { + /* + * 仍然需要再判断一次,例如"色情信息哪里有?", + * 如果节点只包含"色情电影"一个词, + * 仍然能够取到word为"色情电影",但是不该匹配。 + */ + if (sp.nextStartsWith(i, word)) { + // 匹配成功,将匹配的部分,用replace制定的内容替代 + sp.fill(i, i + word.length, replace); + // 跳过已经替代的部分 + step = word.length; + // 标示有替换 + replaced = true; + // 跳出循环(然后是while循环的下一个位置) + break outer; + } + } + } + + } + } + } + + // 移动到下一个匹配位置 + i += step; + } + + // 如果没有替换,直接返回入参(节约String的构造copy) + if (replaced) { + String res = sp.toString(); + return res.substring(0, res.length() - 2); + } else { + return sentence; + } + } + + /** + * 初始化敏感词 + * + * @param args + * @throws Exception + */ + @Override + public void run(ApplicationArguments args) { + try { + nodes = new SensitiveWordsNode[DEFAULT_INITIAL_CAPACITY]; + //加入平台添加的敏感词 + List list = sensitiveWordsService.list(); + if (ListUtils.isEmpty(list)) { + for (SensitiveWords sensitiveWords : list) { + put(sensitiveWords.getSensitiveWord()); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + + } +} diff --git a/framework/src/main/java/cn/lili/modules/system/utils/SensitiveWordsNode.java b/framework/src/main/java/cn/lili/modules/system/utils/SensitiveWordsNode.java new file mode 100644 index 00000000..7932b773 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/utils/SensitiveWordsNode.java @@ -0,0 +1,38 @@ +package cn.lili.modules.system.utils; + +import java.io.Serializable; +import java.util.TreeSet; + +/** + * @Description: 敏感词节点,每个节点包含了以相同的2个字符开头的所有词 + * @author Bulbasaur + * @version v1.0 + * @since v1.0 + * 2020-02-25 14:10:16 + */ +public class SensitiveWordsNode implements Serializable{ + + /** + * 头两个字符的mix,mix相同,两个字符相同 + */ + protected final int headTwoCharMix; + + /** + * 所有以这两个字符开头的词表 + */ + protected final TreeSet words = new TreeSet(); + + /** + * 下一个节点 + */ + protected SensitiveWordsNode next; + + public SensitiveWordsNode(int headTwoCharMix){ + this.headTwoCharMix = headTwoCharMix; + } + + public SensitiveWordsNode(int headTwoCharMix, SensitiveWordsNode parent){ + this.headTwoCharMix = headTwoCharMix; + parent.next = this; + } +} diff --git a/framework/src/main/java/cn/lili/modules/system/utils/StringPointer.java b/framework/src/main/java/cn/lili/modules/system/utils/StringPointer.java new file mode 100644 index 00000000..b797da0b --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/system/utils/StringPointer.java @@ -0,0 +1,171 @@ +package cn.lili.modules.system.utils; + +import java.io.Serializable; + +/** + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +public class StringPointer implements Serializable, CharSequence, Comparable{ + + private static final long serialVersionUID = 1L; + + protected final char[] value; + + protected final int offset; + + protected final int length; + + private int hash = 0; + + public StringPointer(String str){ + value = str.toCharArray(); + offset = 0; + length = value.length; + } + + public StringPointer(char[] value, int offset, int length){ + this.value = value; + this.offset = offset; + this.length = length; + } + + + /** + * 计算该位置后(包含)2个字符的hash值 + * @param i 从 0 到 length - 2 + * @return 从 0 到 length - 2 + */ + public int nextTwoCharHash(int i){ + return 31 * value[offset + i] + value[offset + i + 1]; + } + + /** + * 计算该位置后(包含)2个字符和为1个int型的值
+ * int值相同表示2个字符相同 + * + * @param i 从 0 到 length - 2 + * @return int值 + */ + public int nextTwoCharMix(int i){ + return (value[offset + i] << 16) | value[offset + i + 1]; + } + + /** + * 该位置后(包含)的字符串,是否以某个词(word)开头 + * + * @param i 从 0 到 length - 2 + * @param word 词 + * @return 是否? + */ + public boolean nextStartsWith(int i, StringPointer word){ + // 是否长度超出 + if(word.length > length - i){ + return false; + } + // 从尾开始判断 + for(int c = word.length - 1; c >= 0; c --){ + if(value[offset + i + c] != word.value[word.offset + c]){ + return false; + } + } + return true; + } + + /** + * 填充(替换) + * + * @param begin 从此位置开始(含) + * @param end 到此位置结束(不含) + * @param fillWith 以此字符填充(替换) + */ + public void fill(int begin, int end, char fillWith){ + for(int i = begin; i < end; i ++){ + value[offset + i] = fillWith; + } + } + + @Override + public int length(){ + return length; + } + + @Override + public char charAt(int i){ + return value[offset + i]; + } + + public StringPointer substring(int begin){ + return new StringPointer(value, offset + begin, length - begin); + } + + public StringPointer substring(int begin, int end){ + return new StringPointer(value, offset + begin, end - begin); + } + + @Override + public CharSequence subSequence(int start, int end) { + return substring(start, end); + } + + @Override + public String toString(){ + return new String(value, offset, length); + } + + @Override + public int hashCode() { + int h = hash; + if (h == 0 && length > 0) { + for (int i = 0; i < length; i++) { + h = 31 * h + value[offset + i]; + } + hash = h; + } + return h; + } + + @Override + public boolean equals(Object anObject) { + if (this == anObject) { + return true; + } + if (anObject instanceof StringPointer) { + StringPointer that = (StringPointer)anObject; + if (length == that.length) { + char v1[] = this.value; + char v2[] = that.value; + for(int i = 0; i < this.length; i ++){ + if(v1[this.offset + i] != v2[that.offset + i]){ + return false; + } + } + return true; + } + } + return false; + } + + @Override + public int compareTo(StringPointer that) { + int len1 = this.length; + int len2 = that.length; + int lim = Math.min(len1, len2); + char v1[] = this.value; + char v2[] = that.value; + + int k = 0; + while (k < lim) { + char c1 = v1[this.offset + k]; + char c2 = v2[that.offset + k]; + if (c1 != c2) { + return c1 - c2; + } + k++; + } + return len1 - len2; + } + +} + diff --git a/framework/src/main/resources/banner.txt b/framework/src/main/resources/banner.txt new file mode 100644 index 00000000..be6731c5 --- /dev/null +++ b/framework/src/main/resources/banner.txt @@ -0,0 +1,19 @@ + ___ ___ ___ ___ ________ ________ _______ ________ _____ ______ ___ __ ________ ________ ___ __ +|\ \ |\ \|\ \ |\ \ |\ _____\\ __ \|\ ___ \ |\ __ \|\ _ \ _ \|\ \ |\ \|\ __ \|\ __ \|\ \|\ \ +\ \ \ \ \ \ \ \ \ \ \ ____________\ \ \__/\ \ \|\ \ \ __/|\ \ \|\ \ \ \\\__\ \ \ \ \ \ \ \ \ \|\ \ \ \|\ \ \ \/ /|_ + \ \ \ \ \ \ \ \ \ \ \|\____________\ \ __\\ \ _ _\ \ \_|/_\ \ __ \ \ \\|__| \ \ \ \ __\ \ \ \ \\\ \ \ _ _\ \ ___ \ + \ \ \____\ \ \ \ \____\ \ \|____________|\ \ \_| \ \ \\ \\ \ \_|\ \ \ \ \ \ \ \ \ \ \ \ \|\__\_\ \ \ \\\ \ \ \\ \\ \ \\ \ \ + \ \_______\ \__\ \_______\ \__\ \ \__\ \ \__\\ _\\ \_______\ \__\ \__\ \__\ \ \__\ \____________\ \_______\ \__\\ _\\ \__\\ \__\ + \|_______|\|__|\|_______|\|__| \|__| \|__|\|__|\|_______|\|__|\|__|\|__| \|__|\|____________|\|_______|\|__|\|__|\|__| \|__| + + + + ___ ___ ___ ___ ________ ___ ___ ________ ________ + |\ \ |\ \|\ \ |\ \ |\ ____\|\ \|\ \|\ __ \|\ __ \ + \ \ \ \ \ \ \ \ \ \ \ ____________\ \ \___|\ \ \\\ \ \ \|\ \ \ \|\ \ + \ \ \ \ \ \ \ \ \ \ \|\____________\ \_____ \ \ __ \ \ \\\ \ \ ____\ + \ \ \____\ \ \ \ \____\ \ \|____________|\|____|\ \ \ \ \ \ \ \\\ \ \ \___| + \ \_______\ \__\ \_______\ \__\ ____\_\ \ \__\ \__\ \_______\ \__\ + \|_______|\|__|\|_______|\|__| |\_________\|__|\|__|\|_______|\|__| + \|_________| + diff --git a/framework/src/main/resources/hibernate.properties b/framework/src/main/resources/hibernate.properties new file mode 100644 index 00000000..c0d0fe46 --- /dev/null +++ b/framework/src/main/resources/hibernate.properties @@ -0,0 +1,2 @@ +# 自动生成数据库表引擎为InnoDB +hibernate.dialect.storage_engine=innodb diff --git a/framework/src/main/resources/lili-alipay/CSR/z171l91606.51mypc.cn.csr b/framework/src/main/resources/lili-alipay/CSR/z171l91606.51mypc.cn.csr new file mode 100644 index 00000000..d16c6275 --- /dev/null +++ b/framework/src/main/resources/lili-alipay/CSR/z171l91606.51mypc.cn.csr @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIC2DCCAcACAQAwgZIxDzANBgNVBAYMBuS4reWbvTEPMA0GA1UECAwG5YyX5Lqs +MQ8wDQYDVQQHDAbmtbfmt4AxLTArBgNVBAoMJOWMl+S6rOWuj+S4muaxh+aIkOen +keaKgOaciemZkOWFrOWPuDEPMA0GA1UECwwG5oqA5pyvMR0wGwYDVQQDExR6MTcx +bDkxNjA2LjUxbXlwYy5jbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AIfj8IYemfNMBLuN6o+rXTHYoCpKadfaYFDaKTCPdqZVnf0l6dBie9xqx7dxc+40 +T8NlylJdbnVzerKGjtOcRQ3xDZp/bcciL3T0MvZ0e/zP5/Zx4U9biuOtBaZbu4yx +i8pDFZAn4/rIgwf/tChPg2y1hs+JpDxQWvhA/1TxNTBH0k3fN7HBsDDmdabPdVVW +d245vR08vhOZYv3d/SWCymHvJW+20W3cZDYUBfuNyswQ5T6sWL3eopOwPOWkOfIF +U7eEVuVHUeVKAggdYTV5lSUQbVjKrHVh/AndaslAgNLX2Vl5RHmvI9+aOtXX+edz +zs8mLVRMHSV3Opd4aJCzzf8CAwEAAaAAMA0GCSqGSIb3DQEBBAUAA4IBAQASJKf5 +xBRd63zynwTWebIHKvJumWMI4jhS7Sk3SnRICc+ZihlM0K0R/kR00MpRkiLDu7j6 +uPWD/lcQwDDRzDsuN9iRHjpUiexSkgrtgX8BYHJkLTUOD2fuBpf8C0yonepeGM3B +jRsCS1O2BT1/pHVJTbCdqx95/U9Vwjaiddckh8LpFLUUB09i3XYsnbNzTCaIHp2K +/NqgkEptZswQfT2Vfiz1xrrtLZpaesnUaDryqUbg13QmB+cvkHxbJnyVkeFEDSTW +oAfmttYSDQ+Ihz5SjB2POSXNB95QkfzK+vkalEIO7kYBQ3YAojTBv2ORsy1trpgu +1erWy6VN36go9zfK +-----END CERTIFICATE REQUEST----- diff --git a/framework/src/main/resources/lili-alipay/CSR/z171l91606.51mypc.cn_公钥.txt b/framework/src/main/resources/lili-alipay/CSR/z171l91606.51mypc.cn_公钥.txt new file mode 100644 index 00000000..08ec401c --- /dev/null +++ b/framework/src/main/resources/lili-alipay/CSR/z171l91606.51mypc.cn_公钥.txt @@ -0,0 +1 @@ +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh+Pwhh6Z80wEu43qj6tdMdigKkpp19pgUNopMI92plWd/SXp0GJ73GrHt3Fz7jRPw2XKUl1udXN6soaO05xFDfENmn9txyIvdPQy9nR7/M/n9nHhT1uK460Fplu7jLGLykMVkCfj+siDB/+0KE+DbLWGz4mkPFBa+ED/VPE1MEfSTd83scGwMOZ1ps91VVZ3bjm9HTy+E5li/d39JYLKYe8lb7bRbdxkNhQF+43KzBDlPqxYvd6ik7A85aQ58gVTt4RW5UdR5UoCCB1hNXmVJRBtWMqsdWH8Cd1qyUCA0tfZWXlEea8j35o61df553POzyYtVEwdJXc6l3hokLPN/wIDAQAB \ No newline at end of file diff --git a/framework/src/main/resources/lili-alipay/CSR/z171l91606.51mypc.cn_私钥.txt b/framework/src/main/resources/lili-alipay/CSR/z171l91606.51mypc.cn_私钥.txt new file mode 100644 index 00000000..d881e959 --- /dev/null +++ b/framework/src/main/resources/lili-alipay/CSR/z171l91606.51mypc.cn_私钥.txt @@ -0,0 +1 @@ +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCH4/CGHpnzTAS7jeqPq10x2KAqSmnX2mBQ2ikwj3amVZ39JenQYnvcase3cXPuNE/DZcpSXW51c3qyho7TnEUN8Q2af23HIi909DL2dHv8z+f2ceFPW4rjrQWmW7uMsYvKQxWQJ+P6yIMH/7QoT4NstYbPiaQ8UFr4QP9U8TUwR9JN3zexwbAw5nWmz3VVVnduOb0dPL4TmWL93f0lgsph7yVvttFt3GQ2FAX7jcrMEOU+rFi93qKTsDzlpDnyBVO3hFblR1HlSgIIHWE1eZUlEG1Yyqx1YfwJ3WrJQIDS19lZeUR5ryPfmjrV1/nnc87PJi1UTB0ldzqXeGiQs83/AgMBAAECggEAd//IeZdyWY/qqfkThlVMUeU64r2do/h6j0tnjjNSEmaOwmqFg/hD6ov82zh7qIFXYnRz6rIJBfy5AAek/qcDr+ELI0CeH3hHsh6nFk4c405xR4ae8WYkSxOSZqqg/wgWKsF8vDUNtHr2gszCOcqlgwz2BDVG6+AFxS4Yi9kJIB0khnu8ClE+z35mplDvuDdAYH3/SrJMEdOcMEyE7+7PV1nuXSpyYWqa8UQL1pFvy2mD/mXnBXV3vQi8Qdcc3E2MTsZUg3k+2FYQANdsrYLofpZw28Go4hpWZpdw7BA8fYh/8Gl0is9omTgonj7EZF2MpIEikOhoGPT/Dfnxlgw6gQKBgQD6W6Qsi5pz851kuzQhgMWOAOyA9iKrrV9yfIID2jxrBLFk2rzQm2cRhid9+UgvWDAWJA3pwC448L7T6U9ku48kfI60oQh/P/YaTP3CKwPlzJdNk4FNHWhESjZ95qe4deJCOgKzN9qSKWJQFQNDN362/gWxuK/XMlimC6q3YEY8vwKBgQCK8+oynpgwMyi9GeDPsifsHzrlbEYK02YyQZ0ikUlSXyssx6Fzm4lULI9Yh2m5LLdeOLreyLGUXpf/PWdOf47OqzPUxBE7qTrLyKlyGl9vUESTXBXL2AMX44t79djatDHfFHycLpWBmmZ2KW1CR0oPJqxseHPnkHZR5c24eqZ+wQKBgQD3LQMp96rGT/9VNH6DlV92k/LsYBZa6RETN1ol85EzF68Mo+lHDCb2Zj0XYsah96+CLJaP8e2wyf+duPtpuMPagh9itpptB/kyflELIuz2NMgtDzTZre8DfUHPi6qLs9dvC1cfGkXxiGpIJnbSJjg08xcsH+t7y3k6dspVwfeWgQKBgAmnzVxbHOQsieYqCC8miBOmT+dlULCr4+wWFx8xzVCGAHAKJzPrWO2acxOJxVTywIMxmfqlI4HV2OYJlEYmIvxFdUKRpUjqB4XlmbCuGHeZskozGeiiSAS+8RBaEwmPf++qcsY/jnAFFJ2dX1Q9s+29mDmpXRR4O2CRgWwSScOBAoGAWkovHm4T7KvujeN2ZzQ9b1u2BosBMW14J8C0RgW6hR1Twjk61pv7tjcSfZdQmD1ss8aqR2byFGJq86vRjE4tsFbPCSitF01xRbvSExGAb8ZI5fJoNiQPTuIvkW7chvrzj2KWuffkjcivhQJ6ExlWco3ncqRQ6QMPoaE0xNPqGuE= \ No newline at end of file diff --git a/framework/src/main/resources/lili-alipay/alipayCertPublicKey_RSA2.crt b/framework/src/main/resources/lili-alipay/alipayCertPublicKey_RSA2.crt new file mode 100644 index 00000000..50817761 --- /dev/null +++ b/framework/src/main/resources/lili-alipay/alipayCertPublicKey_RSA2.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDsjCCApqgAwIBAgIQICASFdUt4pOdF6AF49Xo2TANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE +BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs +YXNzIDIgUjEwHhcNMjAxMjE1MTAzMDUwWhcNMjIxMjE1MTAzMDUwWjCBkjELMAkGA1UEBhMCQ04x +LTArBgNVBAoMJOWMl+S6rOWuj+S4muaxh+aIkOenkeaKgOaciemZkOWFrOWPuDEPMA0GA1UECwwG +QWxpcGF5MUMwQQYDVQQDDDrmlK/ku5jlrp0o5Lit5Zu9Kee9kee7nOaKgOacr+aciemZkOWFrOWP +uC0yMDg4NzMxNDAxODM4MzQ2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjTjdq5cN +F/7o1pFf8woNQkWget8kmJb/pKwbZWUWrH8lv+BUzCJIrZDC/QnlPGEmC6D8zHVs8CTm73XuRzmu +y6zapUYAhY4Zwd9NC1izZxSkuS7KHfKltaCcRCfEbIQpILL7rTIazBDlgBYRFFaeWF1g1uXrgzt/ +axTg8D/sQN2QwkpdAue1WyXo8OTIxULdkNSISw6BRrTisncrD6TwXtth+okVDEaU5JUrtTBzapdM +WCGqM4dIT9nSAZ7ibRNAbtC41rCHYBGELtDcSkrrLpWyiGRTnyJ9HgdiQlhY5SG1nKmPvrRuTpgR +LZqGAC2rmXv/ymDtYa6Ldw9zB64yDwIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCA/gwDQYJKoZIhvcN +AQELBQADggEBAGJzZpqeMk0/TbgEaAqXe33w7hhYRDt3K70r7R4J4U5DXm3gbXX6Mw/wOq2/ZR4/ +BswxkwbOsAxkAG2Be3ShjW4FD1r54bWZJNYpXKRUEoCEZvIeLH+bRTCnr9y5NFxTTXXiT2NS6Oox +JaSDymQHX0DrgiA+9L/8xMuibe16/zHcSTMUSrCk1CHhw8QDFHtxUa4pe6DwRsqVoQbfp+Rw9wM8 +7FdHpAXT5Mnchl5d0jywTAa1EAFfZ6hty9/XiFI9ferUdqMUdI8SnTe6tx67+QIY8lOyUh3ZmITM +GnoWdjLdHlSHHCrTA7SauhylhwhsjPaLCdTq8q2jwQhmmhwNVnM= +-----END CERTIFICATE----- diff --git a/framework/src/main/resources/lili-alipay/alipayRootCert.crt b/framework/src/main/resources/lili-alipay/alipayRootCert.crt new file mode 100644 index 00000000..d370e5b3 --- /dev/null +++ b/framework/src/main/resources/lili-alipay/alipayRootCert.crt @@ -0,0 +1,88 @@ +-----BEGIN CERTIFICATE----- +MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG +EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw +MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO +UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE +MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT +V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti +W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ +MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b +53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI +pDoiVhsLwg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE +BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0 +MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV +BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk +rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2 +xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp +dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6 +vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl +YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1 +Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H +DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98 +SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG +PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe +9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC +AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90 +tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy +nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf +tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq +JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3 +IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW +05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41 +T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI +kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop +PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N +1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y +jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02 +77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi +kT9qhqn+lw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG +EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0 +WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE +CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp +YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU +WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt +rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ +4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2 +zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg +wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH +Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF +BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM +E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg +MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq +MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp +bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv +b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV +nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5 +4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg +wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw +WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN +z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g +KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA +uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp +emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3 +U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I +UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn +DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU +1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX +Yf4Zr0fJsGuv +-----END CERTIFICATE----- \ No newline at end of file diff --git a/framework/src/main/resources/lili-alipay/appCertPublicKey_2021002107649773.crt b/framework/src/main/resources/lili-alipay/appCertPublicKey_2021002107649773.crt new file mode 100644 index 00000000..3d76730b --- /dev/null +++ b/framework/src/main/resources/lili-alipay/appCertPublicKey_2021002107649773.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEsTCCA5mgAwIBAgIQICASFoAHYjkCdKIRVlK0fjANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE +BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs +YXNzIDEgUjEwHhcNMjAxMjE2MTEyNTUyWhcNMjIxMjE2MTEyNTUyWjB5MQswCQYDVQQGEwJDTjEt +MCsGA1UECgwk5YyX5Lqs5a6P5Lia5rGH5oiQ56eR5oqA5pyJ6ZmQ5YWs5Y+4MQ8wDQYDVQQLDAZB +bGlwYXkxKjAoBgNVBAMMITIwODg3MzE0MDE4MzgzNDYtMjAyMTAwMjEwNzY0OTc3MzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAIfj8IYemfNMBLuN6o+rXTHYoCpKadfaYFDaKTCPdqZV +nf0l6dBie9xqx7dxc+40T8NlylJdbnVzerKGjtOcRQ3xDZp/bcciL3T0MvZ0e/zP5/Zx4U9biuOt +BaZbu4yxi8pDFZAn4/rIgwf/tChPg2y1hs+JpDxQWvhA/1TxNTBH0k3fN7HBsDDmdabPdVVWd245 +vR08vhOZYv3d/SWCymHvJW+20W3cZDYUBfuNyswQ5T6sWL3eopOwPOWkOfIFU7eEVuVHUeVKAggd +YTV5lSUQbVjKrHVh/AndaslAgNLX2Vl5RHmvI9+aOtXX+edzzs8mLVRMHSV3Opd4aJCzzf8CAwEA +AaOCASkwggElMB8GA1UdIwQYMBaAFHEH4gRhFuTl8mXrMQ/J4PQ8mtWRMB0GA1UdDgQWBBS9rOZn +eNQ2jrdlC4nkw8nw+Rn3xDBABgNVHSAEOTA3MDUGB2CBHAFuAQEwKjAoBggrBgEFBQcCARYcaHR0 +cDovL2NhLmFsaXBheS5jb20vY3BzLnBkZjAOBgNVHQ8BAf8EBAMCBsAwLwYDVR0fBCgwJjAkoCKg +IIYeaHR0cDovL2NhLmFsaXBheS5jb20vY3JsNDguY3JsMGAGCCsGAQUFBwEBBFQwUjAoBggrBgEF +BQcwAoYcaHR0cDovL2NhLmFsaXBheS5jb20vY2E2LmNlcjAmBggrBgEFBQcwAYYaaHR0cDovL2Nh +LmFsaXBheS5jb206ODM0MC8wDQYJKoZIhvcNAQELBQADggEBAEkTDAVJ15SzJbSq85qASGxHPOl6 +XUrgZQRQJX9zkgkXd98O5PQmoEgLx9EssAUaUuEsEUkIueDsp8uMiw6Ndj73pNJs6uoYiTVc9Bbk +fE4Cl+zxHqJRat0/ifSnEKnn668d2Mwcrlm6ygbsPi4dm9cVTy+a/09LQH+LD3jyL0/SetBYvNes +b//0xCTNNqRkedsJjjs01oR1P6RWfkoeF6yICGblsHkKUgekKzCEAo2B1tePi1zY2hF+51GPLTDk +sH+8gqJRtJqqQA2YoghxXen61Tw4DFx6t+z1YmAOJXnTfjnv7W+9fmBA4acHqrU6nq/rxoSl5uuK +b2OxNEwyTfs= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/framework/src/main/resources/lili-wechat/apiclient_cert.p12 b/framework/src/main/resources/lili-wechat/apiclient_cert.p12 new file mode 100644 index 00000000..8615edff Binary files /dev/null and b/framework/src/main/resources/lili-wechat/apiclient_cert.p12 differ diff --git a/framework/src/main/resources/lili-wechat/apiclient_cert.pem b/framework/src/main/resources/lili-wechat/apiclient_cert.pem new file mode 100644 index 00000000..c0502c8a --- /dev/null +++ b/framework/src/main/resources/lili-wechat/apiclient_cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID8DCCAtigAwIBAgIUMHAf6+fRKNPVyiRvVOwGwAiFCfIwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjAwMTEzMDMyMDQwWhcNMjUwMTExMDMyMDQwWjCBgTETMBEGA1UEAwwK +MTU3NDE4MTIxMTEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMS0wKwYDVQQL +DCTljJfkuqzlro/kuJrmsYfmiJDnp5HmioDmnInpmZDlhazlj7gxCzAJBgNVBAYM +AkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAPUtUG5V+OoOxXsoYALT6xY4xJqhy2zlGwQZ238cl4e4lM2SWFxx+8AW +7Vu1cPx+c1xqkWQqCxhD+UYT7Tap9o5TlCTaQ5ZCkU3X1o8rtpJTR83eHHJUELqu ++SB0v+8Fhbe8ekb+4b3mf31aqzla3bglfpxsj8WofmF3ZM2CPw1L7zrt+F9GbdjA +ObipcLLeq0OoJg9r5oJPEPZPQ3/vkAoDf+8Cyat9sUoyNeui+8aO7qliBV6Q7SiY +NfsGg3xe8THtNTxJtMj/NAe7blGciNvIPLV/9ipS0lOCI0tv/8kCcXaWxjLX8XUB +Ei2bkKSSzVpNZh7OH9RzLlXGzXpQrhMCAwEAAaOBgTB/MAkGA1UdEwQCMAAwCwYD +VR0PBAQDAgTwMGUGA1UdHwReMFwwWqBYoFaGVGh0dHA6Ly9ldmNhLml0cnVzLmNv +bS5jbi9wdWJsaWMvaXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1 +NDk4NDZDMDFDM0U4RUJEMjANBgkqhkiG9w0BAQsFAAOCAQEAu38/E6xw/cr+SDQA +KFFVByBtMIBcISHKAuMdyM1LXefvpTqDivtcn/ZiEI96y47h48P+QkLseoFw/k+q +r8/+02pBZHMlnRpXeE0DexoB2pNR+lGAmhDll0qHL9ycSj1tS4/auFlDrljY/ike +5t40x67hfcKfFKUhv1zhekoX38ri4UTR0T/OqzPii2Zr7CTbGEoSDXy7Xjolp6BL +vrbNkwzchJbLRQRmyXILJ1MWzAGJNafLUKChBa/pxnxp+KEvExQ/LN0/5T/DfYev +XFjRlGGfIzVUEopo2O56/phO4y+VMspsuos2TeFunZInCQDoUo0AynUhi4Ipqjwe +M8Pjow== +-----END CERTIFICATE----- diff --git a/framework/src/main/resources/lili-wechat/apiclient_key.pem b/framework/src/main/resources/lili-wechat/apiclient_key.pem new file mode 100644 index 00000000..c3915749 --- /dev/null +++ b/framework/src/main/resources/lili-wechat/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQD1LVBuVfjqDsV7 +KGAC0+sWOMSaocts5RsEGdt/HJeHuJTNklhccfvAFu1btXD8fnNcapFkKgsYQ/lG +E+02qfaOU5Qk2kOWQpFN19aPK7aSU0fN3hxyVBC6rvkgdL/vBYW3vHpG/uG95n99 +Wqs5Wt24JX6cbI/FqH5hd2TNgj8NS+867fhfRm3YwDm4qXCy3qtDqCYPa+aCTxD2 +T0N/75AKA3/vAsmrfbFKMjXrovvGju6pYgVekO0omDX7BoN8XvEx7TU8SbTI/zQH +u25RnIjbyDy1f/YqUtJTgiNLb//JAnF2lsYy1/F1ARItm5Ckks1aTWYezh/Ucy5V +xs16UK4TAgMBAAECggEBAMdEUherFkidQtHrKcWCmZIRB3GqSFy0BHYb0fMIuPud +AIvoApr4JSWlPLsJx8/fskENeCeRqDzLmkDjlyuTjgl1UnV1U/M/HTvITeFFJPje +aQCgIjylqIciCObhHNHWiiygKj2jkLO4MCwg0gMmoAe3qaTW8y8x1629zRbeYgKZ ++pIt4ZK/Ditj1P+fn9YfP+5poYsYoEm2G5MgxJZ9Z8Y4X0HFNw5RZVUeAVHskmke +dPsZlR9JdncCaXwh1nd7guWpARjqykKoPBcZzzJ2/PpkEZvlp36mMRRhisUANit+ +Irjnh0sA7Di8R0+IqPiuSegN3n8dRfuxfq+A2lGeLZkCgYEA+4e9h7fgpyTiAs/s +8La+NE+dXK4RamrHH/zZun72SR3VLMWrgEQ13p1jVJ4tGOvGdNbWMiYSGGwYubrL +sL+W1pzKpFL+dV7SEZ84nB13WiE7bjhXtTcoOKT2P57JszqZe1tKTBWaLycWPy8e +mo72NQJg2QLahi36KuHCkpAa5NcCgYEA+Yir+VLW/Y2bL5oA7/qrjfsw5RbPI1DV +O4k6mARaPXEb5U+upitZtBr7nRu8i2/Yw3H/VJV3vjyGZ2J/ApTI8DN2+k+LpQKN +nBCd0heCXy69GU+zB5ZAHmkluCWxWjZF8VHLaBzTrmOoboR4M2PfuYTKZCV5Qkxs +F7w1DUwg3SUCgYEAsb23Abu/JX2btE8H6QXJnJ+R3JcouWFg+sqrdO0YzowlQrEg +QtBwnLHtKjNoPM8HezMBQ4jBir6ALmbAX1LTnS5IyBvZskjh+w/fafosh+Up4PYx +jF3Ar15qoIRtlZhYMuJMuz0cqitdviJm5uMVOlpG47LvhpfgywKjgvwP9RsCgYEA +s7sBQ1+rgo1QTYT7T96ZWnamzRsUrzc1nuBE1+Gqrb6efRhbj0yCG/ujZ5Hmx/gT +hRLC+cV+rgMiO6zyFoXcBUbMV5ab2opwPQyq3/wW5z/e7DA/nyNVo5HQnUZo67VT +w3FKtP6uaVlWo+O+QCWbqyA+NsvW/y3UXXQuMhm3QhECgYEAhy6hubIfUw05Msv0 +CK5VTxU/B+uE+WZo1PoYUeYfU1KUYsELyb/XbtiAYuI5z3lw75vLAuiYEUtc28Kb +ZSUv1z36DNGuRuj5wOAr1cfpMmqKA6IZpyAEdRZC8XXvl1+U3NZ3C2qeyGMJ06/N +YtbBEJ3p2CaDuUr3AFEDDNg1WKw= +-----END PRIVATE KEY----- diff --git a/framework/src/main/resources/lili-wechat/platform_cert.p12 b/framework/src/main/resources/lili-wechat/platform_cert.p12 new file mode 100644 index 00000000..e7c20023 --- /dev/null +++ b/framework/src/main/resources/lili-wechat/platform_cert.p12 @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3DCCAsSgAwIBAgIUV+as/5BHXy5fyCESrE6g2onbgtAwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjAwMTEzMDMyMDQwWhcNMjUwMTExMDMyMDQwWjBuMRgwFgYDVQQDDA9U +ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl +bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo +ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1cnBvpt8zKcj9LNen +u/fmqC9FYDmC7EJ8OWpEdUFIdI5jmzwj8enLqVUIcboObA+1B9tqQBhQEIi+mBdd +sIRk9+YXFhmNbOPZiRiFnIC5B2tCq84ieDr7WZZAzRwUH1585Tiv3CsOTBib2xQs +hSMwglvnHsXCjlqUybejOk5FMahgm0ejgs8omRm3nBfcMfilOxXH4UUP56cfeDYO +YdaAQhuJ5bDILnQYm2SD0d+aN8VV5d0WjXW01NFLzxpyMUYHEXNcDC7pLjgNVgKl +0sk5TJlIUgxEIRli8RdfE1qrcnBqYKDgym4LfHCY6T2cBc0PQY4Q7cww8G7jHn4I +snjDAgMBAAGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIE8DBlBgNVHR8EXjBc +MFqgWKBWhlRodHRwOi8vZXZjYS5pdHJ1cy5jb20uY24vcHVibGljL2l0cnVzY3Js +P0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3NTQ5ODQ2QzAxQzNFOEVCRDIwDQYJ +KoZIhvcNAQELBQADggEBAJLIwFYvFLaT3BECVuapQu1t3Od+Oy6EY/PjLOhYlo5k +ALXWPJ5v8EsisFQhSRsZMWd+ynr3/tnLXcfU+fQMjU8lvYN3mXSp2fnSlzzublZB +fL06nt5PWBnUgAASDfC0MCEBLrIrRX/yiNrzS6tkjc6NYQJ1i7jovaC++6TjdyNp +DO2aasitXV5TUF1a2TKsoqcWlFO9X6cP5Dwqc81aeFU3ncQNzDcpauXU/5pmF5Jd +2nNaJnuo0uIY43szcpzUHvCNHX7lQZUQulOThW8EspLJZWaPbawvn/tCOL7Vnkko +APr31mGbXOZV5lNNSLuoTn5/bueLP/TkrcpmM9Xw+9Y= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/framework/src/main/resources/lili-wechat/证书使用说明.txt b/framework/src/main/resources/lili-wechat/证书使用说明.txt new file mode 100644 index 00000000..041befb4 --- /dev/null +++ b/framework/src/main/resources/lili-wechat/证书使用说明.txt @@ -0,0 +1,18 @@ +欢迎使用微信支付! +附件中的三份文件(证书pkcs12格式、证书pem格式、证书密钥pem格式),为接口中强制要求时需携带的证书文件。 +证书属于敏感信息,请妥善保管不要泄露和被他人复制。 +不同开发语言下的证书格式不同,以下为说明指引: + 证书pkcs12格式(apiclient_cert.p12) + 包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份 + 部分安全性要求较高的API需要使用该证书来确认您的调用身份 + windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户号(如:1900006031) + 证书pem格式(apiclient_cert.pem) + 从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制 + 部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供 + 您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem + 证书密钥pem格式(apiclient_key.pem) + 从apiclient_cert.p12中导出密钥部分的文件,为pem格式 + 部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供 + 您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem +备注说明: + 由于绝大部分操作系统已内置了微信支付服务器证书的根CA证书, 2018年3月6日后, 不再提供CA证书文件(rootca.pem)下载 \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ArticleCategoryMapper.xml b/framework/src/main/resources/mapper/ArticleCategoryMapper.xml new file mode 100644 index 00000000..ce5612b9 --- /dev/null +++ b/framework/src/main/resources/mapper/ArticleCategoryMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ArticleMapper.xml b/framework/src/main/resources/mapper/ArticleMapper.xml new file mode 100644 index 00000000..e005dc3f --- /dev/null +++ b/framework/src/main/resources/mapper/ArticleMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/BillMapper.xml b/framework/src/main/resources/mapper/BillMapper.xml new file mode 100644 index 00000000..97b15e6e --- /dev/null +++ b/framework/src/main/resources/mapper/BillMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/BrandMapper.xml b/framework/src/main/resources/mapper/BrandMapper.xml new file mode 100644 index 00000000..fc5ee370 --- /dev/null +++ b/framework/src/main/resources/mapper/BrandMapper.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/CategoryBrandMapper.xml b/framework/src/main/resources/mapper/CategoryBrandMapper.xml new file mode 100644 index 00000000..b4edf6f7 --- /dev/null +++ b/framework/src/main/resources/mapper/CategoryBrandMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/CategoryParameterGroupMapper.xml b/framework/src/main/resources/mapper/CategoryParameterGroupMapper.xml new file mode 100644 index 00000000..eb629109 --- /dev/null +++ b/framework/src/main/resources/mapper/CategoryParameterGroupMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/CategorySpecificationMapper.xml b/framework/src/main/resources/mapper/CategorySpecificationMapper.xml new file mode 100644 index 00000000..f622e1a5 --- /dev/null +++ b/framework/src/main/resources/mapper/CategorySpecificationMapper.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ConnectConfigMapper.xml b/framework/src/main/resources/mapper/ConnectConfigMapper.xml new file mode 100644 index 00000000..dbd74bbf --- /dev/null +++ b/framework/src/main/resources/mapper/ConnectConfigMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ConnectMapper.xml b/framework/src/main/resources/mapper/ConnectMapper.xml new file mode 100644 index 00000000..7b72b2cd --- /dev/null +++ b/framework/src/main/resources/mapper/ConnectMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/CouponMapper.xml b/framework/src/main/resources/mapper/CouponMapper.xml new file mode 100644 index 00000000..9b18097f --- /dev/null +++ b/framework/src/main/resources/mapper/CouponMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/DepartmentMapper.xml b/framework/src/main/resources/mapper/DepartmentMapper.xml new file mode 100644 index 00000000..20af930a --- /dev/null +++ b/framework/src/main/resources/mapper/DepartmentMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/DepartmentRoleMapper.xml b/framework/src/main/resources/mapper/DepartmentRoleMapper.xml new file mode 100644 index 00000000..4241a205 --- /dev/null +++ b/framework/src/main/resources/mapper/DepartmentRoleMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/DepositLogMapper.xml b/framework/src/main/resources/mapper/DepositLogMapper.xml new file mode 100644 index 00000000..470c65eb --- /dev/null +++ b/framework/src/main/resources/mapper/DepositLogMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/DistributionCashMapper.xml b/framework/src/main/resources/mapper/DistributionCashMapper.xml new file mode 100644 index 00000000..5998314d --- /dev/null +++ b/framework/src/main/resources/mapper/DistributionCashMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/DistributionGoodsMapper.xml b/framework/src/main/resources/mapper/DistributionGoodsMapper.xml new file mode 100644 index 00000000..199f46da --- /dev/null +++ b/framework/src/main/resources/mapper/DistributionGoodsMapper.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/DistributionOrderMapper.xml b/framework/src/main/resources/mapper/DistributionOrderMapper.xml new file mode 100644 index 00000000..deb20d73 --- /dev/null +++ b/framework/src/main/resources/mapper/DistributionOrderMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ExpressPlatformMapper.xml b/framework/src/main/resources/mapper/ExpressPlatformMapper.xml new file mode 100644 index 00000000..4dbc5f38 --- /dev/null +++ b/framework/src/main/resources/mapper/ExpressPlatformMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/FocusPictureMapper.xml b/framework/src/main/resources/mapper/FocusPictureMapper.xml new file mode 100644 index 00000000..07c95c92 --- /dev/null +++ b/framework/src/main/resources/mapper/FocusPictureMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/FootprintMapper.xml b/framework/src/main/resources/mapper/FootprintMapper.xml new file mode 100644 index 00000000..9be92429 --- /dev/null +++ b/framework/src/main/resources/mapper/FootprintMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/FullDiscountMapper.xml b/framework/src/main/resources/mapper/FullDiscountMapper.xml new file mode 100644 index 00000000..d62e32ce --- /dev/null +++ b/framework/src/main/resources/mapper/FullDiscountMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/GoodsCollectionMapper.xml b/framework/src/main/resources/mapper/GoodsCollectionMapper.xml new file mode 100644 index 00000000..4ff9b1f1 --- /dev/null +++ b/framework/src/main/resources/mapper/GoodsCollectionMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/GoodsGalleryMapper.xml b/framework/src/main/resources/mapper/GoodsGalleryMapper.xml new file mode 100644 index 00000000..882df260 --- /dev/null +++ b/framework/src/main/resources/mapper/GoodsGalleryMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/GoodsMapper.xml b/framework/src/main/resources/mapper/GoodsMapper.xml new file mode 100644 index 00000000..864362a0 --- /dev/null +++ b/framework/src/main/resources/mapper/GoodsMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/GoodsParamsMapper.xml b/framework/src/main/resources/mapper/GoodsParamsMapper.xml new file mode 100644 index 00000000..9472dc41 --- /dev/null +++ b/framework/src/main/resources/mapper/GoodsParamsMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/GoodsSkuMapper.xml b/framework/src/main/resources/mapper/GoodsSkuMapper.xml new file mode 100644 index 00000000..4ca8bb3f --- /dev/null +++ b/framework/src/main/resources/mapper/GoodsSkuMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/GoodsUnitMapper.xml b/framework/src/main/resources/mapper/GoodsUnitMapper.xml new file mode 100644 index 00000000..516ea99e --- /dev/null +++ b/framework/src/main/resources/mapper/GoodsUnitMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/LogisticsMapper.xml b/framework/src/main/resources/mapper/LogisticsMapper.xml new file mode 100644 index 00000000..48045bad --- /dev/null +++ b/framework/src/main/resources/mapper/LogisticsMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberAddressMapper.xml b/framework/src/main/resources/mapper/MemberAddressMapper.xml new file mode 100644 index 00000000..12579d10 --- /dev/null +++ b/framework/src/main/resources/mapper/MemberAddressMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberCouponMapper.xml b/framework/src/main/resources/mapper/MemberCouponMapper.xml new file mode 100644 index 00000000..d1a02fce --- /dev/null +++ b/framework/src/main/resources/mapper/MemberCouponMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberEvaluationMapper.xml b/framework/src/main/resources/mapper/MemberEvaluationMapper.xml new file mode 100644 index 00000000..26a0befb --- /dev/null +++ b/framework/src/main/resources/mapper/MemberEvaluationMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberMapper.xml b/framework/src/main/resources/mapper/MemberMapper.xml new file mode 100644 index 00000000..6dcff501 --- /dev/null +++ b/framework/src/main/resources/mapper/MemberMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberNoticeLogMapper.xml b/framework/src/main/resources/mapper/MemberNoticeLogMapper.xml new file mode 100644 index 00000000..5ded7a70 --- /dev/null +++ b/framework/src/main/resources/mapper/MemberNoticeLogMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberNoticeMapper.xml b/framework/src/main/resources/mapper/MemberNoticeMapper.xml new file mode 100644 index 00000000..f418e98c --- /dev/null +++ b/framework/src/main/resources/mapper/MemberNoticeMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberNoticeSenterMapper.xml b/framework/src/main/resources/mapper/MemberNoticeSenterMapper.xml new file mode 100644 index 00000000..caf65eb8 --- /dev/null +++ b/framework/src/main/resources/mapper/MemberNoticeSenterMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberPointsHistoryMapper.xml b/framework/src/main/resources/mapper/MemberPointsHistoryMapper.xml new file mode 100644 index 00000000..1f72c95d --- /dev/null +++ b/framework/src/main/resources/mapper/MemberPointsHistoryMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberSign.xml b/framework/src/main/resources/mapper/MemberSign.xml new file mode 100644 index 00000000..7a69fe9f --- /dev/null +++ b/framework/src/main/resources/mapper/MemberSign.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberStatisticsDataMapper.xml b/framework/src/main/resources/mapper/MemberStatisticsDataMapper.xml new file mode 100644 index 00000000..343ba5fe --- /dev/null +++ b/framework/src/main/resources/mapper/MemberStatisticsDataMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MemberWalletMapper.xml b/framework/src/main/resources/mapper/MemberWalletMapper.xml new file mode 100644 index 00000000..cad94a66 --- /dev/null +++ b/framework/src/main/resources/mapper/MemberWalletMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/MessageMapper.xml b/framework/src/main/resources/mapper/MessageMapper.xml new file mode 100644 index 00000000..6f02e518 --- /dev/null +++ b/framework/src/main/resources/mapper/MessageMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/OrderItemMapper.xml b/framework/src/main/resources/mapper/OrderItemMapper.xml new file mode 100644 index 00000000..40032662 --- /dev/null +++ b/framework/src/main/resources/mapper/OrderItemMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/OrderLogMapper.xml b/framework/src/main/resources/mapper/OrderLogMapper.xml new file mode 100644 index 00000000..7b87b503 --- /dev/null +++ b/framework/src/main/resources/mapper/OrderLogMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/OrderMapper.xml b/framework/src/main/resources/mapper/OrderMapper.xml new file mode 100644 index 00000000..a41b6c94 --- /dev/null +++ b/framework/src/main/resources/mapper/OrderMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PageMapper.xml b/framework/src/main/resources/mapper/PageMapper.xml new file mode 100644 index 00000000..180dec09 --- /dev/null +++ b/framework/src/main/resources/mapper/PageMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ParametersMapper.xml b/framework/src/main/resources/mapper/ParametersMapper.xml new file mode 100644 index 00000000..88e60be1 --- /dev/null +++ b/framework/src/main/resources/mapper/ParametersMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PaymentLogMapper.xml b/framework/src/main/resources/mapper/PaymentLogMapper.xml new file mode 100644 index 00000000..1af3e778 --- /dev/null +++ b/framework/src/main/resources/mapper/PaymentLogMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PermissionMapper.xml b/framework/src/main/resources/mapper/PermissionMapper.xml new file mode 100644 index 00000000..1a7ba370 --- /dev/null +++ b/framework/src/main/resources/mapper/PermissionMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PintuanMapper.xml b/framework/src/main/resources/mapper/PintuanMapper.xml new file mode 100644 index 00000000..c7f2ad03 --- /dev/null +++ b/framework/src/main/resources/mapper/PintuanMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PlatformSettingMapper.xml b/framework/src/main/resources/mapper/PlatformSettingMapper.xml new file mode 100644 index 00000000..093d2318 --- /dev/null +++ b/framework/src/main/resources/mapper/PlatformSettingMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PointLogMapper.xml b/framework/src/main/resources/mapper/PointLogMapper.xml new file mode 100644 index 00000000..4c1f1611 --- /dev/null +++ b/framework/src/main/resources/mapper/PointLogMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PromotionGoodsMapper.xml b/framework/src/main/resources/mapper/PromotionGoodsMapper.xml new file mode 100644 index 00000000..6f9e25ea --- /dev/null +++ b/framework/src/main/resources/mapper/PromotionGoodsMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PurchaseOrderItemMapper.xml b/framework/src/main/resources/mapper/PurchaseOrderItemMapper.xml new file mode 100644 index 00000000..819b75e4 --- /dev/null +++ b/framework/src/main/resources/mapper/PurchaseOrderItemMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PurchaseOrderMapper.xml b/framework/src/main/resources/mapper/PurchaseOrderMapper.xml new file mode 100644 index 00000000..cd72f34b --- /dev/null +++ b/framework/src/main/resources/mapper/PurchaseOrderMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PurchaseQuotedItemMapper.xml b/framework/src/main/resources/mapper/PurchaseQuotedItemMapper.xml new file mode 100644 index 00000000..bf83a637 --- /dev/null +++ b/framework/src/main/resources/mapper/PurchaseQuotedItemMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/PurchaseQuotedMapper.xml b/framework/src/main/resources/mapper/PurchaseQuotedMapper.xml new file mode 100644 index 00000000..7572cead --- /dev/null +++ b/framework/src/main/resources/mapper/PurchaseQuotedMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ReceiptMapper.xml b/framework/src/main/resources/mapper/ReceiptMapper.xml new file mode 100644 index 00000000..559fe312 --- /dev/null +++ b/framework/src/main/resources/mapper/ReceiptMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/RechargeMapper.xml b/framework/src/main/resources/mapper/RechargeMapper.xml new file mode 100644 index 00000000..a393e9f8 --- /dev/null +++ b/framework/src/main/resources/mapper/RechargeMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/RefundLogMapper.xml b/framework/src/main/resources/mapper/RefundLogMapper.xml new file mode 100644 index 00000000..d5fed122 --- /dev/null +++ b/framework/src/main/resources/mapper/RefundLogMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/RegionMapper.xml b/framework/src/main/resources/mapper/RegionMapper.xml new file mode 100644 index 00000000..fe38f09b --- /dev/null +++ b/framework/src/main/resources/mapper/RegionMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/RoleMapper.xml b/framework/src/main/resources/mapper/RoleMapper.xml new file mode 100644 index 00000000..2731ffa3 --- /dev/null +++ b/framework/src/main/resources/mapper/RoleMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/RoleMenuMapper.xml b/framework/src/main/resources/mapper/RoleMenuMapper.xml new file mode 100644 index 00000000..586913d5 --- /dev/null +++ b/framework/src/main/resources/mapper/RoleMenuMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/SeckillApplyMapper.xml b/framework/src/main/resources/mapper/SeckillApplyMapper.xml new file mode 100644 index 00000000..c6a1c7f1 --- /dev/null +++ b/framework/src/main/resources/mapper/SeckillApplyMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/SeckillMapper.xml b/framework/src/main/resources/mapper/SeckillMapper.xml new file mode 100644 index 00000000..467d68ae --- /dev/null +++ b/framework/src/main/resources/mapper/SeckillMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/SensitiveWordsMapper.xml b/framework/src/main/resources/mapper/SensitiveWordsMapper.xml new file mode 100644 index 00000000..6ce6ff07 --- /dev/null +++ b/framework/src/main/resources/mapper/SensitiveWordsMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ServiceNoticeMapper.xml b/framework/src/main/resources/mapper/ServiceNoticeMapper.xml new file mode 100644 index 00000000..50eff690 --- /dev/null +++ b/framework/src/main/resources/mapper/ServiceNoticeMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ShippingAddressMapper.xml b/framework/src/main/resources/mapper/ShippingAddressMapper.xml new file mode 100644 index 00000000..8b80fce0 --- /dev/null +++ b/framework/src/main/resources/mapper/ShippingAddressMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/ShortLinkMapper.xml b/framework/src/main/resources/mapper/ShortLinkMapper.xml new file mode 100644 index 00000000..7c6e4c6a --- /dev/null +++ b/framework/src/main/resources/mapper/ShortLinkMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/SiteNavigationMapper.xml b/framework/src/main/resources/mapper/SiteNavigationMapper.xml new file mode 100644 index 00000000..d91ffe98 --- /dev/null +++ b/framework/src/main/resources/mapper/SiteNavigationMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/SpecValuesMapper.xml b/framework/src/main/resources/mapper/SpecValuesMapper.xml new file mode 100644 index 00000000..bd9c7a25 --- /dev/null +++ b/framework/src/main/resources/mapper/SpecValuesMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/SpecificationMapper.xml b/framework/src/main/resources/mapper/SpecificationMapper.xml new file mode 100644 index 00000000..e7b1d8b2 --- /dev/null +++ b/framework/src/main/resources/mapper/SpecificationMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/StoreAddressMapper.xml b/framework/src/main/resources/mapper/StoreAddressMapper.xml new file mode 100644 index 00000000..73d8604a --- /dev/null +++ b/framework/src/main/resources/mapper/StoreAddressMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/StoreCollectionMapper.xml b/framework/src/main/resources/mapper/StoreCollectionMapper.xml new file mode 100644 index 00000000..0c8a40b6 --- /dev/null +++ b/framework/src/main/resources/mapper/StoreCollectionMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/StoreFlowMapper.xml b/framework/src/main/resources/mapper/StoreFlowMapper.xml new file mode 100644 index 00000000..4d526cba --- /dev/null +++ b/framework/src/main/resources/mapper/StoreFlowMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/StoreLogisticsMapper.xml b/framework/src/main/resources/mapper/StoreLogisticsMapper.xml new file mode 100644 index 00000000..9df71ce2 --- /dev/null +++ b/framework/src/main/resources/mapper/StoreLogisticsMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/StoreMapper.xml b/framework/src/main/resources/mapper/StoreMapper.xml new file mode 100644 index 00000000..a1be516a --- /dev/null +++ b/framework/src/main/resources/mapper/StoreMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/TradeMapper.xml b/framework/src/main/resources/mapper/TradeMapper.xml new file mode 100644 index 00000000..be1e03c3 --- /dev/null +++ b/framework/src/main/resources/mapper/TradeMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/UserRoleMapper.xml b/framework/src/main/resources/mapper/UserRoleMapper.xml new file mode 100644 index 00000000..0c6da826 --- /dev/null +++ b/framework/src/main/resources/mapper/UserRoleMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/VerificationSourceMapper.xml b/framework/src/main/resources/mapper/VerificationSourceMapper.xml new file mode 100644 index 00000000..c80edca5 --- /dev/null +++ b/framework/src/main/resources/mapper/VerificationSourceMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/WechatMPMessageMapper.xml b/framework/src/main/resources/mapper/WechatMPMessageMapper.xml new file mode 100644 index 00000000..9d639de1 --- /dev/null +++ b/framework/src/main/resources/mapper/WechatMPMessageMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/mapper/WechatMessageMapper.xml b/framework/src/main/resources/mapper/WechatMessageMapper.xml new file mode 100644 index 00000000..ed6244e7 --- /dev/null +++ b/framework/src/main/resources/mapper/WechatMessageMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/script/limit.lua b/framework/src/main/resources/script/limit.lua new file mode 100644 index 00000000..e9d5b41e --- /dev/null +++ b/framework/src/main/resources/script/limit.lua @@ -0,0 +1,13 @@ +local c +c = redis.call('get',KEYS[1]) +-- 调用不超过最大值,则直接返回 +if c and tonumber(c) > tonumber(ARGV[1]) then + return c; +end +-- 执行计算器自加 +c = redis.call('incr',KEYS[1]) +if tonumber(c) == 1 then +-- 从第一次调用开始限流,设置对应键值的过期 + redis.call('expire',KEYS[1],ARGV[2]) +end +return c; \ No newline at end of file diff --git a/framework/src/main/resources/script/quantity.lua b/framework/src/main/resources/script/quantity.lua new file mode 100644 index 00000000..1a51a938 --- /dev/null +++ b/framework/src/main/resources/script/quantity.lua @@ -0,0 +1,62 @@ +-- 可能回滚的列表,一个记录要回滚的skuid一个记录库存 +local id_list= {} +local quantity_list= {} + +-- 调用放传递的keys 和 values execute(RedisScript script, List keys, Object... args) +local keys = KEYS +local values = ARGV; + +local function deduction(key,num) + keys[1] = key; + local value = redis.call("get",keys[1]) + if not value then + value = 0; + end + value = value + num + -- 变更后库存数量小于 + if(value<0) + then + -- 发生超卖 + return false; + end + redis.call("set",keys[1],value) + + return true +end + +local function rollback() + for i,k in ipairs (id_list) do + -- 还原库存 + keys[1] = k; + redis.call("incrby",keys[1],0-quantity_list[i]) + end +end + +local function execute() + -- i 类java for循环 for(int i=0;i get(@PathVariable ${entity.primaryKeyType} id){ + + ${entity.className} ${entity.classNameLowerCase} = ${entity.classNameLowerCase}Service.getById(id); + return new ResultUtil<${entity.className}>().setData(${entity.classNameLowerCase}); + } + + @GetMapping + @ApiOperation(value = "分页获取${entity.description}") + public ResultMessage> getByPage(${entity.className} entity, + SearchVO searchVo, + PageVO page){ + IPage<${entity.className}> data = ${entity.classNameLowerCase}Service.page(PageUtil.initPage(page),PageUtil.initWrapper(entity, searchVo)); + return new ResultUtil>().setData(data); + } + + @PostMapping + @ApiOperation(value = "新增${entity.description}") + public ResultMessage<${entity.className}> save(${entity.className} ${entity.classNameLowerCase}){ + + if(${entity.classNameLowerCase}Service.save(${entity.classNameLowerCase})){ + return new ResultUtil<${entity.className}>().setData(${entity.classNameLowerCase}); + } + return new ResultUtil<${entity.className}>().setErrorMsg("未知异常,请稍后重试"); + } + + @PutMapping("/{id}") + @ApiOperation(value = "更新${entity.description}") + public ResultMessage<${entity.className}> update(@PathVariable String id, ${entity.className} ${entity.classNameLowerCase}){ + if(${entity.classNameLowerCase}Service.updateById(${entity.classNameLowerCase})){ + return new ResultUtil<${entity.className}>().setData(${entity.classNameLowerCase}); + } + return new ResultUtil<${entity.className}>().setErrorMsg("未知异常,请稍后重试"); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "删除${entity.description}") + public ResultMessage delAllByIds(@PathVariable List ids){ + + ${entity.classNameLowerCase}Service.removeByIds(ids); + return ResultUtil.success("成功删除"); + } +} diff --git a/framework/src/main/resources/templates/java/entity.btl b/framework/src/main/resources/templates/java/entity.btl new file mode 100644 index 00000000..0c4df5e6 --- /dev/null +++ b/framework/src/main/resources/templates/java/entity.btl @@ -0,0 +1,23 @@ +package ${entity.entityPackage}; + +import cn.lili.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import javax.persistence.Entity; +import javax.persistence.Table; + +/** + * @author ${entity.author} + */ +@Data +@Entity +@Table(name = "${entity.tableName}") +@TableName("${entity.tableName}") +@ApiModel(value = "${entity.description}") +public class ${entity.className} extends BaseEntity { + + private static final long serialVersionUID = 1L; + +} \ No newline at end of file diff --git a/framework/src/main/resources/templates/java/mapper.btl b/framework/src/main/resources/templates/java/mapper.btl new file mode 100644 index 00000000..05dcbe57 --- /dev/null +++ b/framework/src/main/resources/templates/java/mapper.btl @@ -0,0 +1,14 @@ +package ${entity.daoPackage}; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import ${entity.entityPackage}.${entity.className}; + +import java.util.List; + +/** + * ${entity.description} Dao层 + * @author ${entity.author} + */ +public interface ${entity.className}Mapper extends BaseMapper<${entity.className}> { + +} \ No newline at end of file diff --git a/framework/src/main/resources/templates/java/mapperXml.btl b/framework/src/main/resources/templates/java/mapperXml.btl new file mode 100644 index 00000000..09236adf --- /dev/null +++ b/framework/src/main/resources/templates/java/mapperXml.btl @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/framework/src/main/resources/templates/java/service.btl b/framework/src/main/resources/templates/java/service.btl new file mode 100644 index 00000000..611cf9b6 --- /dev/null +++ b/framework/src/main/resources/templates/java/service.btl @@ -0,0 +1,14 @@ +package ${entity.servicePackage}; + +import com.baomidou.mybatisplus.extension.service.IService; +import ${entity.entityPackage}.${entity.className}; + +import java.util.List; + +/** + * ${entity.description} 业务层 + * @author ${entity.author} + */ +public interface ${entity.className}Service extends IService<${entity.className}> { + +} \ No newline at end of file diff --git a/framework/src/main/resources/templates/java/serviceImpl.btl b/framework/src/main/resources/templates/java/serviceImpl.btl new file mode 100644 index 00000000..556514f7 --- /dev/null +++ b/framework/src/main/resources/templates/java/serviceImpl.btl @@ -0,0 +1,23 @@ +package ${entity.serviceImplPackage}; + +import ${entity.daoPackage}.${entity.className}Mapper; +import ${entity.entityPackage}.${entity.className}; +import ${entity.servicePackage}.${entity.className}Service; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * ${entity.description} 业务实现 + * @author ${entity.author} + */ +@Service +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ${entity.className}ServiceImpl extends ServiceImpl<${entity.className}Mapper, ${entity.className}> implements ${entity.className}Service { + + private final ${entity.className}Mapper ${entity.classNameLowerCase}Mapper; +} \ No newline at end of file diff --git a/framework/src/main/resources/templates/vue/table.btl b/framework/src/main/resources/templates/vue/table.btl new file mode 100644 index 00000000..260f5772 --- /dev/null +++ b/framework/src/main/resources/templates/vue/table.btl @@ -0,0 +1,735 @@ + + + + \ No newline at end of file diff --git a/framework/src/main/resources/templates/vue/tree.btl b/framework/src/main/resources/templates/vue/tree.btl new file mode 100644 index 00000000..269738b6 --- /dev/null +++ b/framework/src/main/resources/templates/vue/tree.btl @@ -0,0 +1,687 @@ + + + + \ No newline at end of file diff --git a/framework/src/test/java/cn/lili/test/RedisLimiterHelperTest.java b/framework/src/test/java/cn/lili/test/RedisLimiterHelperTest.java new file mode 100644 index 00000000..1ac8a419 --- /dev/null +++ b/framework/src/test/java/cn/lili/test/RedisLimiterHelperTest.java @@ -0,0 +1,61 @@ +package cn.lili.test; + +import cn.lili.modules.order.order.entity.dos.OrderItem; +import cn.lili.modules.order.order.service.OrderItemService; +import cn.lili.modules.statistics.serviceimpl.OrderStatisticsDataServiceImpl; +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; + +import java.util.ArrayList; +import java.util.List; + +/** + * RedisLimiterHelperTest + * + * @author Chopper + * @version v1.0 + * @since 2020-06-13 12:17 + */ + +@RunWith(SpringRunner.class) +@SpringBootTest +public class RedisLimiterHelperTest { + + @Autowired + private OrderItemService orderItemService; + + @Autowired + private OrderStatisticsDataServiceImpl orderStatisticsDataService; + + + @Test + public void orderTest() { + + + + } + + + @Test + public void testBatchUpdate() { + OrderItem orderItem = new OrderItem(); + orderItem.setId("1356539557729796097"); + orderItem.setCreateBy("1356539557729796097"); + + + OrderItem orderItem1 = new OrderItem(); + orderItem1.setId("1356787800921341953"); + orderItem1.setCreateBy("1356787800921341953"); + + + List orderItemList = new ArrayList<>(); + orderItemList.add(orderItem); + orderItemList.add(orderItem1); + + orderItemService.updateBatchById(orderItemList); + + } +} diff --git a/framework/src/test/java/cn/lili/test/script/ScriptTest.java b/framework/src/test/java/cn/lili/test/script/ScriptTest.java new file mode 100644 index 00000000..6dd5b284 --- /dev/null +++ b/framework/src/test/java/cn/lili/test/script/ScriptTest.java @@ -0,0 +1,84 @@ +package cn.lili.test.script; + +import cn.lili.common.test.BaseTest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.scripting.support.ResourceScriptSource; +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; + +/** + * redis 事务测试 + * + * @author Chopper + * @version v1.0 + * @since + * 2020-02-22 20:26 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +@Transactional(rollbackFor = Exception.class) +@Rollback() +@ContextConfiguration +@Configuration +@ComponentScan("cn.lili") +public class ScriptTest extends BaseTest { + @Resource + private DefaultRedisScript redisScript; + @Resource + private StringRedisTemplate stringRedisTemplate; + + @Test + public void lua() { + stringRedisTemplate.opsForValue().set("key_1", "100"); + stringRedisTemplate.opsForValue().set("key_2", "95"); + stringRedisTemplate.opsForValue().set("key_3", "90"); + stringRedisTemplate.opsForValue().set("key_4", "85"); + List keys = new ArrayList<>(); + keys.add("key_1"); + keys.add("key_2"); + keys.add("key_3"); + keys.add("key_4"); + List value = new ArrayList<>(); + value.add("-1"); + value.add("-1"); + value.add("-1"); + value.add("-1"); + //启用十个线程 + for (int i = 0; i <= 10; i++) { + //每个线程循环十次 + Thread thread = new Thread(() -> { + for (int i1 = 0; i1 <= 10; i1++) { + Boolean execute = stringRedisTemplate.execute(redisScript, keys, value.toArray()); + System.out.println(Thread.currentThread().getName() + "|" + i1 + "|" + execute); + } + }); + thread.start(); + } + } + +} + +@Configuration +class LuaConfiguration { + @Bean + public DefaultRedisScript redisScript() { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("script/quantity.lua"))); + redisScript.setResultType(Boolean.class); + return redisScript; + } +} diff --git a/framework/src/test/java/cn/lili/test/trigger/TestTimeTrigger.java b/framework/src/test/java/cn/lili/test/trigger/TestTimeTrigger.java new file mode 100644 index 00000000..71c45ecb --- /dev/null +++ b/framework/src/test/java/cn/lili/test/trigger/TestTimeTrigger.java @@ -0,0 +1,35 @@ +package cn.lili.test.trigger; + +import cn.lili.common.cache.Cache; +import cn.lili.common.trigger.interfaces.TimeTriggerExecutor; +import cn.lili.common.utils.DateUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * TestTimeTrigger + * + * @author Chopper + * @version v1.0 + * @since + * 2019-02-19 下午3:01 + */ +@Component +public class TestTimeTrigger implements TimeTriggerExecutor { + + public static String key = "rabbitmq_test_value"; + @Autowired + private Cache cache; + + /** + * 执行任务 + * + * @param object 任务参数 + */ + @Override + public void execute(Object object) { + System.out.println(DateUtil.toString(DateUtil.getDateline(), "yyyy-MM-dd HH:mm:ss")); + System.out.println(key + "===" + object); + cache.put(key, object); + } +} diff --git a/manager-api/pom.xml b/manager-api/pom.xml new file mode 100644 index 00000000..bf583500 --- /dev/null +++ b/manager-api/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + manager-api + + cn.lili + lili-shop-parent + 1.0.1 + + + + + cn.lili + framework + 1.0.1 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/manager-api/src/main/java/cn/lili/ManagerApiApplication.java b/manager-api/src/main/java/cn/lili/ManagerApiApplication.java new file mode 100644 index 00000000..c9d22b70 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/ManagerApiApplication.java @@ -0,0 +1,37 @@ +package cn.lili; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.core.task.TaskExecutor; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +/** + * 运营后台 API + * + * @author Chopper + * @date 2020/11/16 10:03 下午 + */ +@SpringBootApplication +@EnableCaching +@EnableAsync +@EnableJpaAuditing +public class ManagerApiApplication { + + @Primary + @Bean + public TaskExecutor primaryTask() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + return executor; + } + + public static void main(String[] args) { + System.setProperty("es.set.netty.runtime.available.processors", "false"); + SpringApplication.run(ManagerApiApplication.class, args); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/distribution/DistributionCashManagerController.java b/manager-api/src/main/java/cn/lili/controller/distribution/DistributionCashManagerController.java new file mode 100644 index 00000000..1aff99ad --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/distribution/DistributionCashManagerController.java @@ -0,0 +1,57 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dos.DistributionCash; +import cn.lili.modules.distribution.entity.vos.DistributionCashSearchParams; +import cn.lili.modules.distribution.service.DistributionCashService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 管理端,分销佣金管理接口 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@RestController +@Api(tags = "管理端,分销佣金管理接口") +@RequestMapping("/manager/distribution/cash") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionCashManagerController { + + private final DistributionCashService distributorCashService; + + @ApiOperation(value = "通过id获取分销佣金详情") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(distributorCashService.getById(id)); + } + + @ApiOperation(value = "分页获取") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(DistributionCashSearchParams distributionCashSearchParams) { + + return ResultUtil.data(distributorCashService.getDistributionCash(distributionCashSearchParams)); + } + + + @ApiOperation(value = "审核") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "分销佣金ID", required = true, paramType = "path", dataType = "String"), + @ApiImplicitParam(name = "result", value = "处理结果", required = true, paramType = "query", dataType = "String") + }) + @PostMapping(value = "/audit/{id}") + public ResultMessage audit(@PathVariable String id, @NotNull String result) { + return ResultUtil.data(distributorCashService.audit(id, result)); + } +} + diff --git a/manager-api/src/main/java/cn/lili/controller/distribution/DistributionGoodsManagerController.java b/manager-api/src/main/java/cn/lili/controller/distribution/DistributionGoodsManagerController.java new file mode 100644 index 00000000..aeb003dc --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/distribution/DistributionGoodsManagerController.java @@ -0,0 +1,46 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dto.DistributionGoodsSearchParams; +import cn.lili.modules.distribution.entity.vos.DistributionGoodsVO; +import cn.lili.modules.distribution.service.DistributionGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 管理端,分销商品管理接口 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@RestController +@Api(tags = "管理端,分销商品管理接口") +@RequestMapping("/manager/distribution/goods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionGoodsManagerController { + + private final DistributionGoodsService distributionGoodsService; + + @GetMapping(value = "/getByPage") + @ApiOperation(value = "分页获取") + public ResultMessage> getByPage(DistributionGoodsSearchParams distributionGoodsSearchParams) { + return ResultUtil.data(distributionGoodsService.goodsPage(distributionGoodsSearchParams)); + } + + + @DeleteMapping(value = "/delByIds/{ids}") + @ApiOperation(value = "批量删除") + public ResultMessage delAllByIds(@PathVariable List ids) { + + distributionGoodsService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/distribution/DistributionManagerController.java b/manager-api/src/main/java/cn/lili/controller/distribution/DistributionManagerController.java new file mode 100644 index 00000000..a4469cab --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/distribution/DistributionManagerController.java @@ -0,0 +1,84 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dos.Distribution; +import cn.lili.modules.distribution.entity.dto.DistributionSearchParams; +import cn.lili.modules.distribution.service.DistributionService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 管理端,分销员管理接口 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@RestController +@Api(tags = "管理端,分销员管理接口") +@RequestMapping("/manager/distribution") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionManagerController { + + private final DistributionService distributionService; + + @ApiOperation(value = "分页获取") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(DistributionSearchParams distributionSearchParams, PageVO page) { + return ResultUtil.data(distributionService.distributionPage(distributionSearchParams, page)); + } + + + @ApiOperation(value = "清退分销商") + @PutMapping(value = "/retreat/{id}") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "分销商id", required = true, paramType = "path", dataType = "String") + }) + public ResultMessage retreat(@PathVariable String id) { + if (distributionService.retreat(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } else { + return ResultUtil.error(ResultCode.DISTRIBUTION_RETREAT_ERROR); + } + + } + + @ApiOperation(value = "恢复分销商") + @PutMapping(value = "/resume/{id}") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "分销商id", required = true, paramType = "path", dataType = "String") + }) + public ResultMessage resume(@PathVariable String id) { + if (distributionService.resume(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } else { + return ResultUtil.error(ResultCode.DISTRIBUTION_RETREAT_ERROR); + } + + } + + @ApiOperation(value = "审核分销商") + @PutMapping(value = "/audit/{id}") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "分销商id", required = true, paramType = "path", dataType = "String"), + @ApiImplicitParam(name = "status", value = "审核结果,PASS 通过 REFUSE 拒绝", required = true, paramType = "query", dataType = "String") + }) + public ResultMessage audit(@NotNull @PathVariable String id, @NotNull String status) { + if (distributionService.audit(id, status)) { + return ResultUtil.success(ResultCode.SUCCESS); + } else { + return ResultUtil.error(ResultCode.DISTRIBUTION_AUDIT_ERROR); + } + + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/distribution/DistributionOrderManagerController.java b/manager-api/src/main/java/cn/lili/controller/distribution/DistributionOrderManagerController.java new file mode 100644 index 00000000..21ddaeee --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/distribution/DistributionOrderManagerController.java @@ -0,0 +1,46 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dos.DistributionOrder; +import cn.lili.modules.distribution.entity.vos.DistributionOrderSearchParams; +import cn.lili.modules.distribution.service.DistributionOrderService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 管理端,分销订单管理接口 + * + * @author pikachu + * @date 2020-03-14 23:04:56 + */ +@RestController +@Api(tags = "管理端,分销订单管理接口") +@RequestMapping("/manager/distribution/order") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionOrderManagerController { + + private final DistributionOrderService distributionOrderService; + + @ApiOperation(value = "通过id获取分销订单") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + + return ResultUtil.data(distributionOrderService.getById(id)); + } + + + @ApiOperation(value = "分页获取分销订单") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(DistributionOrderSearchParams distributionOrderSearchParams) { + + return ResultUtil.data(distributionOrderService.getDistributionOrderPage(distributionOrderSearchParams)); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/file/FileManagerController.java b/manager-api/src/main/java/cn/lili/controller/file/FileManagerController.java new file mode 100644 index 00000000..d807733b --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/file/FileManagerController.java @@ -0,0 +1,61 @@ +package cn.lili.controller.file; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.file.entity.File; +import cn.lili.modules.file.service.FileService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,文件管理管理接口 + * + * @author Chopper + * @date 2020/11/26 15:41 + */ +@RestController +@Api(tags = "管理端,文件管理管理接口") +@RequestMapping("/manager/file") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FileManagerController { + + private final FileService fileService; + + + @ApiOperation(value = "管理端管理所有图片") + @GetMapping + @ApiImplicitParam(name = "title", value = "名称模糊匹配") + public ResultMessage> adminFiles(File file, SearchVO searchVO, PageVO pageVo) { + + return ResultUtil.data(fileService.customerPage(file, searchVO, pageVo)); + } + + + @ApiOperation(value = "文件重命名") + @PostMapping(value = "/rename") + public ResultMessage upload(String id, String newName) { + File file = fileService.getById(id); + file.setName(newName); + fileService.updateById(file); + return ResultUtil.data(file); + } + + @ApiOperation(value = "文件删除") + @DeleteMapping(value = "/delete/{ids}") + public ResultMessage delete(@PathVariable List ids) { + fileService.batchDelete(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/BrandManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/BrandManagerController.java new file mode 100644 index 00000000..7ea4bd95 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/BrandManagerController.java @@ -0,0 +1,110 @@ +package cn.lili.controller.goods; + + +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.Brand; +import cn.lili.modules.goods.entity.dto.BrandPageDTO; +import cn.lili.modules.goods.entity.vos.BrandVO; +import cn.lili.modules.goods.service.BrandService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + + +/** + * 管理端,品牌接口 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@RestController +@Api(tags = "管理端,品牌接口") +@RequestMapping("/manager/goods/brand") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BrandManagerController { + + /** + * 品牌 + */ + private final BrandService brandService; + + @ApiOperation(value = "通过id获取") + @ApiImplicitParam(name = "id", value = "品牌ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@NotNull @PathVariable String id) { + return ResultUtil.data(brandService.getById(id)); + } + + @GetMapping(value = "/all") + @ApiOperation(value = "获取所有可用品牌") + public List getAll() { + List list = brandService.list(new QueryWrapper().eq("delete_flag", 0)); + return list; + } + + @ApiOperation(value = "分页获取") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(BrandPageDTO page) { + return ResultUtil.data(brandService.getBrandsByPage(page)); + } + + @ApiOperation(value = "新增品牌") + @PostMapping + public ResultMessage save(@Valid BrandVO brand) { + if (brandService.addBrand(brand)) { + return ResultUtil.data(brand); + } + return ResultUtil.error(ResultCode.BRAND_SAVE_ERROR); + } + + @ApiOperation(value = "更新数据") + @ApiImplicitParam(name = "id", value = "品牌ID", required = true, dataType = "String", paramType = "path") + @PutMapping("/{id}") + public ResultMessage update(@PathVariable String id, @Valid BrandVO brand) { + brand.setId(id); + if (brandService.updateBrand(brand)) { + return ResultUtil.data(brand); + } + return ResultUtil.error(ResultCode.BRAND_UPDATE_ERROR); + } + + @ApiOperation(value = "后台禁用品牌") + @ApiImplicitParams({ + @ApiImplicitParam(name = "brandId", value = "品牌ID", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "id", value = "是否不可用", required = true, dataType = "String", paramType = "query") + }) + @PutMapping(value = "/disable/{brandId}") + public ResultMessage disable(@PathVariable String brandId, @RequestParam Boolean disable) { + if (brandService.brandDisable(brandId, disable)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.BRAND_DISABLE_ERROR); + } + + @ApiOperation(value = "批量删除") + @ApiImplicitParam(name = "ids", value = "品牌ID", required = true, dataType = "String", allowMultiple = true, paramType = "path") + @DeleteMapping(value = "/delByIds/{ids}") + public ResultMessage delAllByIds(@PathVariable List ids) { + + for (String id : ids) { + Brand brand = brandService.getById(id); + brand.setDeleteFlag(true); + brandService.updateById(brand); + } + return ResultUtil.success(ResultCode.BRAND_DELETE_ERROR); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/CategoryBrandManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/CategoryBrandManagerController.java new file mode 100644 index 00000000..1708d609 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/CategoryBrandManagerController.java @@ -0,0 +1,61 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.CategoryBrand; +import cn.lili.modules.goods.entity.vos.CategoryBrandVO; +import cn.lili.modules.goods.service.CategoryBrandService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 管理端,分类品牌接口 + * + * @author pikachu + * @date 2020-02-27 15:18:56 + */ +@RestController +@Api(tags = "管理端,分类品牌接口") +@RequestMapping("/manager/category/brand") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategoryBrandManagerController { + + /** + * 规格品牌管理 + */ + private final CategoryBrandService categoryBrandService; + + @ApiOperation(value = "查询某分类下绑定的品牌信息") + @ApiImplicitParam(name = "categoryId", value = "分类id", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{categoryId}") + public ResultMessage> getCategoryBrand(@PathVariable String categoryId) { + return ResultUtil.data(categoryBrandService.getCategoryBrandList(categoryId)); + } + + @ApiOperation(value = "保存某分类下绑定的品牌信息") + @PostMapping(value = "/{categoryId}") + @ApiImplicitParams({ + @ApiImplicitParam(name = "categoryId", value = "分类id", required = true, paramType = "path", dataType = "String"), + @ApiImplicitParam(name = "categoryBrands", value = "品牌id数组", required = true, paramType = "query", dataType = "String[]") + }) + public ResultMessage saveCategoryBrand(@PathVariable String categoryId, @RequestParam String[] categoryBrands) { + //删除分类品牌绑定信息 + this.categoryBrandService.remove(new QueryWrapper().eq("category_id", categoryId)); + //绑定品牌信息 + for (String brandId : categoryBrands) { + CategoryBrand categoryBrand = new CategoryBrand(categoryId, brandId); + categoryBrandService.save(categoryBrand); + } + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/CategoryManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/CategoryManagerController.java new file mode 100644 index 00000000..9a5cedfb --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/CategoryManagerController.java @@ -0,0 +1,142 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.Category; +import cn.lili.modules.goods.entity.vos.CategoryVO; +import cn.lili.modules.goods.service.CategoryService; +import cn.lili.modules.goods.service.GoodsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 管理端,商品分类接口 + * + * @author pikachu + * @date 2020-02-27 15:18:56 + */ +@RestController +@Api(tags = "管理端,商品分类接口") +@RequestMapping("/manager/goods/category") +@CacheConfig(cacheNames = "category") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategoryManagerController { + + /** + * 分类 + */ + private final CategoryService categoryService; + + /** + * 商品 + */ + private final GoodsService goodsService; + + @ApiOperation(value = "查询某分类下的全部子分类列表") + @ApiImplicitParam(name = "parentId", value = "父id,顶级为0", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{parentId}/all-children") + public ResultMessage> list(@PathVariable String parentId) { + return ResultUtil.data(this.categoryService.dbList(parentId)); + } + + @ApiOperation(value = "查询全部分类列表") + @GetMapping(value = "/allChildren") + public ResultMessage> list() { + return ResultUtil.data(this.categoryService.listAllChildrenDB()); + } + + @PostMapping + @ApiOperation(value = "添加商品分类") + public ResultMessage saveCategory(@Valid Category category) { + + //不能添加重复的分类名称 + Category category1 = new Category(); + category1.setName(category.getName()); + List list = categoryService.findByAllBySortOrder(category1); + if (StringUtils.isNotEmpty(list)) { + return ResultUtil.error(ResultCode.CATEGORY_NOT_EXIST); + } + // 非顶级分类 + if (category.getParentId() != null && !category.getParentId().equals("0")) { + Category parent = categoryService.getById(category.getParentId()); + if (parent == null) { + return ResultUtil.error(ResultCode.CATEGORY_PARENT_NOT_EXIST); + } + if (category.getLevel() >= 4) { + return ResultUtil.error(ResultCode.CATEGORY_BEYOND_THREE); + } + } + if (categoryService.saveCategory(category)) { + return ResultUtil.data(category); + } + return ResultUtil.error(ResultCode.CATEGORY_SAVE_ERROR); + } + + @PutMapping + @ApiOperation(value = "修改商品分类") + public ResultMessage updateCategory(CategoryVO category) { + Category catTemp = categoryService.getById(category.getId()); + if (catTemp == null) { + return ResultUtil.error(ResultCode.CATEGORY_PARENT_NOT_EXIST); + } + //不能添加重复的分类名称 + Category category1 = new Category(); + category1.setName(category.getName()); + category1.setId(category.getId()); + List list = categoryService.findByAllBySortOrder(category1); + if (StringUtils.isNotEmpty(list)) { + return ResultUtil.error(ResultCode.CATEGORY_NAME_IS_EXIST); + } + + categoryService.updateCategory(category); + return ResultUtil.data(category); + } + + @DeleteMapping(value = "/{id}") + @ApiImplicitParam(name = "goodsId", value = "分类ID", required = true, paramType = "path", dataType = "String") + @ApiOperation(value = "通过id删除分类") + public ResultMessage delAllByIds(@NotNull @PathVariable String id) { + Category category = new Category(); + category.setParentId(id); + List list = categoryService.findByAllBySortOrder(category); + if (list != null && !list.isEmpty()) { + return ResultUtil.error(ResultCode.CATEGORY_HAS_CHILDREN); + + } + // 查询某商品分类的商品数量 + Integer count = goodsService.getGoodsCountByCategory(id); + if (count > 0) { + return ResultUtil.error(ResultCode.CATEGORY_HAS_GOODS); + } + categoryService.delete(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @PutMapping(value = "/disable/{id}") + @ApiImplicitParams({ + @ApiImplicitParam(name = "goodsId", value = "分类ID", required = true, paramType = "path", dataType = "String") + }) + @ApiOperation(value = "后台 禁用/启用 分类") + public ResultMessage disable(@PathVariable String id, @RequestParam Boolean enableOperations) { + + Category category = categoryService.getById(id); + if (category == null) { + return ResultUtil.error(ResultCode.CATEGORY_NOT_EXIST); + } + categoryService.updateCategoryStatus(id, enableOperations); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} \ No newline at end of file diff --git a/manager-api/src/main/java/cn/lili/controller/goods/CategoryParameterGroupManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/CategoryParameterGroupManagerController.java new file mode 100644 index 00000000..01604f18 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/CategoryParameterGroupManagerController.java @@ -0,0 +1,81 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.CategoryParameterGroup; +import cn.lili.modules.goods.entity.dos.Parameters; +import cn.lili.modules.goods.entity.vos.ParameterGroupVO; +import cn.lili.modules.goods.service.CategoryParameterGroupService; +import cn.lili.modules.goods.service.ParametersService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 管理端,分类绑定参数组接口 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@RestController +@Api(tags = "管理端,分类绑定参数组接口") +@RequestMapping("/manager/goods/category/parameters") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategoryParameterGroupManagerController { + + /** + * 参数组 + */ + private final ParametersService parametersService; + + /** + * 分类参数 + */ + private final CategoryParameterGroupService categoryParameterGroupService; + + @ApiOperation(value = "查询某分类下绑定的参数信息") + @GetMapping(value = "/{categoryId}") + @ApiImplicitParam(value = "分类id", required = true, dataType = "String", paramType = "path") + public ResultMessage> getCategoryParam(@PathVariable String categoryId) { + return ResultUtil.data(categoryParameterGroupService.getCategoryParams(categoryId)); + } + + @ApiOperation(value = "保存数据") + @PostMapping + public ResultMessage saveOrUpdate(CategoryParameterGroup categoryParameterGroup) { + + if (categoryParameterGroupService.save(categoryParameterGroup)) { + return ResultUtil.data(categoryParameterGroup); + } + return ResultUtil.error(ResultCode.CATEGORY_PARAMETER_SAVE_ERROR); + } + + @ApiOperation(value = "更新数据") + @PutMapping + public ResultMessage update(CategoryParameterGroup categoryParameterGroup) { + + if (categoryParameterGroupService.updateById(categoryParameterGroup)) { + return ResultUtil.data(categoryParameterGroup); + } + return ResultUtil.error(ResultCode.CATEGORY_PARAMETER_UPDATE_ERROR); + } + + @ApiOperation(value = "通过id删除参数组") + @ApiImplicitParam(name = "id", value = "参数组ID", required = true, dataType = "String", paramType = "path") + @DeleteMapping(value = "/{id}") + public ResultMessage delAllByIds(@PathVariable String id) { + //删除参数 + parametersService.remove(new QueryWrapper().eq("group_id", id)); + //删除参数组 + categoryParameterGroupService.removeById(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/CategorySpecificationManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/CategorySpecificationManagerController.java new file mode 100644 index 00000000..541f3550 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/CategorySpecificationManagerController.java @@ -0,0 +1,78 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.CategorySpecification; +import cn.lili.modules.goods.entity.vos.CategorySpecificationVO; +import cn.lili.modules.goods.entity.vos.GoodsSpecValueVO; +import cn.lili.modules.goods.service.CategorySpecificationService; +import cn.lili.modules.goods.service.SpecificationService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 管理端,商品分类规格接口 + * + * @author pikachu + * @date 2020-02-27 15:18:56 + */ +@RestController +@Api(tags = "管理端,商品分类规格接口") +@RequestMapping("/manager/goods/category/spec") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategorySpecificationManagerController { + + /** + * 分类规格 + */ + private final CategorySpecificationService categorySpecificationService; + + /** + * 规格 + */ + private final SpecificationService specificationService; + + + @ApiOperation(value = "查询某分类下绑定的规格信息") + @GetMapping(value = "/{categoryId}") + @ApiImplicitParam(name = "categoryId", value = "分类id", required = true, dataType = "String", paramType = "path") + public List getCategorySpec(@PathVariable String categoryId) { + return categorySpecificationService.getCategorySpecList(categoryId); + } + + @ApiOperation(value = "查询某分类下绑定的规格信息,商品操作使用") + @GetMapping(value = "/goods/{categoryId}") + @ApiImplicitParam(name = "categoryId", value = "分类id", required = true, dataType = "String", paramType = "path") + public List getSpec(@PathVariable String categoryId) { + return specificationService.getGoodsSpecValue(categoryId); + } + + + @ApiOperation(value = "保存某分类下绑定的规格信息") + @PostMapping(value = "/{categoryId}") + @ApiImplicitParams({ + @ApiImplicitParam(name = "categoryId", value = "分类id", required = true, paramType = "path", dataType = "String"), + @ApiImplicitParam(name = "categorySpecs", value = "规格id数组", required = true, paramType = "query", dataType = "String[]") + }) + public ResultMessage saveCategoryBrand(@PathVariable String categoryId, + @RequestParam String[] categorySpecs) { + //删除分类规格绑定信息 + this.categorySpecificationService.remove(new QueryWrapper().eq("category_id", categoryId)); + //绑定规格信息 + for (String specId : categorySpecs) { + CategorySpecification categoryBrand = new CategorySpecification(categoryId, specId); + categorySpecificationService.save(categoryBrand); + } + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/GoodsManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/GoodsManagerController.java new file mode 100644 index 00000000..711d6757 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/GoodsManagerController.java @@ -0,0 +1,113 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.dto.GoodsSearchParams; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.entity.vos.GoodsVO; +import cn.lili.modules.goods.service.GoodsService; +import cn.lili.modules.goods.service.GoodsSkuService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotEmpty; +import java.util.Arrays; +import java.util.List; + +/** + * 管理端,商品管理接口 + * + * @author pikachu + * @date 2020-02-23 15:18:56 + */ +@RestController +@Api(tags = "管理端,商品管理接口") +@RequestMapping("/manager/goods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsManagerController { + //商品 + private final GoodsService goodsService; + //规格商品 + private final GoodsSkuService goodsSkuService; + + @ApiOperation(value = "分页获取") + @GetMapping(value = "/list") + public IPage getByPage(GoodsSearchParams goodsSearchParams) { + return goodsService.queryByParams(goodsSearchParams); + } + + @ApiOperation(value = "分页获取商品列表") + @GetMapping(value = "/sku/list") + public ResultMessage> getSkuByPage(GoodsSearchParams goodsSearchParams) { + return ResultUtil.data(goodsSkuService.getGoodsSkuByPage(goodsSearchParams)); + } + + @ApiOperation(value = "分页获取待审核商品") + @GetMapping(value = "/auth/list") + public IPage getAuthPage(GoodsSearchParams goodsSearchParams) { + + goodsSearchParams.setIsAuth(GoodsAuthEnum.TOBEAUDITED.name()); + goodsSearchParams.setMarketEnable(GoodsStatusEnum.UPPER.name()); + return goodsService.queryByParams(goodsSearchParams); + } + + @ApiOperation(value = "管理员下架商品", notes = "管理员下架商品时使用") + @ApiImplicitParams({ + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, paramType = "query", allowMultiple = true), + @ApiImplicitParam(name = "reason", value = "下架理由", required = true, paramType = "query") + }) + @PutMapping(value = "/{goodsId}/under") + public ResultMessage underGoods(@PathVariable String goodsId, @NotEmpty(message = "下架原因不能为空") @RequestParam String reason) { + List goodsIds = Arrays.asList(goodsId.split(",")); + if (Boolean.TRUE.equals(goodsService.updateGoodsMarketAble(goodsIds, GoodsStatusEnum.DOWN, reason))) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.GOODS_UNDER_ERROR); + } + + @ApiOperation(value = "管理员审核商品", notes = "管理员审核商品") + @ApiImplicitParams({ + @ApiImplicitParam(name = "goodsIds", value = "商品ID", required = true, paramType = "path", allowMultiple = true, dataType = "int"), + @ApiImplicitParam(name = "isAuth", value = "审核结果", required = true, paramType = "query", dataType = "string") + }) + @PutMapping(value = "{goodsIds}/auth") + public ResultMessage auth(@PathVariable List goodsIds, @RequestParam String isAuth) { + //校验商品是否存在 + if (goodsService.auditGoods(goodsIds, GoodsAuthEnum.valueOf(isAuth))) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.GOODS_AUTH_ERROR); + } + + + @ApiOperation(value = "管理员上架商品", notes = "管理员上架商品时使用") + @PutMapping(value = "/{goodsId}/up") + @ApiImplicitParams({ + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, allowMultiple = true) + }) + public ResultMessage unpGoods(@PathVariable List goodsId) { + if (goodsService.updateGoodsMarketAble(goodsId, GoodsStatusEnum.UPPER, "")) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.GOODS_UPPER_ERROR); + } + + + @ApiOperation(value = "通过id获取商品详情") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + GoodsVO goods = goodsService.getGoodsVO(id); + return ResultUtil.data(goods); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/GoodsParameterManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/GoodsParameterManagerController.java new file mode 100644 index 00000000..2ed5b661 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/GoodsParameterManagerController.java @@ -0,0 +1,44 @@ +package cn.lili.controller.goods; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.vos.GoodsParamsGroupVO; +import cn.lili.modules.goods.service.GoodsParamsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 管理端,商品关联参数管理接口 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@RestController +@Api(tags = "管理端,商品关联参数管理接口") +@RequestMapping("/manager/goods/parameters") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsParameterManagerController { + + private final GoodsParamsService goodsParamsService; + + @ApiImplicitParams({ + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, paramType = "path", dataType = "String"), + @ApiImplicitParam(name = "categoryId", value = "分类ID", required = true, paramType = "path", dataType = "String") + }) + @ApiOperation(value = "通过商品id和分类id查询参数信息") + @GetMapping(value = "/{goodsId}/{categoryId}") + public ResultMessage> getGoodsParameters(@PathVariable String goodsId, @PathVariable String categoryId) { + return ResultUtil.data(this.goodsParamsService.queryGoodsParams(goodsId, categoryId)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/GoodsUnitManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/GoodsUnitManagerController.java new file mode 100644 index 00000000..75de6e86 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/GoodsUnitManagerController.java @@ -0,0 +1,77 @@ +package cn.lili.controller.goods; + + +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.GoodsUnit; +import cn.lili.modules.goods.service.GoodsUnitService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 管理端,商品计量单位接口 + * + * @author Bulbasaur + * @date: 2020/11/26 16:15 + */ +@RestController +@Api(tags = "管理端,商品计量单位接口") +@RequestMapping("/manager/goods/goodsUnit") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsUnitManagerController { + + private final GoodsUnitService goodsUnitService; + + + @ApiOperation(value = "分页获取商品计量单位") + @GetMapping + public ResultMessage> getByPage(PageVO pageVO) { + return ResultUtil.data(goodsUnitService.page(PageUtil.initPage(pageVO))); + } + + @ApiOperation(value = "获取商品计量单位") + @ApiImplicitParam(name = "id", value = "计量单位ID", required = true, paramType = "path") + @GetMapping("/get/{id}") + public ResultMessage getById(@NotNull @PathVariable String id) { + return ResultUtil.data(goodsUnitService.getById(id)); + } + + @ApiOperation(value = "添加商品计量单位") + @PostMapping + public ResultMessage save(@Valid GoodsUnit goodsUnit) { + goodsUnitService.save(goodsUnit); + return ResultUtil.data(goodsUnit); + } + + @ApiOperation(value = "编辑商品计量单位") + @ApiImplicitParam(name = "id", value = "计量单位ID", required = true, paramType = "path") + @PutMapping("/{id}") + public ResultMessage update(@NotNull @PathVariable String id, @Valid GoodsUnit goodsUnit) { + goodsUnit.setId(id); + goodsUnitService.updateById(goodsUnit); + return ResultUtil.data(goodsUnit); + } + + @ApiOperation(value = "删除商品计量单位") + @ApiImplicitParam(name = "ids", value = "计量单位ID", required = true, paramType = "path") + @DeleteMapping("/delete/{ids}") + public ResultMessage delete(@NotNull @PathVariable List ids) { + goodsUnitService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/ParameterManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/ParameterManagerController.java new file mode 100644 index 00000000..60844fa2 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/ParameterManagerController.java @@ -0,0 +1,61 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.Parameters; +import cn.lili.modules.goods.service.ParametersService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 管理端,分类绑定参数组管理接口 + * + * @author Bulbasaur + * @date: 2020/11/26 16:15 + */ +@RestController +@Api(tags = "管理端,分类绑定参数组管理接口") +@RequestMapping("/manager/goods/parameters") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ParameterManagerController { + + private final ParametersService parametersService; + + @ApiOperation(value = "添加参数") + @PostMapping + public ResultMessage save(@Valid Parameters parameters) { + + if (parametersService.save(parameters)) { + return ResultUtil.data(parameters); + } + return ResultUtil.error(ResultCode.PARAMETER_SAVE_ERROR); + + } + + @ApiOperation(value = "编辑参数") + @PutMapping + public ResultMessage update(@Valid Parameters parameters) { + + if (parametersService.updateById(parameters)) { + return ResultUtil.data(parameters); + } + return ResultUtil.error(ResultCode.PARAMETER_UPDATE_ERROR); + } + + @ApiOperation(value = "通过id删除参数") + @ApiImplicitParam(name = "id", value = "参数ID", required = true, paramType = "path") + @DeleteMapping(value = "/{id}") + public ResultMessage delById(@PathVariable String id) { + parametersService.removeById(id); + return ResultUtil.success(ResultCode.SUCCESS); + + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/SpecValuesManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/SpecValuesManagerController.java new file mode 100644 index 00000000..b050a248 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/SpecValuesManagerController.java @@ -0,0 +1,55 @@ +package cn.lili.controller.goods; + + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.SpecValues; +import cn.lili.modules.goods.service.SpecValuesService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 管理端,规格项管理接口 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@RestController +@Api(tags = "管理端,规格项管理接口") +@RequestMapping("/manager/goods/specValues") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SpecValuesManagerController { + + private final SpecValuesService specValuesService; + + @GetMapping(value = "/values/{id}") + @ApiImplicitParam(name = "id", value = "规格项ID", required = true, dataType = "String", paramType = "path") + @ApiOperation(value = "查询规格值列表") + public ResultMessage> list(@PathVariable("id") String id) { + return ResultUtil.data(specValuesService.query().eq("spec_id", id).list()); + } + + @ApiOperation(value = "保存规格值") + @ApiImplicitParams({ + @ApiImplicitParam(name = "specId", value = "商品规格ID", required = true, paramType = "path"), + @ApiImplicitParam(name = "specValue", value = "商品项", required = true, allowMultiple = true, paramType = "query") + }) + @PostMapping(value = "/save/{specId}") + public ResultMessage> saveSpecValue(@PathVariable String specId, + @NotNull(message = "至少添加一个规格值") @RequestParam String[] specValue) { + //重新添加 + List list = specValuesService.saveSpecValue(specId, specValue); + return ResultUtil.data(list); + + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/goods/SpecificationManagerController.java b/manager-api/src/main/java/cn/lili/controller/goods/SpecificationManagerController.java new file mode 100644 index 00000000..5eae4cd7 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/goods/SpecificationManagerController.java @@ -0,0 +1,93 @@ +package cn.lili.controller.goods; + + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.Specification; +import cn.lili.modules.goods.entity.dto.SpecificationSearchParams; +import cn.lili.modules.goods.entity.vos.SpecificationVO; +import cn.lili.modules.goods.service.SpecificationService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + + +/** + * 管理端,商品规格接口 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@RestController +@Api(tags = "管理端,商品规格接口") +@RequestMapping("/manager/goods/spec") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SpecificationManagerController { + + private final SpecificationService specificationService; + + @GetMapping(value = "/{id}") + @ApiImplicitParam(name = "id", value = "商品规格ID", required = true, dataType = "String", paramType = "path") + @ApiOperation(value = "通过id获取商品规格") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(specificationService.getSpecification(id)); + } + + @RequestMapping(value = "/all", method = RequestMethod.GET) + @ApiOperation(value = "获取所有可用规格") + public List getAll() { + List list = specificationService.list(new QueryWrapper().eq("delete_flag", 0)); + return list; + } + + + @GetMapping(value = "/page") + @ApiOperation(value = "分页获取") + public ResultMessage> getByPage(@RequestParam(required = false) String specName, PageVO pageVo) { + SpecificationSearchParams searchParams = new SpecificationSearchParams(); + searchParams.setSpecName(specName); + return ResultUtil.data(specificationService.getSpecificationPage(searchParams, pageVo)); + } + + @PutMapping + @ApiOperation(value = "编辑规格") + public ResultMessage update(@Valid SpecificationVO parameters) { + if (parameters.getStoreId() == null) { + parameters.setStoreId("0"); + } + if (specificationService.updateSpecification(parameters)) { + return ResultUtil.data(parameters); + } + return ResultUtil.error(ResultCode.SPEC_UPDATE_ERROR); + } + + @PostMapping + @ApiOperation(value = "添加规格") + public ResultMessage save(@Valid SpecificationVO parameters) { + if (parameters.getStoreId() == null) { + parameters.setStoreId("0"); + } + if (specificationService.addSpecification(parameters) != null) { + return ResultUtil.data(parameters); + } + return ResultUtil.error(ResultCode.SPEC_SAVE_ERROR); + } + + @DeleteMapping(value = "/{ids}") + @ApiImplicitParam(name = "ids", value = "规格ID", required = true, dataType = "String", allowMultiple = true, paramType = "path") + @ApiOperation(value = "批量删除") + public ResultMessage delAllByIds(@PathVariable List ids) { + specificationService.deleteSpecification(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/member/ConnectConfigManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/ConnectConfigManagerController.java new file mode 100644 index 00000000..0cf6fcc6 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/ConnectConfigManagerController.java @@ -0,0 +1,53 @@ +package cn.lili.controller.member; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.connect.entity.ConnectConfig; +import cn.lili.modules.connect.entity.vo.ConnectConfigForm; +import cn.lili.modules.connect.service.ConnectConfigService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,联合登陆配置接口 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "管理端,联合登陆配置接口") +@RequestMapping("/manager/connectConfig") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ConnectConfigManagerController { + + private final ConnectConfigService connectConfigService; + + @GetMapping(value = "/list") + @ApiOperation(value = "获取所有联合配置") + public ResultMessage> all() { + return ResultUtil.data(connectConfigService.listForms()); + } + + @GetMapping(value = "/{key}") + @ApiOperation(value = "查看联合登陆配置详情") + public ResultMessage get(@PathVariable String key) { + ConnectConfig connectConfig = connectConfigService.getConfig(key); + return ResultUtil.data(connectConfig); + } + + + @PutMapping("/{configKey}") + @ApiOperation(value = "更新联合登陆配置") + public ResultMessage update(@PathVariable String configKey, ConnectConfig connectConfig) { + connectConfig.setConfigKey(configKey); + connectConfigService.saveConfig(connectConfig); + return ResultUtil.data(connectConfig); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/member/IpInfoManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/IpInfoManagerController.java new file mode 100644 index 00000000..620b5931 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/IpInfoManagerController.java @@ -0,0 +1,37 @@ +package cn.lili.controller.member; + +import cn.lili.common.utils.IpHelper; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; + +/** + * 管理端,IP接口 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "管理端,IP接口") +@RequestMapping("/manager/common/ip") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class IpInfoManagerController { + + private final IpHelper ipHelper; + + @RequestMapping(value = "/info", method = RequestMethod.GET) + @ApiOperation(value = "IP及天气相关信息") + public ResultMessage upload(HttpServletRequest request) { + + String result = ipHelper.getIpCity(request); + return ResultUtil.data(result); + } +} \ No newline at end of file diff --git a/manager-api/src/main/java/cn/lili/controller/member/MemberAddressManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/MemberAddressManagerController.java new file mode 100644 index 00000000..d87d2041 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/MemberAddressManagerController.java @@ -0,0 +1,64 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.MemberAddress; +import cn.lili.modules.promotion.service.MemberAddressService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 管理端,会员地址API + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "管理端,会员地址API") +@RequestMapping("/manager/member/address") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberAddressManagerController { + + private final MemberAddressService memberAddressService; + + @ApiOperation(value = "会员地址分页列表") + @GetMapping("/{memberId}") + public ResultMessage> getByPage(PageVO page, @PathVariable("memberId") String memberId) { + return ResultUtil.data(memberAddressService.getAddressByMember(page, memberId)); + } + + @ApiOperation(value = "删除会员收件地址") + @ApiImplicitParam(name = "id", value = "会员地址ID", dataType = "String", paramType = "path") + @DeleteMapping(value = "/delById/{id}") + public ResultMessage delShippingAddressById(@PathVariable String id) { + if (memberAddressService.removeMemberAddress(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改会员收件地址") + @PutMapping + public ResultMessage editShippingAddress(@Valid MemberAddress shippingAddress) { + //修改会员地址 + return ResultUtil.data(memberAddressService.updateMemberAddress(shippingAddress)); + } + + @ApiOperation(value = "新增会员收件地址") + @PostMapping + public ResultMessage addShippingAddress(@Valid MemberAddress shippingAddress) { + //添加会员地址 + return ResultUtil.data(memberAddressService.saveMemberAddress(shippingAddress)); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/member/MemberEvaluationManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/MemberEvaluationManagerController.java new file mode 100644 index 00000000..c593b19a --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/MemberEvaluationManagerController.java @@ -0,0 +1,74 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dto.EvaluationQueryParams; +import cn.lili.modules.member.entity.vo.MemberEvaluationListVO; +import cn.lili.modules.member.entity.vo.MemberEvaluationVO; +import cn.lili.modules.member.service.MemberEvaluationService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 管理端,会员商品评价接口 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "管理端,会员商品评价接口") +@RequestMapping("/manager/memberEvaluation") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberEvaluationManagerController { + + private final MemberEvaluationService memberEvaluationService; + + @ApiOperation(value = "通过id获取评论") + @ApiImplicitParam(name = "id", value = "评价ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + + return ResultUtil.data(memberEvaluationService.queryById(id)); + } + + @ApiOperation(value = "获取评价分页") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(EvaluationQueryParams evaluationQueryParams, PageVO page) { + + return ResultUtil.data(memberEvaluationService.queryPage(evaluationQueryParams, page)); + } + + @ApiOperation(value = "修改评价状态") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "评价ID", required = true, paramType = "path"), + @ApiImplicitParam(name = "status", value = "显示状态,OPEN 正常 ,CLOSE 关闭", required = true, paramType = "query") + }) + @GetMapping(value = "/updateStatus/{id}") + public ResultMessage updateStatus(@PathVariable String id, @NotNull String status) { + if (memberEvaluationService.updateStatus(id, status)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "删除评论") + @ApiImplicitParam(name = "id", value = "评价ID", required = true, dataType = "String", paramType = "path") + @PutMapping(value = "/delete/{id}") + public ResultMessage> delete(@PathVariable String id) { + if (memberEvaluationService.delete(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/member/MemberManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/MemberManagerController.java new file mode 100644 index 00000000..43b1b33a --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/MemberManagerController.java @@ -0,0 +1,87 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.entity.dto.ManagerMemberEditDTO; +import cn.lili.modules.member.entity.dto.MemberAddDTO; +import cn.lili.modules.member.entity.vo.MemberSearchVO; +import cn.lili.modules.member.service.MemberService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +/** + * 管理端,会员接口 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "管理端,会员接口") +@RequestMapping("/manager/member") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberManagerController { + + private final MemberService memberService; + + @ApiOperation(value = "会员分页列表") + @GetMapping + public ResultMessage> getByPage(MemberSearchVO memberSearchVO, PageVO page) { + return ResultUtil.data(memberService.getMemberPage(memberSearchVO, page)); + } + + + @ApiOperation(value = "通过ID获取会员信息") + @ApiImplicitParam(name = "id", value = "会员ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage get(@PathVariable String id) { + + return ResultUtil.data(memberService.getById(id)); + } + + @ApiOperation(value = "添加会员") + @PostMapping + public ResultMessage save(@Valid MemberAddDTO member) { + + return ResultUtil.data(memberService.addMember(member)); + } + + @ApiOperation(value = "修改会员基本信息") + @PutMapping + public ResultMessage update(@Valid ManagerMemberEditDTO managerMemberEditDTO) { + return ResultUtil.data(memberService.updateMember(managerMemberEditDTO)); + } + + @ApiOperation(value = "修改会员状态,开启关闭会员") + @ApiImplicitParams({ + @ApiImplicitParam(name = "memberIds", value = "会员ID", required = true, dataType = "String", allowMultiple = true, paramType = "query"), + @ApiImplicitParam(name = "disabled", required = true, dataType = "boolean", paramType = "query") + }) + @PutMapping("/updateMemberStatus") + public ResultMessage updateMemberStatus(@RequestParam List memberIds, @RequestParam Boolean disabled) { + if (memberService.updateMemberStatus(memberIds, disabled)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + + @ApiOperation(value = "根据条件查询会员总数") + @GetMapping("/num") + public ResultMessage getByPage(MemberSearchVO memberSearchVO) { + return ResultUtil.data(memberService.getMemberNum(memberSearchVO)); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/member/MemberNoticeLogManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/MemberNoticeLogManagerController.java new file mode 100644 index 00000000..6636ece7 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/MemberNoticeLogManagerController.java @@ -0,0 +1,69 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.MemberNoticeLog; +import cn.lili.modules.member.service.MemberNoticeLogService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 管理端,会员消息接口 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "管理端,会员消息接口") +@RequestMapping("/manager/memberNoticeLog") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberNoticeLogManagerController { + + private final MemberNoticeLogService memberNoticeLogService; + + @ApiOperation(value = "通过id获取") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + MemberNoticeLog memberNoticeLog = memberNoticeLogService.getById(id); + return ResultUtil.data(memberNoticeLog); + } + + @ApiOperation(value = "获取全部数据") + @GetMapping(value = "/getAll") + public ResultMessage> getAll() { + List list = memberNoticeLogService.list(); + return ResultUtil.data(list); + } + + @ApiOperation(value = "分页获取") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(PageVO page) { + IPage data = memberNoticeLogService.page(PageUtil.initPage(page)); + return ResultUtil.data(data); + } + + @ApiOperation(value = "编辑或更新数据") + @PostMapping(value = "/insertOrUpdate") + public ResultMessage saveOrUpdate(MemberNoticeLog memberNoticeLog) { + if (memberNoticeLogService.saveOrUpdate(memberNoticeLog)) { + return ResultUtil.data(memberNoticeLog); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "批量删除") + @DeleteMapping(value = "/delByIds/{ids}") + public ResultMessage delAllByIds(@PathVariable List ids) { + memberNoticeLogService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/member/MemberNoticeSenterManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/MemberNoticeSenterManagerController.java new file mode 100644 index 00000000..d4119835 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/MemberNoticeSenterManagerController.java @@ -0,0 +1,74 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.member.entity.dos.MemberNoticeSenter; +import cn.lili.modules.member.service.MemberNoticeSenterService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 管理端,会员消息接口 + * + * @author Chopper + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "管理端,会员消息接口") +@RequestMapping("/manager/memberNoticeSenter") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberNoticeSenterManagerController { + + private final MemberNoticeSenterService memberNoticeSenterService; + + @ApiOperation(value = "通过id获取") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + MemberNoticeSenter memberNoticeSenter = memberNoticeSenterService.getById(id); + return ResultUtil.data(memberNoticeSenter); + } + + @ApiOperation(value = "获取全部数据") + @GetMapping(value = "/getAll") + public ResultMessage> getAll() { + + List list = memberNoticeSenterService.list(); + return ResultUtil.data(list); + } + + @ApiOperation(value = "分页获取") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(MemberNoticeSenter entity, + SearchVO searchVo, + PageVO page) { + IPage data = memberNoticeSenterService.page(PageUtil.initPage(page), PageUtil.initWrapper(entity, searchVo)); + return ResultUtil.data(data); + } + + @ApiOperation(value = "编辑或更新数据") + @PostMapping(value = "/insertOrUpdate") + public ResultMessage saveOrUpdate(MemberNoticeSenter memberNoticeSenter) { + + if (memberNoticeSenterService.customSave(memberNoticeSenter)) { + return ResultUtil.data(memberNoticeSenter); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "批量删除") + @DeleteMapping(value = "/delByIds/{ids}") + public ResultMessage delAllByIds(@PathVariable List ids) { + memberNoticeSenterService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/member/MemberPointsHistoryManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/MemberPointsHistoryManagerController.java new file mode 100644 index 00000000..99f5d15c --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/MemberPointsHistoryManagerController.java @@ -0,0 +1,59 @@ +package cn.lili.controller.member; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.MemberPointsHistory; +import cn.lili.modules.member.entity.vo.MemberPointsHistoryVO; +import cn.lili.modules.member.service.MemberPointsHistoryService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +/** + * 管理端,会员积分历史接口 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "管理端,会员积分历史接口") +@RequestMapping("/manager/member/memberPointsHistory") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberPointsHistoryManagerController { + + private final MemberPointsHistoryService memberPointsHistoryService; + + @ApiOperation(value = "分页获取") + @ApiImplicitParams({ + @ApiImplicitParam(name = "memberId", value = "会员ID", required = true, paramType = "query"), + @ApiImplicitParam(name = "memberName", value = "会员名称", required = true, paramType = "query") + }) + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(PageVO page, String memberId, String memberName) { + + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq(memberId != null, "member_id", memberId); + queryWrapper.like(memberName != null, "member_name", memberName); + + return ResultUtil.data(memberPointsHistoryService.page(PageUtil.initPage(page), queryWrapper)); + } + + @ApiOperation(value = "获取会员积分VO") + @ApiImplicitParam(name = "memberId", value = "会员ID", paramType = "query") + @GetMapping(value = "/getMemberPointsHistoryVO") + public ResultMessage getMemberPointsHistoryVO(String memberId) { + return ResultUtil.data(memberPointsHistoryService.getMemberPointsHistoryVO(memberId)); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/member/MemberWalletManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/MemberWalletManagerController.java new file mode 100644 index 00000000..c57b6c8f --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/MemberWalletManagerController.java @@ -0,0 +1,39 @@ +package cn.lili.controller.member; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.vo.MemberWalletVO; +import cn.lili.modules.member.service.MemberWalletService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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; + +/** + * 管理端,预存款接口 + * + * @author pikachu + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "管理端,预存款接口") +@RequestMapping("/manager/members/wallet") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberWalletManagerController { + + private final MemberWalletService memberWalletService; + + @GetMapping + @ApiOperation(value = "查询会员预存款余额") + @ApiImplicitParam(name = "memberId", value = "会员ID", paramType = "query") + public ResultMessage get(@RequestParam("memberId") String memberId) { + return ResultUtil.data(memberWalletService.getMemberWallet(memberId)); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/member/MemberWithdrawApplyManagerController.java b/manager-api/src/main/java/cn/lili/controller/member/MemberWithdrawApplyManagerController.java new file mode 100644 index 00000000..8bcb2372 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/member/MemberWithdrawApplyManagerController.java @@ -0,0 +1,57 @@ +package cn.lili.controller.member; + + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.MemberWithdrawApply; +import cn.lili.modules.member.entity.vo.MemberWithdrawApplyQueryVO; +import cn.lili.modules.member.service.MemberWithdrawApplyService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + + +/** + * 管理端,余额提现记录接口 + * + * @author pikachu + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "管理端,余额提现记录接口") +@RequestMapping("/manager/members/withdraw-apply") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberWithdrawApplyManagerController { + + private final MemberWithdrawApplyService memberWithdrawApplyService; + + + @ApiOperation(value = "分页获取提现记录") + @GetMapping + public ResultMessage> getByPage(PageVO page, MemberWithdrawApplyQueryVO memberWithdrawApplyQueryVO) { + //构建查询 返回数据 + IPage memberWithdrawApplyIPage = memberWithdrawApplyService.getMemberWithdrawPage(page, memberWithdrawApplyQueryVO); + return ResultUtil.data(memberWithdrawApplyIPage); + } + + + @ApiOperation(value = "提现申请审核") + @PostMapping + @ApiImplicitParams({ + @ApiImplicitParam(name = "apply_id", value = "审核记录id", required = true, paramType = "query"), + @ApiImplicitParam(name = "result", value = "审核结果", required = true, paramType = "query", dataType = "boolean"), + @ApiImplicitParam(name = "remark", value = "审核备注", paramType = "query") + }) + public Boolean audit(String applyId, Boolean result, String remark) { + return memberWithdrawApplyService.audit(applyId, result, remark); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/ArticleCategoryManagerController.java b/manager-api/src/main/java/cn/lili/controller/other/ArticleCategoryManagerController.java new file mode 100644 index 00000000..2361279f --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/ArticleCategoryManagerController.java @@ -0,0 +1,81 @@ +package cn.lili.controller.other; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dos.ArticleCategory; +import cn.lili.modules.page.entity.vos.ArticleCategoryVO; +import cn.lili.modules.page.service.ArticleCategoryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +/** + * 管理端,文章分类管理接口 + * + * @author pikachu + * @date 2020-05-5 15:10:16 + */ +@RestController +@Api(tags = "管理端,文章分类管理接口") +@RequestMapping("/manager/article-category") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ArticleCategoryManagerController { + + /** + * 文章分类 + */ + private final ArticleCategoryService articleCategoryService; + + @ApiOperation(value = "查询分类列表") + @GetMapping(value = "/all-children") + public ResultMessage> allChildren() { + try { + return ResultUtil.data(this.articleCategoryService.allChildren()); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @ApiOperation(value = "查看文章分类") + @ApiImplicitParam(name = "id", value = "文章分类ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage getArticleCategory(@PathVariable String id) { + return ResultUtil.data(this.articleCategoryService.getById(id)); + } + + @ApiOperation(value = "保存文章分类") + @PostMapping + public ResultMessage save(@Valid ArticleCategory articleCategory) { + if (articleCategory.getLevel() == null) { + articleCategory.setLevel(0); + } + + return ResultUtil.data(articleCategoryService.saveArticleCategory(articleCategory)); + } + + @ApiOperation(value = "修改文章分类") + @ApiImplicitParam(name = "id", value = "文章分类ID", required = true, dataType = "String", paramType = "path") + @PutMapping("/update/{id}") + public ResultMessage update(@Valid ArticleCategory articleCategory, @PathVariable("id") String id) { + articleCategory.setId(id); + return ResultUtil.data(articleCategoryService.updateArticleCategory(articleCategory)); + } + + @ApiOperation(value = "删除文章分类") + @ApiImplicitParam(name = "id", value = "文章分类ID", required = true, dataType = "String", paramType = "path") + @DeleteMapping("/{id}") + public ResultMessage deleteById(@PathVariable String id) { + if (articleCategoryService.deleteById(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/ArticleManagerController.java b/manager-api/src/main/java/cn/lili/controller/other/ArticleManagerController.java new file mode 100644 index 00000000..0c998053 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/ArticleManagerController.java @@ -0,0 +1,82 @@ +package cn.lili.controller.other; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dos.Article; +import cn.lili.modules.page.entity.dto.ArticleSearchParams; +import cn.lili.modules.page.entity.enums.ArticleEnum; +import cn.lili.modules.page.entity.vos.ArticleVO; +import cn.lili.modules.page.service.ArticleService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 管理端,文章接口 + * + * @author pikachu + * @date 2020-05-06 15:18:56 + */ +@RestController +@Api(tags = "管理端,文章接口") +@RequestMapping("/manager/article") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ArticleManagerController { + + /** + * 文章 + */ + private final ArticleService articleService; + + @ApiOperation(value = "查看文章") + @ApiImplicitParam(name = "id", value = "文章ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage
get(@PathVariable String id) { + + return ResultUtil.data(articleService.getById(id)); + } + + @ApiOperation(value = "分页获取") + @ApiImplicitParams({ + @ApiImplicitParam(name = "categoryId", value = "文章分类ID", dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "title", value = "标题", dataType = "String", paramType = "query") + }) + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(ArticleSearchParams articleSearchParams) { + return ResultUtil.data(articleService.articlePage(articleSearchParams)); + } + + @ApiOperation(value = "添加文章") + @PostMapping + public ResultMessage
save(@Valid Article article) { + article.setType(ArticleEnum.OTHER.name()); + articleService.save(article); + return ResultUtil.data(article); + } + + @ApiOperation(value = "修改文章") + @ApiImplicitParam(name = "id", value = "文章ID", required = true, dataType = "String", paramType = "path") + @PutMapping("update/{id}") + public ResultMessage
update(@Valid Article article, @PathVariable("id") String id) { + article.setId(id); + return ResultUtil.data(articleService.updateArticle(article)); + } + + @ApiOperation(value = "批量删除") + @ApiImplicitParam(name = "id", value = "文章ID", required = true, dataType = "String", paramType = "path") + @DeleteMapping(value = "/delByIds/{id}") + public ResultMessage delAllByIds(@PathVariable String id) { + articleService.customRemove(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/CustomWordsController.java b/manager-api/src/main/java/cn/lili/controller/other/CustomWordsController.java new file mode 100644 index 00000000..e1094fa8 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/CustomWordsController.java @@ -0,0 +1,62 @@ +package cn.lili.controller.other; + +import cn.lili.common.exception.ServiceException; +import cn.lili.common.utils.StringUtils; +import cn.lili.modules.permission.SettingKeys; +import cn.lili.modules.search.service.CustomWordsService; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.service.SettingService; +import io.swagger.annotations.Api; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.nio.charset.StandardCharsets; + +/** + * 管理端,自定义分词接口 + * + * @author paulG + * @since 2020/10/16 + **/ +@RestController +@Api(tags = "管理端,自定义分词接口") +@RequestMapping("/manager/custom-words") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CustomWordsController { + + /** + * 分词 + */ + private final CustomWordsService customWordsService; + /** + * 设置 + */ + private final SettingService settingService; + + @GetMapping + public String getCustomWords(String secretKey) { + if (StringUtils.isEmpty(secretKey)) { + return ""; + } + Setting setting = settingService.get(SettingKeys.ES_SIGN.name()); + if (setting == null || StringUtils.isEmpty(setting.getSettingValue())) { + return ""; + } + + if (!setting.getSettingValue().equals(secretKey)) { + throw new ServiceException("秘钥验证失败!"); + } + + String res = customWordsService.deploy(); + try { + return new String(res.getBytes(), StandardCharsets.UTF_8); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/CustomWordsManagerController.java b/manager-api/src/main/java/cn/lili/controller/other/CustomWordsManagerController.java new file mode 100644 index 00000000..b81e5614 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/CustomWordsManagerController.java @@ -0,0 +1,74 @@ +package cn.lili.controller.other; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.search.entity.dos.CustomWords; +import cn.lili.modules.search.entity.vo.CustomWordsVO; +import cn.lili.modules.search.service.CustomWordsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * 管理端,自定义分词接口 + * + * @author paulG + * @date 2020/10/16 + **/ +@RestController +@Api(tags = "管理端,自定义分词接口") +@RequestMapping("/manager/manager/custom-words") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CustomWordsManagerController { + + /** + * 分词 + */ + private final CustomWordsService customWordsService; + + @ApiOperation(value = "添加自定义分词") + @PostMapping + public ResultMessage addCustomWords(@Valid CustomWordsVO customWords) { + if (customWordsService.addCustomWords(customWords)) { + return ResultUtil.data(customWords); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改自定义分词") + @PutMapping + public ResultMessage updateCustomWords(@Valid CustomWordsVO customWords) { + if (customWordsService.updateCustomWords(customWords)) { + return ResultUtil.data(customWords); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "删除自定义分词") + @ApiImplicitParam(name = "id", value = "文章ID", required = true, dataType = "String", paramType = "path") + @DeleteMapping("/{id}") + public ResultMessage deleteCustomWords(@NotNull @PathVariable String id) { + if (customWordsService.deleteCustomWords(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "分页获取自定义分词") + @ApiImplicitParam(name = "words", value = "分词", required = true, dataType = "String", paramType = "query") + @GetMapping + public ResultMessage> getCustomWords(@RequestParam String words, PageVO pageVo) { + return ResultUtil.data(customWordsService.getCustomWordsByPage(words, pageVo)); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/ElasticsearchController.java b/manager-api/src/main/java/cn/lili/controller/other/ElasticsearchController.java new file mode 100644 index 00000000..a5237cb1 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/ElasticsearchController.java @@ -0,0 +1,64 @@ +package cn.lili.controller.other; + +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.promotion.service.PromotionService; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.service.EsGoodsIndexService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import io.swagger.annotations.Api; +import org.junit.jupiter.api.Assertions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * ElasticsearchController + * + * @author Chopper + * @version v1.0 + * 2021-03-24 18:32 + */ +@RestController +@Api(tags = "ES初始化接口") +@RequestMapping("/manager/elasticsearch") +public class ElasticsearchController { + + @Autowired + private EsGoodsIndexService esGoodsIndexService; + + @Autowired + private GoodsSkuService goodsSkuService; + + @Autowired + private StringRedisTemplate stringRedisTemplate; + + @Autowired + private PromotionService promotionService; + + @GetMapping + public void init() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(GoodsSku::getIsAuth, GoodsAuthEnum.PASS.name()); + queryWrapper.eq(GoodsSku::getMarketEnable, GoodsStatusEnum.UPPER.name()); + List list = goodsSkuService.list(queryWrapper); + List esGoodsIndices = new ArrayList<>(); + for (GoodsSku goodsSku : list) { + EsGoodsIndex index = new EsGoodsIndex(goodsSku); + Map goodsCurrentPromotionMap = promotionService.getGoodsCurrentPromotionMap(index); + index.setPromotionMap(goodsCurrentPromotionMap); + esGoodsIndices.add(index); + stringRedisTemplate.opsForValue().set(GoodsSkuService.getStockCacheKey(goodsSku.getId()), goodsSku.getQuantity().toString()); + } + esGoodsIndexService.initIndex(esGoodsIndices); + Assertions.assertTrue(true); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/FeedbackManagerController.java b/manager-api/src/main/java/cn/lili/controller/other/FeedbackManagerController.java new file mode 100644 index 00000000..7c5c6eed --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/FeedbackManagerController.java @@ -0,0 +1,51 @@ +package cn.lili.controller.other; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dos.Feedback; +import cn.lili.modules.page.service.FeedbackService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 管理端,意见反馈接口 + * + * @author Bulbasaur + * @date 2020-05-5 15:10:16 + */ +@RestController +@Api(tags = "管理端,意见反馈接口") +@RequestMapping("/manager/feedback") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FeedbackManagerController { + + /** + * 意见反馈 + */ + private final FeedbackService feedbackService; + + @ApiOperation(value = "查询意见反馈列表") + @ApiImplicitParam(name = "parentId", value = "父id,顶级为0", required = true, dataType = "String", paramType = "path") + @GetMapping() + public ResultMessage> page(PageVO pageVO) { + return ResultUtil.data(feedbackService.page(PageUtil.initPage(pageVO))); + } + + @ApiOperation(value = "查看意见反馈") + @ApiImplicitParam(name = "id", value = "意见反馈ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage getFeedback(@PathVariable String id) { + return ResultUtil.data(this.feedbackService.getById(id)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/MessageManagerController.java b/manager-api/src/main/java/cn/lili/controller/other/MessageManagerController.java new file mode 100644 index 00000000..d98b7819 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/MessageManagerController.java @@ -0,0 +1,57 @@ +package cn.lili.controller.other; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.message.entity.dos.Message; +import cn.lili.modules.message.entity.vos.MessageVO; +import cn.lili.modules.message.service.MessageService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 管理端,消息发送管理接口 + * + * @author pikachu + * @date 2020-05-06 15:18:56 + */ +@RestController +@Api(tags = "管理端,消息发送管理接口") +@RequestMapping("/manager/message") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MessageManagerController { + + private final MessageService messageService; + + + @GetMapping + @ApiOperation(value = "多条件分页获取") + public ResultMessage> getByCondition(MessageVO messageVO, + PageVO pageVo) { + return ResultUtil.data(messageService.getPage(messageVO, pageVo)); + } + + @PostMapping + @ApiOperation(value = "发送消息") + public ResultMessage sendMessage(Message message) { + + return ResultUtil.data(messageService.sendMessage(message)); + } + + @DeleteMapping("/{id}") + @ApiOperation(value = "删除消息") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "消息id", required = true, dataType = "String", paramType = "path") + }) + public ResultMessage deleteMessage(@PathVariable String id) { + + return ResultUtil.data(messageService.deleteMessage(id)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/PageDataManagerController.java b/manager-api/src/main/java/cn/lili/controller/other/PageDataManagerController.java new file mode 100644 index 00000000..0e407ddf --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/PageDataManagerController.java @@ -0,0 +1,78 @@ +package cn.lili.controller.other; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dos.PageData; +import cn.lili.modules.page.entity.dto.PageDataDTO; +import cn.lili.modules.page.entity.vos.PageDataListVO; +import cn.lili.modules.page.service.PageDataService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * 管理端,页面设置管理接口 + * + * @author paulGao + * @date 2020-05-06 15:18:56 + */ +@RestController +@Api(tags = "管理端,页面设置管理接口") +@RequestMapping("/manager/pageData") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PageDataManagerController { + + private final PageDataService pageDataService; + + @ApiOperation(value = "获取页面信息") + @ApiImplicitParam(name = "id", value = "页面ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage getPageData(@PathVariable String id) { + return ResultUtil.data(pageDataService.getById(id)); + } + + @ApiOperation(value = "添加页面") + @PostMapping("/add") + public ResultMessage addPageData(@Valid PageData pageData) { + return ResultUtil.data(pageDataService.addPageData(pageData)); + } + + @ApiOperation(value = "修改页面") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "页面ID", required = true, dataType = "String", paramType = "path") + }) + @PutMapping("/update/{id}") + public ResultMessage updatePageData(@Valid PageData pageData, @NotNull @PathVariable String id) { + pageData.setId(id); + return ResultUtil.data(pageDataService.updatePageData(pageData)); + } + + @ApiOperation(value = "页面列表") + @GetMapping("pageDataList") + public ResultMessage> pageDataList(PageVO pageVO, PageDataDTO pageDataDTO) { + return ResultUtil.data(pageDataService.getPageDataList(pageVO, pageDataDTO)); + } + + @ApiOperation(value = "发布页面") + @ApiImplicitParam(name = "id", value = "页面ID", required = true, dataType = "String", paramType = "path") + @PutMapping("/release/{id}") + public ResultMessage release(@PathVariable String id) { + return ResultUtil.data(pageDataService.releasePageData(id)); + } + + @ApiOperation(value = "删除页面") + @ApiImplicitParam(name = "id", value = "页面ID", required = true, dataType = "String", paramType = "path") + @DeleteMapping("/remove/{id}") + public ResultMessage remove(@PathVariable String id) { + return ResultUtil.data(pageDataService.removePageData(id)); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/SensitiveWordsManagerController.java b/manager-api/src/main/java/cn/lili/controller/other/SensitiveWordsManagerController.java new file mode 100644 index 00000000..555a89fa --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/SensitiveWordsManagerController.java @@ -0,0 +1,83 @@ +package cn.lili.controller.other; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.system.entity.dos.SensitiveWords; +import cn.lili.modules.system.service.SensitiveWordsService; +import cn.lili.modules.system.utils.SensitiveWordsFilter; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +/** + * 管理端,敏感词管理接口 + * + * @author Bulbasaur + * @date 2020-05-06 15:18:56 + */ +@RestController +@Api(tags = "管理端,敏感词管理接口") +@RequestMapping("/manager/sensitiveWords") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SensitiveWordsManagerController { + + private final SensitiveWordsService sensitiveWordsService; + + @ApiOperation(value = "通过id获取") + @ApiImplicitParam(name = "id", value = "敏感词ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(sensitiveWordsService.getById(id)); + } + + @ApiOperation(value = "分页获取") + @GetMapping + public ResultMessage> getByPage(PageVO page) { + return ResultUtil.data(sensitiveWordsService.page(PageUtil.initPage(page))); + } + + @ApiOperation(value = "添加敏感词") + @PostMapping + public ResultMessage add(@Valid SensitiveWords sensitiveWords) { + if (sensitiveWordsService.save(sensitiveWords)) { + SensitiveWordsFilter.put(sensitiveWords.getSensitiveWord()); + return ResultUtil.data(sensitiveWords); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改敏感词") + @ApiImplicitParam(name = "id", value = "敏感词ID", required = true, dataType = "String", paramType = "path") + @PutMapping("/{id}") + public ResultMessage edit(@PathVariable String id, SensitiveWords sensitiveWords) { + sensitiveWords.setId(id); + if (sensitiveWordsService.updateById(sensitiveWords)) { + SensitiveWordsFilter.put(sensitiveWords.getSensitiveWord()); + return ResultUtil.data(sensitiveWords); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "批量删除") + @ApiImplicitParam(name = "ids", value = "敏感词ID", required = true, dataType = "String", allowMultiple = true, paramType = "path") + @DeleteMapping(value = "/delByIds/{ids}") + public ResultMessage delAllByIds(@PathVariable List ids) { + for (String id : ids) { + String name = sensitiveWordsService.getById(id).getSensitiveWord(); + SensitiveWordsFilter.remove(name); + sensitiveWordsService.removeById(id); + } + return ResultUtil.success(ResultCode.SUCCESS); + + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/SpecialManagerController.java b/manager-api/src/main/java/cn/lili/controller/other/SpecialManagerController.java new file mode 100644 index 00000000..a199801f --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/SpecialManagerController.java @@ -0,0 +1,84 @@ +package cn.lili.controller.other; + +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dos.Special; +import cn.lili.modules.page.service.SpecialService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +/** + * 管理端,专题活动接口 + * + * @author Bulbasaur + * @date: 2020/12/7 11:33 + */ +@RestController +@Api(tags = "管理端,专题活动接口") +@RequestMapping("/manager/special") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SpecialManagerController { + + private final SpecialService specialService; + + @ApiOperation(value = "添加专题活动") + @PostMapping("/addSpecial") + public ResultMessage addSpecial(@Valid Special special) { + return ResultUtil.data(specialService.addSpecial(special)); + } + + @ApiOperation(value = "修改专题活动") + @ApiImplicitParam(name = "id", value = "专题ID", required = true, dataType = "String", paramType = "path") + @PutMapping("/updateSpecial") + public ResultMessage updateSpecial(@PathVariable String id, @Valid Special special) { + special.setId(id); + if (specialService.updateById(special)) { + return ResultUtil.data(special); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "删除专题活动") + @ApiImplicitParam(name = "id", value = "专题ID", required = true, dataType = "String", paramType = "path") + @DeleteMapping("/{id}") + public ResultMessage deleteSpecial(@PathVariable String id) { + if(specialService.removeSpecial(id)){ + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + + } + + @ApiOperation(value = "分页获取专题活动") + @GetMapping + public ResultMessage> getSpecials(PageVO pageVo) { + return ResultUtil.data(specialService.page(PageUtil.initPage(pageVo))); + } + + @ApiOperation(value = "获取专题活动列表") + @GetMapping("/getSpecialsList") + public ResultMessage> getSpecialsList() { + return ResultUtil.data(specialService.list()); + } + + + @ApiOperation(value = "获取专题活动") + @ApiImplicitParam(name = "id", value = "专题ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage getSpecialsList(@PathVariable String id) { + return ResultUtil.data(specialService.getById(id)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/other/VerificationSourceController.java b/manager-api/src/main/java/cn/lili/controller/other/VerificationSourceController.java new file mode 100644 index 00000000..6542cade --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/other/VerificationSourceController.java @@ -0,0 +1,84 @@ +package cn.lili.controller.other; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.base.entity.dos.VerificationSource; +import cn.lili.modules.base.service.VerificationSourceService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,验证码资源维护接口 + * + * @author Chopper + * @date: 2020/12/7 11:33 + */ +@RestController +@Api(tags = "管理端,验证码资源维护接口") +@RequestMapping("/manager/verificationSource") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class VerificationSourceController { + + private final VerificationSourceService verificationSourceService; + + @GetMapping(value = "/{id}") + @ApiOperation(value = "查看验证码资源维护详情") + public ResultMessage get(@PathVariable String id) { + + VerificationSource verificationSource = verificationSourceService.getById(id); + return ResultUtil.data(verificationSource); + } + + @GetMapping + @ApiOperation(value = "分页获取验证码资源维护") + public ResultMessage> getByPage(VerificationSource entity, + SearchVO searchVo, + PageVO page) { + IPage data = verificationSourceService.page(PageUtil.initPage(page), PageUtil.initWrapper(entity, searchVo)); + return ResultUtil.data(data); + } + + @PostMapping + @ApiOperation(value = "新增验证码资源维护") + public ResultMessage save(VerificationSource verificationSource) { + + if (verificationSourceService.save(verificationSource)) { + verificationSourceService.initCache(); + return ResultUtil.data(verificationSource); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @PutMapping("/{id}") + @ApiOperation(value = "更新验证码资源维护") + public ResultMessage update(@PathVariable String id, VerificationSource verificationSource) { + verificationSource.setId(id); + if (verificationSourceService.updateById(verificationSource)) { + verificationSourceService.initCache(); + return ResultUtil.data(verificationSource); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "删除验证码资源维护") + public ResultMessage delAllByIds(@PathVariable List ids) { + + verificationSourceService.removeByIds(ids); + verificationSourceService.initCache(); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/passport/AdminUserManagerController.java b/manager-api/src/main/java/cn/lili/controller/passport/AdminUserManagerController.java new file mode 100644 index 00000000..869cd4be --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/passport/AdminUserManagerController.java @@ -0,0 +1,176 @@ +package cn.lili.controller.passport; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.token.Token; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.permission.entity.dos.AdminUser; +import cn.lili.modules.permission.entity.dto.AdminUserDTO; +import cn.lili.modules.permission.entity.vo.AdminUserVO; +import cn.lili.modules.permission.service.AdminUserService; +import cn.lili.modules.permission.service.DepartmentService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; +import java.util.List; + + +/** + * 管理员接口 + * + * @author Chopper + * @since 2020/11/16 10:57 + */ +@Slf4j +@RestController +@Api(tags = "管理员") +@RequestMapping("/manager/user") +@Transactional +@Validated +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AdminUserManagerController { + + private final AdminUserService adminUserService; + + private final DepartmentService departmentService; + + + @GetMapping(value = "/login") + @ApiOperation(value = "登录管理员") + public ResultMessage login(String username, String password) { + return ResultUtil.data(adminUserService.login(username, password)); + } + + + @ApiOperation(value = "刷新token") + @GetMapping("/refresh/{refreshToken}") + public ResultMessage refreshToken(@NotNull(message = "刷新token不能为空") @PathVariable String refreshToken) { + return ResultUtil.data(this.adminUserService.refreshToken(refreshToken)); + } + + + @GetMapping(value = "/info") + @ApiOperation(value = "获取当前登录用户接口") + public ResultMessage getUserInfo() { + AuthUser tokenUser = UserContext.getCurrentUser(); + if (tokenUser != null) { + AdminUserVO adminUser = new AdminUserVO(adminUserService.findByUsername(tokenUser.getUsername())); + if (StringUtils.isNotEmpty(adminUser.getDepartmentId())) { + adminUser.setDepartmentTitle(departmentService.getById(adminUser.getDepartmentId()).getTitle()); + } + adminUser.setPassword(null); + return ResultUtil.data(adminUser); + } + return ResultUtil.error(ResultCode.USER_NOT_LOGIN); + } + + @PutMapping(value = "/edit") + @ApiOperation(value = "修改用户自己资料", notes = "用户名密码不会修改") + public ResultMessage editOwner(AdminUser adminUser) { + + AuthUser tokenUser = UserContext.getCurrentUser(); + if (tokenUser != null) { + //查询当前管理员 + AdminUser adminUserDB = adminUserService.findByUsername(tokenUser.getUsername()); + adminUserDB.setAvatar(adminUser.getAvatar()); + adminUserDB.setNickName(adminUser.getNickName()); + if (!adminUserService.updateById(adminUserDB)) { + return ResultUtil.error(ResultCode.USER_EDIT_ERROR); + } + return ResultUtil.success(ResultCode.USER_EDIT_SUCCESS); + } + return ResultUtil.error(ResultCode.USER_NOT_LOGIN); + } + + @PutMapping(value = "/admin/edit") + @ApiOperation(value = "超级管理员修改其他管理员资料") + public ResultMessage edit(AdminUser adminUser, + @RequestParam(required = false) List roles) { + if (!adminUserService.updateAdminUser(adminUser, roles)) { + return ResultUtil.error(ResultCode.USER_EDIT_ERROR); + } + return ResultUtil.success(ResultCode.USER_EDIT_SUCCESS); + } + + /** + * 修改密码 + * + * @param password + * @param newPassword + * @return + */ + @PutMapping(value = "/editPassword") + @ApiOperation(value = "修改密码") + public ResultMessage editPassword(String password, String newPassword) { + adminUserService.editPassword(password, newPassword); + return ResultUtil.success(ResultCode.USER_EDIT_SUCCESS); + } + + @PostMapping(value = "/resetPassword/{ids}") + @ApiOperation(value = "重置密码") + public ResultMessage resetPassword(@PathVariable List ids) { + adminUserService.resetPassword(ids); + return ResultUtil.success(ResultCode.USER_EDIT_SUCCESS); + } + + @GetMapping + @ApiOperation(value = "多条件分页获取用户列表") + public ResultMessage> getByCondition(AdminUserDTO user, + SearchVO searchVo, + PageVO pageVo) { + IPage page = adminUserService.adminUserPage(PageUtil.initPage(pageVo), PageUtil.initWrapper(user, searchVo)); + + return ResultUtil.data(page); + } + + + @PostMapping + @ApiOperation(value = "添加用户") + public ResultMessage register(AdminUserDTO adminUser, + @RequestParam(required = false) List roles) { + try { + if (roles != null & roles.size() >= 10) { + return ResultUtil.error(ResultCode.PERMISSION_BEYOND_TEN); + } + adminUserService.saveAdminUser(adminUser, roles); + } catch (Exception e) { + e.printStackTrace(); + } + return ResultUtil.success(ResultCode.SUCCESS); + } + + @PutMapping(value = "/enable/{userId}") + @ApiOperation(value = "禁/启 用 用户") + public ResultMessage disable(@ApiParam("用户唯一id标识") @PathVariable String userId, Boolean status) { + AdminUser user = adminUserService.getById(userId); + if (user == null) { + return ResultUtil.error(ResultCode.USER_NOT_EXIST); + } + user.setStatus(status); + adminUserService.updateById(user); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "批量通过ids删除") + public ResultMessage delAllByIds(@PathVariable List ids) { + adminUserService.deleteCompletely(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/permission/DepartmentManagerController.java b/manager-api/src/main/java/cn/lili/controller/permission/DepartmentManagerController.java new file mode 100644 index 00000000..66c0bf8d --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/permission/DepartmentManagerController.java @@ -0,0 +1,73 @@ +package cn.lili.controller.permission; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.permission.entity.dos.Department; +import cn.lili.modules.permission.entity.vo.DepartmentVO; +import cn.lili.modules.permission.service.DepartmentService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,部门管理接口 + * + * @author Chopper + * @since 2020/11/22 12:06 + */ +@RestController +@Api(tags = "管理端,部门管理接口") +@RequestMapping("/manager/department") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DepartmentManagerController { + + private final DepartmentService departmentService; + + @GetMapping(value = "/{id}") + @ApiOperation(value = "查看部门详情") + public ResultMessage get(@PathVariable String id) { + Department department = departmentService.getById(id); + return ResultUtil.data(department); + } + + @GetMapping + @ApiOperation(value = "获取树状结构") + public ResultMessage> getByPage(Department entity, + SearchVO searchVo) { + return ResultUtil.data(departmentService.tree(PageUtil.initWrapper(entity, searchVo))); + + } + + @PostMapping + @ApiOperation(value = "新增部门") + public ResultMessage save(Department department) { + if (departmentService.save(department)) { + return ResultUtil.data(department); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @PutMapping("/{id}") + @ApiOperation(value = "更新部门") + public ResultMessage update(@PathVariable String id, Department department) { + if (departmentService.updateById(department)) { + return ResultUtil.data(department); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "删除部门") + public ResultMessage delAllByIds(@PathVariable List ids) { + departmentService.deleteByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/permission/DepartmentRoleManagerController.java b/manager-api/src/main/java/cn/lili/controller/permission/DepartmentRoleManagerController.java new file mode 100644 index 00000000..d998c745 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/permission/DepartmentRoleManagerController.java @@ -0,0 +1,49 @@ +package cn.lili.controller.permission; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.permission.entity.dos.DepartmentRole; +import cn.lili.modules.permission.service.DepartmentRoleService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,部门角色接口 + * + * @author Chopper + * @since 2020/11/22 14:05 + */ +@RestController +@Api(tags = "管理端,部门角色接口") +@RequestMapping("/manager/departmentRole") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DepartmentRoleManagerController { + + private final DepartmentRoleService departmentRoleService; + + @GetMapping(value = "/{departmentId}") + @ApiOperation(value = "查看部门拥有的角色") + public ResultMessage> get(@PathVariable String departmentId) { + return ResultUtil.data(departmentRoleService.listByDepartmentId(departmentId)); + } + + @PutMapping("/{departmentId}") + @ApiOperation(value = "更新部门角色") + public ResultMessage update(@PathVariable String departmentId, @RequestBody List departmentRole) { + try { + departmentRoleService.updateByDepartmentId(departmentId, departmentRole); + } catch (Exception e) { + e.printStackTrace(); + return ResultUtil.error(ResultCode.ERROR); + } + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/permission/MenuManagerController.java b/manager-api/src/main/java/cn/lili/controller/permission/MenuManagerController.java new file mode 100644 index 00000000..5dbc1d4f --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/permission/MenuManagerController.java @@ -0,0 +1,79 @@ +package cn.lili.controller.permission; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.permission.entity.dos.Menu; +import cn.lili.modules.permission.entity.dto.MenuSearchParams; +import cn.lili.modules.permission.entity.vo.MenuVO; +import cn.lili.modules.permission.service.MenuService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,菜单管理接口 + * + * @author Chopper + * @date 2020/11/20 12:07 + */ +@RestController +@Api(tags = "管理端,菜单管理接口") +@RequestMapping("/manager/menu") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MenuManagerController { + + private final MenuService menuService; + + + @ApiOperation(value = "搜索菜单") + @GetMapping + public ResultMessage> searchPermissionList(MenuSearchParams searchParams) { + return ResultUtil.data(menuService.searchList(searchParams)); + } + + @ApiOperation(value = "添加") + @PostMapping + public ResultMessage add(Menu menu) { + try { + menuService.save(menu); + } catch (Exception e) { + e.printStackTrace(); + } + return ResultUtil.data(menu); + } + + @ApiImplicitParam(name = "id", value = "菜单ID", required = true, paramType = "path", dataType = "String") + @ApiOperation(value = "编辑") + @PutMapping(value = "/{id}") + public ResultMessage edit(@PathVariable String id, Menu menu) { + menu.setId(id); + menuService.updateById(menu); + return ResultUtil.data(menu); + } + + @ApiOperation(value = "批量删除") + @DeleteMapping(value = "/{ids}") + public ResultMessage delByIds(@PathVariable List ids) { + menuService.deleteIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "获取所有菜单") + @GetMapping("/tree") + public ResultMessage> getAllMenuList() { + return ResultUtil.data(menuService.tree()); + } + + @ApiOperation(value = "获取所有菜单") + @GetMapping("/memberMenu") + public ResultMessage> memberMenu() { + return ResultUtil.data(menuService.findUserTree()); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/permission/RoleManagerController.java b/manager-api/src/main/java/cn/lili/controller/permission/RoleManagerController.java new file mode 100644 index 00000000..82551cf5 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/permission/RoleManagerController.java @@ -0,0 +1,64 @@ +package cn.lili.controller.permission; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.permission.entity.dos.Role; +import cn.lili.modules.permission.service.RoleService; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,角色管理接口 + * + * @author Chopper + * @date 2020/11/20 18:50 + */ +@RestController +@Api(tags = "管理端,角色管理接口") +@RequestMapping("/manager/role") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RoleManagerController { + + private final RoleService roleService; + + @PostMapping + @ApiOperation(value = "添加") + public ResultMessage add(Role role) { + roleService.save(role); + return ResultUtil.data(role); + } + + @GetMapping + @ApiOperation(value = "查询") + public ResultMessage add(PageVO pageVo, Role role) { + Page page = roleService.page(PageUtil.initPage(pageVo), PageUtil.initWrapper(role)); + return ResultUtil.data(page); + } + + @PutMapping("/{roleId}") + @ApiOperation(value = "编辑") + public ResultMessage edit(@PathVariable String roleId, Role role) { + role.setId(roleId); + roleService.updateById(role); + return ResultUtil.data(role); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "批量删除") + public ResultMessage delByIds(@PathVariable List ids) { + roleService.deleteRoles(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/permission/RoleMenuManagerController.java b/manager-api/src/main/java/cn/lili/controller/permission/RoleMenuManagerController.java new file mode 100644 index 00000000..5c69271c --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/permission/RoleMenuManagerController.java @@ -0,0 +1,44 @@ +package cn.lili.controller.permission; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.permission.entity.dos.RoleMenu; +import cn.lili.modules.permission.service.RoleMenuService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,角色菜单接口 + * + * @author Chopper + * @date 2020/11/22 11:40 + */ +@RestController +@Api(tags = "管理端,角色菜单接口") +@RequestMapping("/manager/roleMenu") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RoleMenuManagerController { + + private final RoleMenuService roleMenuService; + + @GetMapping(value = "/{roleId}") + @ApiOperation(value = "查看某角色拥有到菜单") + public ResultMessage> get(@PathVariable String roleId) { + return ResultUtil.data(roleMenuService.findByRoleId(roleId)); + } + + @PostMapping(value = "/{roleId}") + @ApiOperation(value = "保存角色菜单") + public ResultMessage save(@PathVariable String roleId, @RequestBody List roleMenus) { + roleMenuService.updateRoleMenu(roleId, roleMenus); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/permission/UserRoleManagerController.java b/manager-api/src/main/java/cn/lili/controller/permission/UserRoleManagerController.java new file mode 100644 index 00000000..bedbe5c3 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/permission/UserRoleManagerController.java @@ -0,0 +1,45 @@ +package cn.lili.controller.permission; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.permission.entity.dos.UserRole; +import cn.lili.modules.permission.service.UserRoleService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,管理员角色接口 + * + * @author Chopper + * @date 2020/11/22 11:53 + */ +@RestController +@Api(tags = "管理端,管理员角色接口") +@RequestMapping("/manager/userRole") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class UserRoleManagerController { + + private final UserRoleService userRoleService; + + @GetMapping(value = "/{userId}") + @ApiOperation(value = "查看管理员角色") + public ResultMessage get(@PathVariable String userId) { + UserRole userRole = userRoleService.getById(userId); + return ResultUtil.data(userRole); + } + + @PutMapping("/{userId}") + @ApiOperation(value = "更新角色菜单") + public ResultMessage update(@PathVariable String userId, List userRole) { + userRoleService.updateUserRole(userId, userRole); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/promotion/CouponManagerController.java b/manager-api/src/main/java/cn/lili/controller/promotion/CouponManagerController.java new file mode 100644 index 00000000..1b3fb2ff --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/promotion/CouponManagerController.java @@ -0,0 +1,128 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dos.Coupon; +import cn.lili.modules.promotion.entity.dos.MemberCoupon; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.vos.CouponSearchParams; +import cn.lili.modules.promotion.entity.vos.CouponVO; +import cn.lili.modules.promotion.service.CouponService; +import cn.lili.modules.promotion.service.MemberCouponService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 管理端,优惠券接口 + * + * @author paulG + * @date 2020/10/9 + **/ +@RestController +@Api(tags = "管理端,优惠券接口") +@RequestMapping("/manager/promotion/coupon") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CouponManagerController { + + private final CouponService couponService; + private final MemberCouponService memberCouponService; + + @ApiOperation(value = "获取优惠券列表") + @GetMapping + public ResultMessage> getCouponList(CouponSearchParams queryParam, PageVO page) { + page.setNotConvert(true); + queryParam.setStoreId("platform"); + IPage coupons = couponService.getCouponsByPageFromMongo(queryParam, page); + return ResultUtil.data(coupons); + } + + @ApiOperation(value = "获取优惠券详情") + @GetMapping("/{couponId}") + public ResultMessage getCoupon(@PathVariable String couponId) { + CouponVO coupon = couponService.getCouponDetailFromMongo(couponId); + return ResultUtil.data(coupon); + } + + @ApiOperation(value = "添加优惠券") + @PostMapping(consumes = "application/json", produces = "application/json") + public ResultMessage addCoupon(@RequestBody CouponVO couponVO) { + this.setStoreInfo(couponVO); + if (couponService.add(couponVO) != null) { + return ResultUtil.data(couponVO); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改优惠券") + @PutMapping(consumes = "application/json", produces = "application/json") + public ResultMessage updateCoupon(@RequestBody CouponVO couponVO) { + Coupon coupon = couponService.getById(couponVO.getId()); + couponVO.setPromotionStatus(PromotionStatusEnum.NEW.name()); + if (couponService.updateCoupon(couponVO) != null) { + return ResultUtil.data(coupon); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改优惠券状态") + @PutMapping("/status") + public ResultMessage updateCouponStatus(String couponIds, String promotionStatus) { + String[] split = couponIds.split(","); + if (couponService.updateCouponStatus(Arrays.asList(split), PromotionStatusEnum.valueOf(promotionStatus))) { + return ResultUtil.success(ResultCode.COUPON_EDIT_STATUS_SUCCESS); + } + return ResultUtil.error(ResultCode.COUPON_EDIT_STATUS_ERROR); + } + + @ApiOperation(value = "批量删除") + @DeleteMapping(value = "/{ids}") + public ResultMessage delAllByIds(@PathVariable List ids) { + for (String id : ids) { + couponService.deleteCoupon(id); + } + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "会员优惠券作废") + @PutMapping(value = "/member/cancellation/{id}") + public ResultMessage cancellation(@PathVariable String id) { + memberCouponService.cancellation(id); + return ResultUtil.success(ResultCode.COUPON_CANCELLATION_SUCCESS); + } + + @ApiOperation(value = "根据优惠券id券分页获取会员领详情") + @GetMapping(value = "/member/{id}") + public ResultMessage> getByPage(@PathVariable String id, + PageVO page) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + IPage data = memberCouponService.page(PageUtil.initPage(page), + queryWrapper.eq("coupon_id", id) + ); + return ResultUtil.data(data); + + } + + private void setStoreInfo(CouponVO couponVO) { + AuthUser currentUser = UserContext.getCurrentUser(); + if (currentUser == null) { + throw new ServiceException("获取当前用户信息不存在"); + } + couponVO.setStoreId("platform"); + couponVO.setStoreName("platform"); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/promotion/FullDiscountManagerController.java b/manager-api/src/main/java/cn/lili/controller/promotion/FullDiscountManagerController.java new file mode 100644 index 00000000..82ff03ad --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/promotion/FullDiscountManagerController.java @@ -0,0 +1,52 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.vos.FullDiscountSearchParams; +import cn.lili.modules.promotion.service.FullDiscountService; +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 管理端,满额活动接口 + * + * @author paulG + * @date 2021/1/12 + **/ +@RestController +@Api(tags = "管理端,满额活动接口") +@RequestMapping("/manager/promotion/fullDiscount") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FullDiscountManagerController { + + private final FullDiscountService fullDiscountService; + + @ApiOperation(value = "获取满优惠列表") + @GetMapping + public ResultMessage> getCouponList(FullDiscountSearchParams searchParams, PageVO page) { + page.setNotConvert(true); + return ResultUtil.data(fullDiscountService.getFullDiscountByPageFromMongo(searchParams, page)); + } + + @ApiOperation(value = "获取满优惠详情") + @GetMapping("/{id}") + public ResultMessage getCouponDetail(@PathVariable String id) { + return ResultUtil.data(fullDiscountService.getFullDiscount(id)); + } + + @ApiOperation(value = "获取满优惠商品列表") + @GetMapping("/goods/{id}") + public ResultMessage getCouponGoods(@PathVariable String id) { + return ResultUtil.data(fullDiscountService.getFullDiscount(id)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/promotion/PintuanManagerController.java b/manager-api/src/main/java/cn/lili/controller/promotion/PintuanManagerController.java new file mode 100644 index 00000000..5838a43d --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/promotion/PintuanManagerController.java @@ -0,0 +1,83 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dos.Pintuan; +import cn.lili.modules.promotion.entity.dto.PromotionGoodsDTO; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.PintuanSearchParams; +import cn.lili.modules.promotion.entity.vos.PintuanVO; +import cn.lili.modules.promotion.entity.vos.PromotionGoodsSearchParams; +import cn.lili.modules.promotion.service.PintuanService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; + +/** + * 管理端,平台拼团接口 + * + * @author paulG + * @date 2020/10/9 + **/ +@RestController +@Api(tags = "管理端,平台拼团接口") +@RequestMapping("/manager/promotion/pintuan") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PintuanManagerController { + + private final PintuanService pintuanService; + private final PromotionGoodsService promotionGoodsService; + + @GetMapping(value = "/{id}") + @ApiOperation(value = "通过id获取") + public ResultMessage get(@PathVariable String id) { + Pintuan pintuan = pintuanService.getById(id); + return ResultUtil.data(pintuan); + } + + @GetMapping + @ApiOperation(value = "根据条件分页查询拼团活动列表") + public ResultMessage> getPintuanByPage(PintuanSearchParams queryParam, PageVO pageVo) { + IPage pintuanByPageFromMongo = pintuanService.getPintuanByPageFromMongo(queryParam, pageVo); + return ResultUtil.data(pintuanByPageFromMongo); + } + + @GetMapping("/goods/{pintuanId}") + @ApiOperation(value = "根据条件分页查询拼团活动商品列表") + public ResultMessage> getPintuanGoodsByPage(@PathVariable String pintuanId, PageVO pageVo) { + PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams(); + searchParams.setPromotionId(pintuanId); + searchParams.setPromotionType(PromotionTypeEnum.PINTUAN.name()); + IPage promotionGoods = promotionGoodsService.getPromotionGoods(searchParams, pageVo); + return ResultUtil.data(promotionGoods); + } + + @PutMapping("/open/{pintuanId}") + @ApiOperation(value = "手动开启拼团活动") + public ResultMessage openPintuan(@PathVariable String pintuanId, Long startTime, Long endTime) { + if (pintuanService.openPintuan(pintuanId, new Date(startTime), new Date(endTime))) { + return ResultUtil.success(ResultCode.PINTUAN_MANUAL_OPEN_SUCCESS); + } + return ResultUtil.error(ResultCode.PINTUAN_MANUAL_OPEN_ERROR); + + } + + @PutMapping("/close/{pintuanId}") + @ApiOperation(value = "手动关闭拼团活动") + public ResultMessage closePintuan(@PathVariable String pintuanId) { + if (pintuanService.closePintuan(pintuanId)) { + return ResultUtil.success(ResultCode.PINTUAN_MANUAL_CLOSE_SUCCESS); + } + return ResultUtil.error(ResultCode.PINTUAN_MANUAL_CLOSE_ERROR); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/promotion/PointsGoodsCategoryManagerController.java b/manager-api/src/main/java/cn/lili/controller/promotion/PointsGoodsCategoryManagerController.java new file mode 100644 index 00000000..abddabe6 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/promotion/PointsGoodsCategoryManagerController.java @@ -0,0 +1,70 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dos.PointsGoodsCategory; +import cn.lili.modules.promotion.entity.vos.PointsGoodsCategoryVO; +import cn.lili.modules.promotion.service.PointsGoodsCategoryService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 管理端,积分商品分类接口 + * + * @author paulG + * @date 2021/1/14 + **/ +@RestController +@Api(tags = "管理端,积分商品分类接口") +@RequestMapping("/manager/promotion/pointsGoodsCategory") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PointsGoodsCategoryManagerController { + + private final PointsGoodsCategoryService pointsGoodsCategoryService; + + @PostMapping + @ApiOperation(value = "添加积分商品分类") + public ResultMessage add(PointsGoodsCategoryVO pointsGoodsCategory) { + if (pointsGoodsCategoryService.addCategory(pointsGoodsCategory)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @PutMapping + @ApiOperation(value = "修改积分商品分类") + public ResultMessage update(PointsGoodsCategoryVO pointsGoodsCategory) { + if (pointsGoodsCategoryService.updateCategory(pointsGoodsCategory)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @DeleteMapping("/{id}") + @ApiOperation(value = "删除积分商品分类") + public ResultMessage delete(@PathVariable String id) { + if (pointsGoodsCategoryService.deleteCategory(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @GetMapping + @ApiOperation(value = "获取积分商品分类分页") + public ResultMessage> page(String name, PageVO page) { + return ResultUtil.data(pointsGoodsCategoryService.getCategoryByPage(name, page)); + } + + @GetMapping("/{id}") + @ApiOperation(value = "修改积分商品分类") + public ResultMessage getById(@PathVariable String id) { + return ResultUtil.data(pointsGoodsCategoryService.getCategoryDetail(id)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/promotion/PointsGoodsManagerController.java b/manager-api/src/main/java/cn/lili/controller/promotion/PointsGoodsManagerController.java new file mode 100644 index 00000000..06c26f33 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/promotion/PointsGoodsManagerController.java @@ -0,0 +1,97 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.vos.PointsGoodsSearchParams; +import cn.lili.modules.promotion.entity.vos.PointsGoodsVO; +import cn.lili.modules.promotion.service.PointsGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 管理端,积分商品接口 + * + * @author paulG + * @date 2021/1/14 + **/ +@RestController +@Api(tags = "管理端,积分商品接口") +@RequestMapping("/manager/promotion/pointsGoods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PointsGoodsManagerController { + + private final PointsGoodsService pointsGoodsService; + + @PostMapping(consumes = "application/json", produces = "application/json") + @ApiOperation(value = "添加积分商品") + public ResultMessage addPointsGoods(@RequestBody List pointsGoodsList) { + AuthUser currentUser = UserContext.getCurrentUser(); + List collect = new ArrayList<>(); + for (PointsGoodsVO i : pointsGoodsList) { + i.setStoreName("platform"); + i.setStoreId(currentUser.getId()); + collect.add(i); + } + if (pointsGoodsService.addPointsGoods(collect)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @PutMapping(consumes = "application/json", produces = "application/json") + @ApiOperation(value = "修改积分商品") + public ResultMessage updatePointsGoods(@RequestBody PointsGoodsVO pointsGoods) { + AuthUser currentUser = UserContext.getCurrentUser(); + pointsGoods.setStoreId(currentUser.getId()); + pointsGoods.setStoreName("platform"); + if (pointsGoodsService.updatePointsGoods(pointsGoods)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @PutMapping("/{ids}") + @ApiOperation(value = "修改积分商品状态") + public ResultMessage updatePointsGoodsStatus(@PathVariable String ids, String promotionStatus) { + if (pointsGoodsService.updatePointsGoodsPromotionStatus(Arrays.asList(ids.split(",")), promotionStatus)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @DeleteMapping("/{ids}") + @ApiOperation(value = "删除积分商品") + public ResultMessage delete(@PathVariable String ids) { + if (pointsGoodsService.deletePointsGoods(Arrays.asList(ids.split(",")))) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @GetMapping + @ApiOperation(value = "分页获取积分商品") + public ResultMessage> getPointsGoodsPage(PointsGoodsSearchParams searchParams, PageVO page) { + IPage pointsGoodsByPage = pointsGoodsService.getPointsGoodsByPage(searchParams, page); + return ResultUtil.data(pointsGoodsByPage); + } + + @GetMapping("/{id}") + @ApiOperation(value = "获取积分商品详情") + public ResultMessage getPointsGoodsDetail(@PathVariable String id) { + PointsGoodsVO pointsGoodsDetail = pointsGoodsService.getPointsGoodsDetail(id); + return ResultUtil.data(pointsGoodsDetail); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/promotion/PromotionManagerController.java b/manager-api/src/main/java/cn/lili/controller/promotion/PromotionManagerController.java new file mode 100644 index 00000000..b7ec8b17 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/promotion/PromotionManagerController.java @@ -0,0 +1,52 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dto.PromotionGoodsDTO; +import cn.lili.modules.promotion.service.PromotionService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +/** + * 管理端,促销接口 + * + * @author paulG + * @date 2021/2/2 + **/ +@RestController +@Api(tags = "管理端,促销接口") +@RequestMapping("/manager/promotion") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PromotionManagerController { + + private final PromotionService promotionService; + + private final PromotionGoodsService promotionGoodsService; + + @GetMapping("/current") + @ApiOperation(value = "获取当前进行中的促销活动") + public ResultMessage> getCurrentPromotion() { + Map currentPromotion = promotionService.getCurrentPromotion(); + return ResultUtil.data(currentPromotion); + } + + @GetMapping("/{promotionId}/goods") + @ApiOperation(value = "获取当前进行中的促销活动商品") + public ResultMessage> getPromotionGoods(@PathVariable String promotionId, String promotionType, PageVO pageVO) { + IPage promotionGoods = promotionGoodsService.getCurrentPromotionGoods(promotionType, pageVO); + return ResultUtil.data(promotionGoods); + } + + +} diff --git a/manager-api/src/main/java/cn/lili/controller/promotion/SeckillManagerController.java b/manager-api/src/main/java/cn/lili/controller/promotion/SeckillManagerController.java new file mode 100644 index 00000000..5e1add64 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/promotion/SeckillManagerController.java @@ -0,0 +1,113 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dos.Seckill; +import cn.lili.modules.promotion.entity.dos.SeckillApply; +import cn.lili.modules.promotion.entity.enums.SeckillApplyStatusEnum; +import cn.lili.modules.promotion.entity.vos.SeckillSearchParams; +import cn.lili.modules.promotion.entity.vos.SeckillVO; +import cn.lili.modules.promotion.service.SeckillApplyService; +import cn.lili.modules.promotion.service.SeckillService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 管理端,限时抢购接口 + * + * @author paulG + * @date 2020/8/20 + **/ +@RestController +@Api(tags = "管理端,限时抢购接口") +@RequestMapping("/manager/promotion/seckill") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SeckillManagerController { + + private final SeckillService seckillService; + private final SeckillApplyService seckillApplyService; + + @PostMapping + @ApiOperation(value = "添加限时抢购") + public ResultMessage addSeckill(SeckillVO seckillVO) { + AuthUser currentUser = UserContext.getCurrentUser(); + seckillVO.setStoreId(currentUser.getId()); + seckillVO.setStoreName(currentUser.getUsername()); + seckillVO.setSeckillApplyStatus(SeckillApplyStatusEnum.NOT_APPLY.name()); + if (seckillService.saveSeckill(seckillVO)) { + return ResultUtil.data(seckillVO); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @PutMapping + @ApiOperation(value = "修改限时抢购") + public ResultMessage updateSeckill(SeckillVO seckillVO) { + AuthUser currentUser = UserContext.getCurrentUser(); + seckillVO.setStoreId(currentUser.getId()); + seckillVO.setStoreName(currentUser.getUsername()); + if (seckillService.modifySeckill(seckillVO)) { + return ResultUtil.data(seckillVO); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @GetMapping(value = "/{id}") + @ApiOperation(value = "通过id获取") + public ResultMessage get(@PathVariable String id) { + Seckill seckill = seckillService.getById(id); + return ResultUtil.data(seckill); + } + + @GetMapping + @ApiOperation(value = "分页查询限时抢购列表") + public ResultMessage> getAll(SeckillSearchParams param, PageVO pageVo) { + pageVo.setNotConvert(true); + IPage page = seckillService.getSeckillByPageFromMongo(param, pageVo); + return ResultUtil.data(page); + } + + @DeleteMapping("/{id}") + @ApiOperation(value = "删除一个限时抢购") + public ResultMessage deleteSeckill(@PathVariable String id) { + seckillService.deleteSeckill(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @PutMapping("/close/{id}") + @ApiOperation(value = "关闭一个限时抢购") + public ResultMessage closeSeckill(@PathVariable String id) { + seckillService.closeSeckill(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @PutMapping("/open/{id}") + @ApiOperation(value = "一个限时抢购") + public ResultMessage openSeckill(@PathVariable String id) { + seckillService.openSeckill(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @GetMapping("/apply") + @ApiOperation(value = "获取限时抢购申请列表") + public ResultMessage> getSeckillApply(SeckillSearchParams param, PageVO pageVo) { + IPage seckillApply = seckillApplyService.getSeckillApplyFromMongo(param, pageVo); + return ResultUtil.data(seckillApply); + } + + @PutMapping("/apply/audit/{ids}") + @ApiOperation(value = "审核多个限时抢购申请") + public ResultMessage auditSeckillApply(@PathVariable String[] ids, String seckillId, String applyStatus, String failReason) { + seckillApplyService.auditBatchApply(ids, seckillId, applyStatus, failReason); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/purchase/PurchaseManagerController.java b/manager-api/src/main/java/cn/lili/controller/purchase/PurchaseManagerController.java new file mode 100644 index 00000000..f24904cc --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/purchase/PurchaseManagerController.java @@ -0,0 +1,84 @@ +package cn.lili.controller.purchase; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.purchase.entity.dos.PurchaseOrder; +import cn.lili.modules.purchase.entity.dos.PurchaseQuoted; +import cn.lili.modules.purchase.entity.params.PurchaseOrderSearchParams; +import cn.lili.modules.purchase.entity.vos.PurchaseOrderVO; +import cn.lili.modules.purchase.entity.vos.PurchaseQuotedVO; +import cn.lili.modules.purchase.service.PurchaseOrderService; +import cn.lili.modules.purchase.service.PurchaseQuotedService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 管理端,采购接口 + * + * @author Chopper + * @date: 2020/11/16 10:06 下午 + */ +@Api(tags = "管理端,采购接口") +@RestController +@RequestMapping("/manager/purchase") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PurchaseManagerController { + + /** + * 采购单 + */ + private final PurchaseOrderService purchaseOrderService; + /** + * 采购单报价 + */ + private final PurchaseQuotedService purchaseQuotedService; + + + @ApiOperation(value = "采购单分页") + @GetMapping + public ResultMessage> get(PurchaseOrderSearchParams purchaseOrderSearchParams) { + return ResultUtil.data(purchaseOrderService.page(purchaseOrderSearchParams)); + } + + @ApiOperation(value = "采购单详情") + @ApiImplicitParam(name = "id", value = "采购单ID", required = true, dataType = "Long", paramType = "path") + @GetMapping("/{id}") + public ResultMessage getPurchaseOrder(@NotNull @PathVariable String id) { + return ResultUtil.data(purchaseOrderService.getPurchaseOrder(id)); + } + + @ApiOperation(value = "关闭采购单") + @ApiImplicitParam(name = "id", value = "采购单ID", required = true, dataType = "Long", paramType = "path") + @PutMapping("/{id}") + public ResultMessage close(@NotNull @PathVariable String id) { + + if (purchaseOrderService.close(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "报价列表") + @ApiImplicitParam(name = "purchaseOrderId", value = "采购单ID", required = true, dataType = "String", paramType = "path") + @GetMapping("/purchaseQuoted/list/{purchaseOrderId}") + public ResultMessage> get(@NotNull @PathVariable String purchaseOrderId) { + return ResultUtil.data(purchaseQuotedService.getByPurchaseOrderId(purchaseOrderId)); + } + + @ApiOperation(value = "报价单详情") + @ApiImplicitParam(name = "id", value = "报价单ID", required = true, dataType = "String", paramType = "path") + @GetMapping("/purchaseQuoted/{id}") + public ResultMessage getPurchaseQuoted(@NotNull @PathVariable String id) { + return ResultUtil.data(purchaseQuotedService.getById(id)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/AppVersionManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/AppVersionManagerController.java new file mode 100755 index 00000000..9c54a7c5 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/AppVersionManagerController.java @@ -0,0 +1,78 @@ +package cn.lili.controller.setting; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.utils.StringUtils; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.system.entity.dos.AppVersionDO; +import cn.lili.modules.system.service.AppVersionService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 管理端,app版本控制器 + * + * @author Chopper + * @date 2018-07-04 21:50:52 + */ +@RestController +@Api("管理端,app版本控制器") +@RequestMapping("/manager/systems/app/version") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AppVersionManagerController { + + private final AppVersionService appVersionService; + + + @ApiOperation(value = "查询app升级消息", response = AppVersionDO.class) + @GetMapping + @ApiImplicitParams({ + @ApiImplicitParam(name = "type", value = "APP类型", required = true, dataType = "type", paramType = "query") + }) + public ResultMessage> getByPage(PageVO page, String type) { + return ResultUtil.data(this.appVersionService.page(PageUtil.initPage(page), + new QueryWrapper().eq(StringUtils.isNotEmpty(type), "type", type).orderByDesc("create_time"))); + } + + + @ApiOperation(value = "添加app版本信息", response = AppVersionDO.class) + @PostMapping + public ResultMessage add(@Valid AppVersionDO appVersionDO) { + return ResultUtil.data(this.appVersionService.save(appVersionDO)); + } + + @PutMapping(value = "/{id}") + @ApiOperation(value = "修改app版本信息", response = AppVersionDO.class) + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "主键", required = true, dataType = "String", paramType = "path") + }) + public ResultMessage edit(@Valid AppVersionDO appVersionDO, @PathVariable String id) { + if (appVersionService.getById(id) != null) { + return ResultUtil.data(this.appVersionService.updateById(appVersionDO)); + } + return ResultUtil.data(false); + } + + @DeleteMapping(value = "/{id}") + @ApiOperation(value = "删除app版本") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "要删除的app版本主键", required = true, dataType = "String", paramType = "path") + }) + public ResultMessage delete(@PathVariable String id) { + if (appVersionService.getById(id) != null) { + return ResultUtil.data(this.appVersionService.removeById(id)); + } + return ResultUtil.data(true); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/InstantDeliveryManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/InstantDeliveryManagerController.java new file mode 100644 index 00000000..3c1e1c93 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/InstantDeliveryManagerController.java @@ -0,0 +1,94 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.system.entity.dos.InstantDelivery; +import cn.lili.modules.system.entity.plugin.ConfigItem; +import cn.lili.modules.system.entity.vo.InstantDeliveryVO; +import cn.lili.modules.system.service.InstantDeliveryService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 管理端,即时配送接口 + * + * @author pikachu + * @date: 2020/11/17 7:56 下午 + */ +@RestController +@Api(tags = "管理端,即时配送接口") +@RequestMapping("/manager/instant-delivery") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class InstantDeliveryManagerController { + + private final InstantDeliveryService instantDeliveryService; + + @GetMapping(value = "/getByPage") + @ApiOperation(value = "分页获取") + public ResultMessage> getByPage(PageVO page) { + //查询数据 + IPage data = instantDeliveryService.page(PageUtil.initPage(page)); + //组织数据结构 + IPage newData = instantDeliveryService.getInstantDeliveryPage(data, page); + System.out.println(); + //返回数据 + return ResultUtil.data(newData); + } + + @ApiOperation(value = "修改即时配送方案参数", response = InstantDeliveryVO.class) + @PutMapping(value = "/{bean}/config") + @ApiImplicitParams({ + @ApiImplicitParam(name = "bean", value = "即时配送bean", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "config", value = "即时配送参数", required = true, dataType = "String", paramType = "body") + }) + public ResultMessage edit(@PathVariable String bean, @RequestBody List config) { + InstantDeliveryVO instantDeliveryVO = new InstantDeliveryVO(); + instantDeliveryVO.setDeliveryBean(bean); + instantDeliveryVO.setConfigItems(config); + return ResultUtil.data(this.instantDeliveryService.edit(instantDeliveryVO)); + } + + @ApiOperation(value = "获取即时配送的配置", response = InstantDeliveryVO.class) + @GetMapping("/{bean}") + @ApiImplicitParam(name = "bean", value = "即时配送bean id", required = true, dataType = "String", paramType = "path") + public ResultMessage getInstantDeliverySetting(@PathVariable String bean) { + return ResultUtil.data(this.instantDeliveryService.getInstantDeliveryConfig(bean)); + } + + + @ApiOperation(value = "开启即时配送方案", response = String.class) + @PutMapping("/{bean}/open") + @ApiImplicitParam(name = "bean", value = "bean", required = true, dataType = "String", paramType = "path") + public ResultMessage open(@PathVariable String bean) { + this.instantDeliveryService.openInstantDelivery(bean); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "修改封面图片", response = String.class) + @PutMapping("/{bean}/image") + @ApiImplicitParams({ + @ApiImplicitParam(name = "bean", value = "即时配送bean", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "images", value = "封面图片", required = true, dataType = "String", paramType = "query") + }) + public ResultMessage open(@PathVariable String bean, String images) { + InstantDelivery instantDelivery = this.instantDeliveryService.getOne(new QueryWrapper().eq("delivery_bean", bean)); + if (instantDelivery != null) { + instantDelivery.setImages(images); + return ResultUtil.data(instantDeliveryService.updateById(instantDelivery)); + } + return ResultUtil.data(false); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/LogManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/LogManagerController.java new file mode 100644 index 00000000..5d6226bb --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/LogManagerController.java @@ -0,0 +1,65 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.permission.service.SystemLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,日志管理接口 + * + * @author Chopper + * @date: 2020/11/17 7:56 下午 + */ +@Slf4j +@Transactional +@RestController +@Api(tags = "日志管理接口") +@RequestMapping("/manager/log") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class LogManagerController { + + private final SystemLogService systemLogService; + + @GetMapping(value = "/getAllByPage") + @ApiOperation(value = "分页获取全部") + public ResultMessage getAllByPage(@RequestParam(required = false) Integer type, + @RequestParam String key, + String operatorName, + SearchVO searchVo, + PageVO pageVo) { + try { + return ResultUtil.data(systemLogService.queryLog(null, operatorName, key, searchVo, pageVo)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + @ApiOperation(value = "批量删除") + @DeleteMapping(value = "/{ids}") + public ResultMessage delByIds(@PathVariable List ids) { + systemLogService.deleteLog(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @DeleteMapping + @ApiOperation(value = "全部删除") + public ResultMessage delAll() { + systemLogService.flushAll(); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/LogisticsManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/LogisticsManagerController.java new file mode 100644 index 00000000..c1831b12 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/LogisticsManagerController.java @@ -0,0 +1,70 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.system.entity.dos.Logistics; +import cn.lili.modules.system.service.LogisticsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * 管理端,物流公司接口 + * + * @author Chopper + * @date: 2020/11/17 7:56 下午 + */ +@RestController +@Api(tags = "管理端,物流公司接口") +@RequestMapping("/manager/logistics") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class LogisticsManagerController { + + private final LogisticsService logisticsService; + + @ApiOperation(value = "通过id获取物流公司") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(logisticsService.getById(id)); + } + + @ApiOperation(value = "分页获取物流公司") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(PageVO page) { + return ResultUtil.data(logisticsService.page(PageUtil.initPage(page))); + } + + @ApiOperation(value = "编辑物流公司") + @ApiImplicitParam(name = "id", value = "物流公司ID", required = true, paramType = "path", dataType = "string") + @PutMapping(value = "/{id}") + public ResultMessage update(@NotNull @PathVariable String id, @Valid Logistics logistics) { + logistics.setId(id); + logisticsService.updateById(logistics); + return ResultUtil.data(logistics); + } + + @ApiOperation(value = "添加物流公司") + @PostMapping(value = "/save") + public ResultMessage save(@Valid Logistics logistics) { + logisticsService.save(logistics); + return ResultUtil.data(logistics); + } + + @ApiOperation(value = "删除物流公司") + @ApiImplicitParam(name = "id", value = "物流公司ID", required = true, dataType = "String", paramType = "path") + @DeleteMapping(value = "/delete/{id}") + public ResultMessage delAllByIds(@PathVariable String id) { + logisticsService.removeById(id); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/MemberNoticeManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/MemberNoticeManagerController.java new file mode 100644 index 00000000..2b7b30e7 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/MemberNoticeManagerController.java @@ -0,0 +1,87 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.MemberNotice; +import cn.lili.modules.member.service.MemberNoticeService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 管理端,会员站内信管理接口 + * + * @author Chopper + * @date: 2020/11/17 4:31 下午 + */ +@RestController +@Api(tags = "管理端,会员站内信管理API") +@RequestMapping("/manager/member/notice") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberNoticeManagerController { + + private final MemberNoticeService memberNoticeService; + + @ApiOperation(value = "获取详情") + @GetMapping(value = "/{id}") + public ResultMessage get(@PathVariable String id) { + MemberNotice memberNotice = memberNoticeService.getById(id); + return ResultUtil.data(memberNotice); + } + + @ApiOperation(value = "分页获取站内信") + @GetMapping(value = "/page") + public ResultMessage> getByPage( + PageVO page) { + IPage data = memberNoticeService.page(PageUtil.initPage(page)); + return ResultUtil.data(data); + } + + @ApiOperation(value = "阅读消息") + @PostMapping("/read/{ids}") + public ResultMessage read(@PathVariable List ids) { + UpdateWrapper updateWrapper = new UpdateWrapper(); + updateWrapper.in("id", ids); + updateWrapper.set("is_read", true); + memberNoticeService.update(updateWrapper); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "阅读全部") + @PostMapping("/read/all") + public ResultMessage readAll() { + UpdateWrapper updateWrapper = new UpdateWrapper(); + updateWrapper.in("member_id", UserContext.getCurrentUser().getId()); + updateWrapper.set("is_read", true); + memberNoticeService.update(updateWrapper); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "批量删除") + @DeleteMapping(value = "/{ids}") + public ResultMessage delAllByIds(@PathVariable List ids) { + memberNoticeService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "删除所有") + @DeleteMapping + public ResultMessage deleteAll() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("member_id", UserContext.getCurrentUser().getId()); + memberNoticeService.remove(queryWrapper); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/NoticeMessageManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/NoticeMessageManagerController.java new file mode 100644 index 00000000..80e534af --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/NoticeMessageManagerController.java @@ -0,0 +1,134 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.enums.SwitchEnum; +import cn.lili.common.utils.BeanUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.message.entity.dos.NoticeMessage; +import cn.lili.modules.message.entity.dto.NoticeMessageDetailDTO; +import cn.lili.modules.message.entity.enums.NoticeMessageParameterEnum; +import cn.lili.modules.message.service.NoticeMessageService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.EnumUtils; +import org.elasticsearch.ResourceNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; + + +/** + * 管理端,会员站内信管理接口 + * + * @author Chopper + * @date: 2020/11/17 4:31 下午 + */ +@Slf4j +@RestController +@Api(tags = "管理端,会员站内信管理接口") +@RequestMapping("/manager/noticeMessage") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class NoticeMessageManagerController { + + private final NoticeMessageService noticeMessageService; + + @ApiOperation(value = "消息模板分页") + @GetMapping + @ApiImplicitParams({ + @ApiImplicitParam(name = "type", value = "消息类型", dataType = "String", paramType = "query", + allowableValues = "MEMBER,OTHER,STORE,WECHAT", allowMultiple = true) + }) + public ResultMessage> getPage(PageVO pageVO, String type) { + return ResultUtil.data(noticeMessageService.getMessageTemplate(pageVO, type)); + } + + + @ApiOperation(value = "根据id获取通知详情") + @ApiImplicitParam(name = "id", value = "模板id", dataType = "String", paramType = "path") + @GetMapping("/{id}") + public ResultMessage get(@PathVariable String id) { + //根据id获取当前消息模板 + NoticeMessage noticeMessage = noticeMessageService.getById(id); + if (noticeMessage != null) { + NoticeMessageDetailDTO noticeMessageDetailDTO = new NoticeMessageDetailDTO(); + BeanUtil.copyProperties(noticeMessage, noticeMessageDetailDTO); + //组织消息变量 + String[] variableNames = noticeMessage.getVariable().split(","); + //定义返回参数中变量列表 + List variableValues = new ArrayList<>(); + //循环消息变量赋值 + if (variableNames.length > 0) { + for (String variableName : variableNames) { + if (NoticeMessageParameterEnum.getValueByType(variableName) != null) { + variableValues.add(NoticeMessageParameterEnum.getValueByType(variableName)); + } + } + noticeMessageDetailDTO.setVariables(variableValues); + } + + return ResultUtil.data(noticeMessageDetailDTO); + } + throw new ResourceNotFoundException(ResultCode.NOTICE_NOT_EXIST.message()); + } + + + @ApiOperation(value = "修改站内信模板") + @ApiImplicitParams({ + @ApiImplicitParam(name = "noticeContent", value = "站内信内容", dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "noticeTitle", value = "站内信模板标题", dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "id", value = "模板id", dataType = "String", paramType = "path") + }) + @PutMapping("/{id}") + public ResultMessage updateNoticeTemplate(@RequestParam String noticeContent, + @RequestParam String noticeTitle, + @PathVariable String id) { + //根据id获取当前消息模板 + NoticeMessage noticeMessage = noticeMessageService.getById(id); + if (noticeMessage != null) { + noticeMessage.setNoticeContent(noticeContent); + noticeMessage.setNoticeTitle(noticeTitle); + boolean result = noticeMessageService.updateById(noticeMessage); + if (result) { + return ResultUtil.data(noticeMessage); + } + return ResultUtil.error(ResultCode.ERROR); + } + throw new ResourceNotFoundException(ResultCode.NOTICE_NOT_EXIST.message()); + } + + @ApiOperation(value = "修改站内信状态") + + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "站内信状态", dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "status", value = "站内信状态", dataType = "String", paramType = "path") + }) + @PutMapping("/{id}/{status}") + public ResultMessage status(@PathVariable String id, @PathVariable String status) { + //根据id获取当前消息模板 + NoticeMessage messageTemplate = noticeMessageService.getById(id); + if (messageTemplate != null) { + //校验传递站内信是否符合规范 + Boolean b = EnumUtils.isValidEnum(SwitchEnum.class, status); + if (b) { + //赋值执行修改操作 + messageTemplate.setNoticeStatus(status); + boolean result = noticeMessageService.updateById(messageTemplate); + if (result) { + return ResultUtil.data(messageTemplate); + } + } + return ResultUtil.error(ResultCode.ERROR); + } + throw new ResourceNotFoundException(ResultCode.NOTICE_NOT_EXIST.message()); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/RegionManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/RegionManagerController.java new file mode 100644 index 00000000..dc5a53ce --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/RegionManagerController.java @@ -0,0 +1,83 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.base.service.RegionService; +import cn.lili.modules.system.entity.dos.Region; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + + +/** + * 管理端,行政地区管理接口 + * + * @author Chopper + * @date 2020/12/2 10:40 + */ +@RestController +@Api(tags = "管理端,行政地区管理接口") +@RequestMapping("/manager/region") +@Transactional +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RegionManagerController { + + private final RegionService regionService; + + @PostMapping(value = "/sync") + @ApiOperation(value = "同步高德行政地区数据") + public void synchronizationData(String url) { + regionService.synchronizationData(url); + } + + @GetMapping(value = "/{id}") + @ApiImplicitParam(name = "id", value = "地区ID", required = true, dataType = "String", paramType = "path") + @ApiOperation(value = "通过id获取地区详情") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(regionService.getById(id)); + } + + @GetMapping(value = "/item/{id}") + @ApiImplicitParam(name = "id", value = "地区ID", required = true, dataType = "String", paramType = "path") + @ApiOperation(value = "通过id获取子地区") + public ResultMessage> getItem(@PathVariable String id) { + return ResultUtil.data(regionService.getItem(id)); + } + + @PutMapping(value = "/{id}") + @ApiImplicitParam(name = "id", value = "地区ID", required = true, dataType = "String", paramType = "path") + @ApiOperation(value = "更新地区") + public ResultMessage update(@PathVariable String id, @Valid Region region) { + region.setId(id); + if (regionService.updateById(region)) { + return ResultUtil.data(region); + } + return ResultUtil.error(ResultCode.ERROR); + } + + + @PostMapping + @ApiOperation(value = "增加地区") + public ResultMessage save(@Valid Region region) { + if (regionService.save(region)) { + return ResultUtil.data(region); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @DeleteMapping(value = "{ids}") + @ApiImplicitParam(name = "id", value = "地区ID", required = true, dataType = "String", allowMultiple = true, paramType = "path") + @ApiOperation(value = "批量通过id删除") + public ResultMessage delAllByIds(@PathVariable List ids) { + regionService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/ServiceNoticeManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/ServiceNoticeManagerController.java new file mode 100644 index 00000000..932ea8c7 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/ServiceNoticeManagerController.java @@ -0,0 +1,77 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.system.entity.dos.ServiceNotice; +import cn.lili.modules.system.service.ServiceNoticeService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,服务订阅消息接口 + * + * @author Chopper + * @date: 2020/11/17 4:33 下午 + */ +@RestController +@Api(tags = "管理端,服务订阅消息接口") +@RequestMapping("/manager/admin/notice") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ServiceNoticeManagerController { + + private final ServiceNoticeService serviceNoticeService; + + @ApiOperation(value = "查看服务订阅消息详情") + @GetMapping(value = "/{id}") + public ResultMessage get(@PathVariable String id) { + ServiceNotice serviceNotice = serviceNoticeService.getById(id); + return ResultUtil.data(serviceNotice); + } + + @ApiOperation(value = "分页获取服务订阅消息") + @GetMapping(value = "/page") + public ResultMessage> getByPage(ServiceNotice entity, + SearchVO searchVo, + PageVO page) { + IPage data = serviceNoticeService.page(PageUtil.initPage(page), PageUtil.initWrapper(entity, searchVo)); + return ResultUtil.data(data); + } + + @ApiOperation(value = "新增服务订阅消息") + @PostMapping + public ResultMessage save(ServiceNotice serviceNotice) { + //标记平台消息 + serviceNotice.setStoreId("-1"); + if (serviceNoticeService.saveOrUpdate(serviceNotice)) { + return ResultUtil.data(serviceNotice); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "更新服务订阅消息") + @PostMapping("/{id}") + public ResultMessage update(@PathVariable String id, ServiceNotice serviceNotice) { + if (serviceNoticeService.saveOrUpdate(serviceNotice)) { + return ResultUtil.data(serviceNotice); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "删除服务订阅消息") + @DeleteMapping(value = "/{ids}") + public ResultMessage delAllByIds(@PathVariable List ids) { + serviceNoticeService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/SettingManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/SettingManagerController.java new file mode 100644 index 00000000..7d5dc4bf --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/SettingManagerController.java @@ -0,0 +1,195 @@ +package cn.lili.controller.setting; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.base.aspect.DemoSite; +import cn.lili.modules.system.entity.dos.Setting; +import cn.lili.modules.system.entity.dto.*; +import cn.lili.modules.system.entity.dto.connect.QQConnectSetting; +import cn.lili.modules.system.entity.dto.connect.WechatConnectSetting; +import cn.lili.modules.system.entity.dto.payment.AlipayPaymentSetting; +import cn.lili.modules.system.entity.dto.payment.PaymentSupportSetting; +import cn.lili.modules.system.entity.dto.payment.WechatPaymentSetting; +import cn.lili.modules.system.entity.dto.payment.dto.PaymentSupportForm; +import cn.lili.modules.system.entity.enums.SettingEnum; +import cn.lili.modules.system.service.SettingService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; + +/** + * 管理端,系统设置接口 + * + * @author Chopper + * @date 2020/11/26 15:53 + */ +@RestController +@Api(tags = "管理端,系统设置接口") +@RequestMapping("/manager/system/setting") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SettingManagerController { + + private final SettingService settingService; + + + @DemoSite + @ApiOperation(value = "更新配置") + @PutMapping(value = "/put/{key}") + @ApiImplicitParam(name = "key", value = "配置key", paramType = "path", + allowableValues = "BASE_SETTING,EMAIL_SETTING,GOODS_SETTING,KUAIDI_SETTING,ORDER_SETTING,OSS_SETTING,POINT_SETTING," + + "WECHAT_PC_CONNECT,WECHAT_WAP_CONNECT,WECHAT_APP_CONNECT,WECHAT_MP_CONNECT," + + "QQ_WEB_CONNECT,QQ_APP_CONNECT," + + "QQ_WEB_CONNECT,QQ_APP_CONNECT,WEIBO_CONNECT,ALIPAY_CONNECT," + + "PAYMENT_SUPPORT,ALIPAY_PAYMENT,WECHAT_PAYMENT") + public ResultMessage saveConfig(@PathVariable String key, @RequestBody String configValue) { + SettingEnum settingEnum = SettingEnum.valueOf(key); + //获取系统配置 + Setting setting = settingService.getById(settingEnum.name()); + if (setting == null) { + setting = new Setting(); + setting.setId(settingEnum.name()); + } + //特殊配置过滤 + configValue = filter(settingEnum, configValue); + + setting.setSettingValue(configValue); + settingService.saveUpdate(setting); + return ResultUtil.success(ResultCode.SUCCESS); + } + + /** + * 对配置进行过滤 + * + * @param settingEnum + * @param configValue + */ + private String filter(SettingEnum settingEnum, String configValue) { + if (settingEnum.equals(SettingEnum.POINT_SETTING)) { + PointSetting pointSetting = JSONUtil.toBean(configValue, PointSetting.class); + if (pointSetting.getPointSettingItems() != null && pointSetting.getPointSettingItems().size() > 0) { + Collections.sort(pointSetting.getPointSettingItems()); + if (pointSetting.getPointSettingItems().size() > 4) { + pointSetting.setPointSettingItems(pointSetting.getPointSettingItems().subList(0, 4)); + } + } + configValue = JSONUtil.toJsonStr(pointSetting); + } + return configValue; + } + + + @DemoSite + @ApiOperation(value = "查看配置") + @GetMapping(value = "/get/{key}") + @ApiImplicitParam(name = "key", value = "配置key", paramType = "path" + , allowableValues = "BASE_SETTING,EMAIL_SETTING,GOODS_SETTING,KUAIDI_SETTING,ORDER_SETTING,OSS_SETTING,POINT_SETTING," + + "WECHAT_PC_CONNECT,WECHAT_WAP_CONNECT,WECHAT_APP_CONNECT,WECHAT_MP_CONNECT," + + "QQ_WEB_CONNECT,QQ_APP_CONNECT," + + "QQ_WEB_CONNECT,QQ_APP_CONNECT,WEIBO_CONNECT,ALIPAY_CONNECT," + + "PAYMENT_SUPPORT,ALIPAY_PAYMENT,WECHAT_PAYMENT" + ) + public ResultMessage settingGet(@PathVariable String key) { + try { + return createSetting(key); + } catch (Exception e) { + e.printStackTrace(); + return ResultUtil.error(ResultCode.ERROR); + } + } + + /** + * 获取表单 + * 这里主要包含一个配置对象为空,导致转换异常问题的处理,解决配置项增加减少,带来的系统异常,无法直接配置 + * + * @param key + * @return + * @throws InstantiationException + * @throws IllegalAccessException + */ + private ResultMessage createSetting(String key) { + SettingEnum settingEnum = SettingEnum.valueOf(key); + + Setting setting = settingService.get(key); + switch (settingEnum) { + case BASE_SETTING: + return setting == null ? + ResultUtil.data(new BaseSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), BaseSetting.class)); + + case WITHDRAWAL_SETTING: + return setting == null ? + ResultUtil.data(new WithdrawalSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), WithdrawalSetting.class)); + case DISTRIBUTION_SETTING: + return setting == null ? + ResultUtil.data(new DistributionSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), DistributionSetting.class)); + + case EMAIL_SETTING: + return setting == null ? + ResultUtil.data(new EmailSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), EmailSetting.class)); + case GOODS_SETTING: + return setting == null ? + ResultUtil.data(new GoodsSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), GoodsSetting.class)); + case KUAIDI_SETTING: + return setting == null ? + ResultUtil.data(new KuaidiSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), KuaidiSetting.class)); + case ORDER_SETTING: + return setting == null ? + ResultUtil.data(new OrderSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), OrderSetting.class)); + + case OSS_SETTING: + return setting == null ? + ResultUtil.data(new OssSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), OssSetting.class)); + + case SMS_SETTING: + return setting == null ? + ResultUtil.data(new SmsSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), SmsSetting.class)); + + case POINT_SETTING: + return setting == null ? + ResultUtil.data(new PointSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), PointSetting.class)); + + case QQ_CONNECT: + return setting == null ? + ResultUtil.data(new QQConnectSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), QQConnectSetting.class)); + + case PAYMENT_SUPPORT: + return setting == null ? + ResultUtil.data(new PaymentSupportSetting(new PaymentSupportForm())) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), PaymentSupportSetting.class)); + + case ALIPAY_PAYMENT: + return setting == null ? + ResultUtil.data(new AlipayPaymentSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), AlipayPaymentSetting.class)); + + case WECHAT_CONNECT: + return setting == null ? + ResultUtil.data(new WechatConnectSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), WechatConnectSetting.class)); + + case WECHAT_PAYMENT: + return setting == null ? + ResultUtil.data(new WechatPaymentSetting()) : + ResultUtil.data(JSONUtil.toBean(setting.getSettingValue(), WechatPaymentSetting.class)); + default: + return ResultUtil.error(ResultCode.SETTING_NOT_TO_SET); + } + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/SettingXManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/SettingXManagerController.java new file mode 100644 index 00000000..988f24d9 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/SettingXManagerController.java @@ -0,0 +1,33 @@ +package cn.lili.controller.setting; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.system.entity.dto.payment.dto.PaymentSupportForm; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +/** + * 管理端,系统设置扩展接口 + * 对一些系统设置的支持,例如动态表单等 + * + * @author Chopper + * @date 2020/11/26 15:53 + */ +@RestController +@Api(tags = "管理端,系统设置扩展接口") +@RequestMapping("/manager/system/settingx") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SettingXManagerController { + + @ApiOperation(value = "支持支付方式表单") + @GetMapping("/paymentSupport") + public ResultMessage paymentForm() { + return ResultUtil.data(new PaymentSupportForm()); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/SmsManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/SmsManagerController.java new file mode 100644 index 00000000..7ef4e3ef --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/SmsManagerController.java @@ -0,0 +1,54 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.message.entity.dos.SmsReach; +import cn.lili.modules.message.service.SmsReachService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 管理端,短信接口 + * + * @author Bulbasaur + * @date: 2021/1/30 4:09 下午 + */ +@RestController +@Api(tags = "管理端,短信接口") +@RequestMapping("/manager/sms") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SmsManagerController { + + private final SmsReachService smsReachService; + + @ApiOperation(value = "接口批量发送短信") + @PostMapping + public ResultMessage sendBatchSms(SmsReach smsReach, @RequestParam(value = "mobile") List mobile) { + smsReachService.addSmsReach(smsReach,mobile); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "查询短信任务分页") + @GetMapping() + public ResultMessage> querySmsReachPage(PageVO page) { + return ResultUtil.data(smsReachService.page(PageUtil.initPage(page))); + } + + @ApiOperation(value = "查询短信任务") + @ApiImplicitParam(name = "id", value = "短信任务id", required = true, dataType = "String", allowMultiple = true, paramType = "path") + @GetMapping("/{id}") + public ResultMessage querySmsReach(@PathVariable String id) { + return ResultUtil.data(smsReachService.getById(id)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/SmsSignManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/SmsSignManagerController.java new file mode 100644 index 00000000..2b866701 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/SmsSignManagerController.java @@ -0,0 +1,76 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.message.entity.dos.SmsSign; +import cn.lili.modules.message.service.SmsSignService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 管理端,短信签名接口 + * + * @author Chopper + * @date: 2021/1/30 4:09 下午 + */ +@RestController +@Api(tags = "管理端,短信签名接口") +@RequestMapping("/manager/sms/sign") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SmsSignManagerController { + private final SmsSignService smsSignService; + + + @ApiOperation(value = "新增短信签名") + @PostMapping + public ResultMessage save(@Valid SmsSign smsSign) { + smsSignService.addSmsSign(smsSign); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "删除短信签名") + @DeleteMapping("/{id}") + @ApiImplicitParam(name = "id", value = "短信签名id", required = true, dataType = "String", allowMultiple = true, paramType = "path") + public ResultMessage delete(@PathVariable String id) { + smsSignService.deleteSmsSign(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + + + @ApiOperation(value = "查询签名详细") + @GetMapping("/{id}") + @ApiImplicitParam(name = "id", value = "短信签名id", required = true, dataType = "String", allowMultiple = true, paramType = "path") + public ResultMessage getDetail(@PathVariable String id) { + return ResultUtil.data(smsSignService.getById(id)); + } + + @ApiOperation(value = "查询短信签名状态") + @PutMapping("/querySmsSign") + public ResultMessage querySmsSign() { + smsSignService.querySmsSign(); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "修改短信签名") + @PutMapping("/modifySmsSign") + public ResultMessage modifySmsSign(@Valid SmsSign smsSign) { + smsSignService.modifySmsSign(smsSign); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "查询短信签名分页") + @GetMapping("/querySmsSignPage") + public ResultMessage> querySmsSignPage(PageVO page, Integer signStatus) { + return ResultUtil.data(smsSignService.page(page, signStatus)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/SmsTemplateManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/SmsTemplateManagerController.java new file mode 100644 index 00000000..5dec0463 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/SmsTemplateManagerController.java @@ -0,0 +1,66 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.message.entity.dos.SmsTemplate; +import cn.lili.modules.message.service.SmsTemplateService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 管理端,短信模板接口 + * + * @author Bulbasaur + * @date: 2021/1/30 4:09 下午 + */ +@RestController +@Api(tags = "管理端,短信模板接口") +@RequestMapping("/manager/sms/template") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SmsTemplateManagerController { + private final SmsTemplateService smsTemplateService; + + @ApiOperation(value = "新增短信模板") + @PostMapping + public ResultMessage save(@Valid SmsTemplate smsTemplate) { + smsTemplateService.addSmsTemplate(smsTemplate); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "删除短信模板") + @ApiImplicitParam(name = "id", value = "短信模板ID", required = true, paramType = "path") + @DeleteMapping("/{id}") + public ResultMessage delete(@PathVariable("id") String id) { + smsTemplateService.deleteSmsTemplate(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "查询短信模板状态") + @PutMapping("/querySmsSign") + public ResultMessage querySmsSign() { + smsTemplateService.querySmsTemplate(); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "修改短信模板") + @PutMapping("/modifySmsTemplate") + public ResultMessage modifySmsTemplate(@Valid SmsTemplate smsTemplate) { + smsTemplateService.modifySmsTemplate(smsTemplate); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "查询短信模板分页") + @GetMapping("/querySmsTemplatePage") + public ResultMessage> querySmsTemplatePage(PageVO page, Integer templateStatus) { + return ResultUtil.data(smsTemplateService.page(page,templateStatus)); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/WechatMPMessageManagerController.java b/manager-api/src/main/java/cn/lili/controller/setting/WechatMPMessageManagerController.java new file mode 100644 index 00000000..3c77d3c7 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/WechatMPMessageManagerController.java @@ -0,0 +1,83 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.message.entity.dos.WechatMPMessage; +import cn.lili.modules.message.service.WechatMPMessageService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * @author Chopper + */ +@RestController +@Api(tags = "微信小程序消息订阅接口") +@RequestMapping("/manager/message/wechatMPMessage") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WechatMPMessageManagerController { + + private final WechatMPMessageService wechatMPMessageService; + + @GetMapping(value = "/init") + @ApiOperation(value = "初始化微信小程序消息订阅") + public ResultMessage init() { + wechatMPMessageService.init(); + return ResultUtil.success(ResultCode.SUCCESS); + } + @GetMapping(value = "/{id}") + @ApiOperation(value = "查看微信小程序消息订阅详情") + public ResultMessage get(@PathVariable String id) { + + WechatMPMessage wechatMPMessage = wechatMPMessageService.getById(id); + return new ResultUtil().setData(wechatMPMessage); + } + + @GetMapping + @ApiOperation(value = "分页获取微信小程序消息订阅") + public ResultMessage> getByPage(WechatMPMessage entity, + SearchVO searchVo, + PageVO page) { + IPage data = wechatMPMessageService.page(PageUtil.initPage(page), PageUtil.initWrapper(entity, searchVo)); + return new ResultUtil>().setData(data); + } + + @PostMapping + @ApiOperation(value = "新增微信小程序消息订阅") + public ResultMessage save(WechatMPMessage wechatMPMessage) { + + if (wechatMPMessageService.save(wechatMPMessage)) { + return new ResultUtil().setData(wechatMPMessage); + } + return new ResultUtil().setErrorMsg(ResultCode.ERROR); + } + + @PutMapping("/{id}") + @ApiOperation(value = "更新微信小程序消息订阅") + public ResultMessage update(@PathVariable String id, WechatMPMessage wechatMPMessage) { + if (wechatMPMessageService.updateById(wechatMPMessage)) { + return new ResultUtil().setData(wechatMPMessage); + } + return new ResultUtil().setErrorMsg(ResultCode.ERROR); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "删除微信小程序消息订阅") + public ResultMessage delAllByIds(@PathVariable List ids) { + + wechatMPMessageService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/setting/WechatMessageManageController.java b/manager-api/src/main/java/cn/lili/controller/setting/WechatMessageManageController.java new file mode 100644 index 00000000..ce803533 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/setting/WechatMessageManageController.java @@ -0,0 +1,82 @@ +package cn.lili.controller.setting; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.message.entity.dos.WechatMessage; +import cn.lili.modules.message.service.WechatMessageService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 管理端,微信消息接口 + * + * @author Chopper + * @date 2020/12/2 10:40 + */ +@RestController +@Api(tags = "管理端,微信消息接口") +@RequestMapping("/manager/message/wechat") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WechatMessageManageController { + + private final WechatMessageService wechatMessageService; + + + @GetMapping(value = "/init") + @ApiOperation(value = "初始化微信消息") + public ResultMessage init() { + wechatMessageService.init(); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @GetMapping(value = "/{id}") + @ApiOperation(value = "查看微信消息详情") + public ResultMessage get(@PathVariable String id) { + + WechatMessage wechatMessage = wechatMessageService.getById(id); + return ResultUtil.data(wechatMessage); + } + + @GetMapping + @ApiOperation(value = "分页获取微信消息") + public ResultMessage> getByPage(PageVO page) { + IPage data = wechatMessageService.page(PageUtil.initPage(page)); + return ResultUtil.data(data); + } + + @PostMapping + @ApiOperation(value = "新增微信消息") + public ResultMessage save(WechatMessage wechatMessage) { + + if (wechatMessageService.save(wechatMessage)) { + return ResultUtil.data(wechatMessage); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @PutMapping("/{id}") + @ApiOperation(value = "更新微信消息") + public ResultMessage update(@PathVariable String id, WechatMessage wechatMessage) { + if (wechatMessageService.updateById(wechatMessage)) { + return ResultUtil.data(wechatMessage); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "删除微信消息") + public ResultMessage delAllByIds(@PathVariable List ids) { + wechatMessageService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/statistics/GoodsStatisticsManagerController.java b/manager-api/src/main/java/cn/lili/controller/statistics/GoodsStatisticsManagerController.java new file mode 100644 index 00000000..f34809d0 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/statistics/GoodsStatisticsManagerController.java @@ -0,0 +1,49 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.statistics.model.dto.GoodsStatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.CategoryStatisticsDataVO; +import cn.lili.modules.statistics.model.vo.GoodsStatisticsDataVO; +import cn.lili.modules.statistics.service.GoodsStatisticsDataService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 管理端,商品统计接口 + * + * @author Bulbasaur + * @date: 2020/12/9 19:04 + */ +@Api(tags = "管理端,商品统计接口") +@RestController +@RequestMapping("/manager/statistics/goods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsStatisticsManagerController { + + private final GoodsStatisticsDataService goodsStatisticsDataService; + + @ApiOperation(value = "获取统计列表,排行前一百的数据") + @GetMapping + public ResultMessage> getByPage(GoodsStatisticsQueryParam goodsStatisticsQueryParam) { + try { + return ResultUtil.data(goodsStatisticsDataService.getGoodsStatisticsData(goodsStatisticsQueryParam, 100)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @ApiOperation(value = "获取行业统计列表") + @GetMapping("/getCategoryByPage") + public ResultMessage> getCategoryByPage(GoodsStatisticsQueryParam goodsStatisticsQueryParam) { + return ResultUtil.data(goodsStatisticsDataService.getCategoryStatisticsData(goodsStatisticsQueryParam)); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/statistics/IndexStatisticsManagerController.java b/manager-api/src/main/java/cn/lili/controller/statistics/IndexStatisticsManagerController.java new file mode 100644 index 00000000..d60b83d9 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/statistics/IndexStatisticsManagerController.java @@ -0,0 +1,65 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.statistics.model.vo.GoodsStatisticsDataVO; +import cn.lili.modules.statistics.model.vo.IndexNoticeVO; +import cn.lili.modules.statistics.model.vo.IndexStatisticsVO; +import cn.lili.modules.statistics.model.vo.StoreStatisticsDataVO; +import cn.lili.modules.statistics.service.IndexStatisticsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 管理端,首页统计数据接口 + * + * @author Bulbasaur + * @date: 2020/12/15 17:53 + */ +@Api(tags = "管理端,首页统计数据接口") +@RestController +@RequestMapping("/manager/statistics/index") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class IndexStatisticsManagerController { + + /** + * 首页统计 + */ + private final IndexStatisticsService indexStatisticsService; + + @ApiOperation(value = "获取首页查询数据") + @GetMapping + public ResultMessage index() { + try { + return ResultUtil.data(indexStatisticsService.indexStatistics()); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @ApiOperation(value = "获取首页查询热卖商品TOP10") + @GetMapping("/goodsStatistics") + public ResultMessage> goodsStatistics() { + return ResultUtil.data(indexStatisticsService.goodsStatisticsOfMonth()); + } + + @ApiOperation(value = "获取首页查询热卖店铺TOP10") + @GetMapping("/storeStatistics") + public ResultMessage> storeStatistics() { + return ResultUtil.data(indexStatisticsService.storeStatisticsOfMonth()); + } + + @ApiOperation(value = "通知提示信息") + @GetMapping("/notice") + public ResultMessage notice() { + return ResultUtil.data(indexStatisticsService.indexNotice()); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/statistics/MemberStatisticsManagerController.java b/manager-api/src/main/java/cn/lili/controller/statistics/MemberStatisticsManagerController.java new file mode 100644 index 00000000..78de296b --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/statistics/MemberStatisticsManagerController.java @@ -0,0 +1,37 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.statistics.model.dos.MemberStatisticsData; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.service.MemberStatisticsDataService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 管理端,会员统计接口 + * + * @author Bulbasaur + * @date: 2020/12/9 19:04 + */ +@Api(tags = "管理端,会员统计接口") +@RestController +@RequestMapping("/manager/statistics/member") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberStatisticsManagerController { + + private final MemberStatisticsDataService memberStatisticsDataService; + + @ApiOperation(value = "获取会员统计") + @GetMapping + public ResultMessage> getByList(StatisticsQueryParam statisticsQueryParam) { + return ResultUtil.data(memberStatisticsDataService.statistics(statisticsQueryParam)); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/statistics/OrderStatisticsManagerController.java b/manager-api/src/main/java/cn/lili/controller/statistics/OrderStatisticsManagerController.java new file mode 100644 index 00000000..a189ba53 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/statistics/OrderStatisticsManagerController.java @@ -0,0 +1,83 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import cn.lili.modules.order.order.service.AfterSaleService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.OrderOverviewVO; +import cn.lili.modules.statistics.model.vo.OrderStatisticsDataVO; +import cn.lili.modules.statistics.service.OrderStatisticsDataService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 管理端,订单统计接口 + * + * @author Bulbasaur + * @date: 2020/12/9 19:04 + */ +@Api(tags = "管理端,订单统计接口") +@RestController +@RequestMapping("/manager/statistics/order") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderStatisticsManagerController { + + private final OrderStatisticsDataService orderStatisticsDataService; + + private final OrderService orderService; + + private final AfterSaleService afterSaleService; + + @ApiOperation(value = "订单概览统计") + @GetMapping("/overview") + public ResultMessage overview(StatisticsQueryParam statisticsQueryParam) { + try { + return ResultUtil.data(orderStatisticsDataService.overview(statisticsQueryParam)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @ApiOperation(value = "订单图表统计") + @GetMapping + public ResultMessage> statisticsChart(StatisticsQueryParam statisticsQueryParam) { + try { + return ResultUtil.data(orderStatisticsDataService.statisticsChart(statisticsQueryParam)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + @ApiOperation(value = "订单统计") + @GetMapping("/order") + public ResultMessage> order(StatisticsQueryParam statisticsQueryParam, PageVO pageVO) { + try { + return ResultUtil.data(orderService.getStatistics(statisticsQueryParam, pageVO)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + @ApiOperation(value = "退单统计") + @GetMapping("/refund") + public ResultMessage> refund(StatisticsQueryParam statisticsQueryParam, PageVO pageVO) { + return ResultUtil.data(afterSaleService.getStatistics(statisticsQueryParam, pageVO)); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/statistics/RefundOrderStatisticsManagerController.java b/manager-api/src/main/java/cn/lili/controller/statistics/RefundOrderStatisticsManagerController.java new file mode 100644 index 00000000..a3752cee --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/statistics/RefundOrderStatisticsManagerController.java @@ -0,0 +1,43 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.RefundOrderStatisticsDataVO; +import cn.lili.modules.statistics.service.RefundOrderStatisticsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +/** + * 管理端,退款统计接口 + * + * @author Bulbasaur + * @date: 2020/12/9 19:04 + */ +@Api(tags = "管理端,退款统计接口") +@RestController +@RequestMapping("/manager/statistics/refund/order") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RefundOrderStatisticsManagerController { + + private final RefundOrderStatisticsService refundOrderStatisticsService; + + @ApiOperation(value = "获取退款统计列表") + @GetMapping("/getByPage") + public ResultMessage> getByPage(PageVO pageVO, StatisticsQueryParam statisticsQueryParam) { + return ResultUtil.data(refundOrderStatisticsService.getRefundOrderStatisticsData(pageVO, statisticsQueryParam)); + } + + @ApiOperation(value = "获取退款统计金额") + @GetMapping("/getPrice") + public ResultMessage getPrice(StatisticsQueryParam statisticsQueryParam) { + return ResultUtil.data(refundOrderStatisticsService.getRefundOrderStatisticsPrice(statisticsQueryParam)); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/statistics/ViewStatisticsManagerController.java b/manager-api/src/main/java/cn/lili/controller/statistics/ViewStatisticsManagerController.java new file mode 100644 index 00000000..46e512af --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/statistics/ViewStatisticsManagerController.java @@ -0,0 +1,59 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.vo.MemberDistributionVO; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.OnlineMemberVO; +import cn.lili.modules.statistics.model.vo.PlatformViewVO; +import cn.lili.modules.statistics.service.PlatformViewDataService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 管理端,流量统计接口 + * + * @author Chopper + * @date 2021/2/9 11:19 + */ +@Api(tags = "管理端,流量统计接口") +@RestController +@RequestMapping("/manager/statistics/view") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ViewStatisticsManagerController { + + private final PlatformViewDataService platformViewDataService; + + @ApiOperation(value = "流量数据 表单获取") + @GetMapping("/list") + public ResultMessage> getByPage(StatisticsQueryParam queryParam) { + return ResultUtil.data(platformViewDataService.list(queryParam)); + } + + @ApiOperation(value = "当前在线人数") + @GetMapping("/online/current") + public ResultMessage currentNumberPeopleOnline() { + return ResultUtil.data(platformViewDataService.online()); + } + + + @ApiOperation(value = "会员分布") + @GetMapping("/online/distribution") + public ResultMessage> memberDistribution() { + return ResultUtil.data(platformViewDataService.memberDistribution()); + } + + @ApiOperation(value = "在线人数历史(默认48小时)") + @GetMapping("/online/history") + public ResultMessage> history() { + return ResultUtil.data(platformViewDataService.statisticsOnline()); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/store/BillManagerController.java b/manager-api/src/main/java/cn/lili/controller/store/BillManagerController.java new file mode 100644 index 00000000..8b429536 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/store/BillManagerController.java @@ -0,0 +1,70 @@ +package cn.lili.controller.store; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.store.entity.dos.Bill; +import cn.lili.modules.store.entity.dto.BillSearchParams; +import cn.lili.modules.store.entity.vos.BillListVO; +import cn.lili.modules.store.service.BillService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 管理端,商家结算单接口 + * + * @author Chopper + * @date: 2020/11/17 7:23 下午 + */ +@RestController +@Api(tags = "管理端,商家结算单接口") +@RequestMapping("/manager/store/bill") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BillManagerController { + + private final BillService billService; + + @ApiOperation(value = "通过id获取结算单") + @ApiImplicitParam(name = "id", value = "结算单ID", required = true, paramType = "path") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable @NotNull String id) { + return ResultUtil.data(billService.getById(id)); + } + + @ApiOperation(value = "获取结算单分页") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(BillSearchParams billSearchParams) { + return ResultUtil.data(billService.billPage(billSearchParams)); + } + + @ApiOperation(value = "获取商家结算单流水分页") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "结算单ID", required = true, paramType = "path"), + @ApiImplicitParam(name = "flowType", value = "流水类型:PAY、REFUND", paramType = "query") + }) + @GetMapping(value = "/{id}/getStoreFlow") + public ResultMessage> getStoreFlow(@PathVariable String id, String flowType, PageVO pageVO) { + return ResultUtil.data(billService.getStoreFlow(id, flowType, pageVO)); + } + + @ApiOperation(value = "支付结算单") + @ApiImplicitParam(name = "id", value = "结算单ID", required = true, paramType = "path") + @PutMapping(value = "/pay/{id}") + public ResultMessage pay(@PathVariable String id) { + if (billService.complete(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/store/StoreManagerController.java b/manager-api/src/main/java/cn/lili/controller/store/StoreManagerController.java new file mode 100644 index 00000000..886441a6 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/store/StoreManagerController.java @@ -0,0 +1,131 @@ +package cn.lili.controller.store; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.vos.CategoryVO; +import cn.lili.modules.store.entity.dos.Store; +import cn.lili.modules.store.entity.dto.AdminStoreApplyDTO; +import cn.lili.modules.store.entity.dto.StoreEditDTO; +import cn.lili.modules.store.entity.vos.StoreDetailVO; +import cn.lili.modules.store.entity.vos.StoreSearchParams; +import cn.lili.modules.store.entity.vos.StoreVO; +import cn.lili.modules.store.service.StoreDetailService; +import cn.lili.modules.store.service.StoreService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +/** + * 管理端,店铺管理接口 + * + * @author Bulbasaur + * @date: 2020/12/6 16:09 + */ +@Api(tags = "管理端,店铺管理接口") +@RestController +@RequestMapping("/manager/store") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreManagerController { + + /** + * 店铺 + */ + private final StoreService storeService; + /** + * 店铺详情 + */ + private final StoreDetailService storeDetailService; + + @ApiOperation(value = "获取店铺分页列表") + @GetMapping("/all") + public ResultMessage> getALL() { + return ResultUtil.data(storeService.list(new QueryWrapper().eq("store_disable", "OPEN"))); + } + + @ApiOperation(value = "获取店铺分页列表") + @GetMapping + public ResultMessage> getByPage(StoreSearchParams entity, PageVO page) { + return ResultUtil.data(storeService.findByConditionPage(entity, page)); + } + + @ApiOperation(value = "获取店铺详情") + @ApiImplicitParam(name = "storeId", value = "店铺ID", required = true, paramType = "path", dataType = "String") + @GetMapping(value = "/get/detail/{storeId}") + public ResultMessage detail(@PathVariable String storeId) { + return ResultUtil.data(storeDetailService.getStoreDetailVO(storeId)); + } + + @ApiOperation(value = "添加店铺") + @PostMapping(value = "/add") + public ResultMessage add(@Valid AdminStoreApplyDTO adminStoreApplyDTO) { + return ResultUtil.data(storeService.add(adminStoreApplyDTO)); + } + + @ApiOperation(value = "编辑店铺") + @ApiImplicitParam(name = "storeId", value = "店铺ID", required = true, paramType = "path", dataType = "String") + @PutMapping(value = "/edit/{id}") + public ResultMessage edit(@PathVariable String id, @Valid StoreEditDTO storeEditDTO) { + storeEditDTO.setStoreId(id); + return ResultUtil.data(storeService.edit(storeEditDTO)); + } + + @ApiOperation(value = "审核店铺申请") + @ApiImplicitParams({ + @ApiImplicitParam(name = "passed", value = "是否通过审核 0 通过 1 拒绝 编辑操作则不需传递", paramType = "query", dataType = "int"), + @ApiImplicitParam(name = "id", value = "店铺id", required = true, paramType = "path", dataType = "String") + }) + @PutMapping(value = "/audit/{id}/{passed}") + public ResultMessage audit(@PathVariable String id, @PathVariable Integer passed) { + storeService.audit(id, passed); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "关闭店铺") + @ApiImplicitParam(name = "id", value = "店铺id", required = true, dataType = "String", paramType = "path") + @PutMapping(value = "/disable/{id}") + public ResultMessage disable(@PathVariable String id) { + if (storeService.disable(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "开启店铺") + @ApiImplicitParam(name = "id", value = "店铺id", required = true, dataType = "String", paramType = "path") + @PutMapping(value = "/enable/{id}") + public ResultMessage enable(@PathVariable String id) { + if (storeService.enable(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "查询一级分类列表") + @ApiImplicitParam(name = "storeId", value = "店铺id", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/ManagementCategory/{storeId}") + public ResultMessage> firstCategory(String storeId) { + return ResultUtil.data(this.storeDetailService.goodsManagementCategory(storeId)); + } + + + @ApiOperation(value = "根据会员id查询店铺信息") + @GetMapping("/{memberId}/member") + public ResultMessage getByMemberId(@Valid @PathVariable String memberId) { + List list = storeService.list(new QueryWrapper().eq("member_id", memberId)); + if (list.size() > 0) { + return ResultUtil.data(list.get(0)); + } + return ResultUtil.data(null); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/store/StoreMessageManagerController.java b/manager-api/src/main/java/cn/lili/controller/store/StoreMessageManagerController.java new file mode 100644 index 00000000..65a2da32 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/store/StoreMessageManagerController.java @@ -0,0 +1,44 @@ +package cn.lili.controller.store; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.message.entity.dos.StoreMessage; +import cn.lili.modules.message.entity.vos.StoreMessageQueryVO; +import cn.lili.modules.message.service.StoreMessageService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * 管理端,店铺消息消息管理接口 + * + * @author pikachu + * @date: 2020/12/6 16:09 + */ +@Transactional +@RestController +@Api(tags = "管理端,店铺消息消息管理接口") +@RequestMapping("/manager/message/store") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreMessageManagerController { + + private final StoreMessageService storeMessageService; + + + @GetMapping + @ApiOperation(value = "多条件分页获取") + public ResultMessage> getByCondition(StoreMessageQueryVO storeMessageQueryVO, + PageVO pageVo) { + IPage page = storeMessageService.getPage(storeMessageQueryVO, pageVo); + return ResultUtil.data(page); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/trade/AfterSaleManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/AfterSaleManagerController.java new file mode 100644 index 00000000..4ba3bf05 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/AfterSaleManagerController.java @@ -0,0 +1,70 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.vo.AfterSaleSearchParams; +import cn.lili.modules.order.order.entity.vo.AfterSaleVO; +import cn.lili.modules.order.order.service.AfterSaleService; +import cn.lili.modules.system.entity.vo.Traces; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 管理端,售后接口 + * + * @author Bulbasaur + * @date: 2021/1/6 14:11 + */ +@RestController +@RequestMapping("/manager/afterSale") +@Api(tags = "管理端,售后接口") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AfterSaleManagerController { + + /** + * 售后 + */ + private final AfterSaleService afterSaleService; + + @ApiOperation(value = "分页获取售后服务") + @GetMapping(value = "/page") + public ResultMessage> getByPage(AfterSaleSearchParams searchParams) { + return ResultUtil.data(afterSaleService.getAfterSalePages(searchParams)); + } + + @ApiOperation(value = "查看售后服务详情") + @ApiImplicitParam(name = "sn", value = "售后单号", required = true, paramType = "path") + @GetMapping(value = "/get/{sn}") + public ResultMessage get(@NotNull(message = "售后单号") @PathVariable("sn") String sn) { + return ResultUtil.data(afterSaleService.getAfterSale(sn)); + } + + @ApiOperation(value = "查看买家退货物流踪迹") + @ApiImplicitParam(name = "sn", value = "售后单号", required = true, paramType = "path") + @GetMapping(value = "/getDeliveryTraces/{sn}") + public ResultMessage getDeliveryTraces(@PathVariable String sn) { + return ResultUtil.data(afterSaleService.deliveryTraces(sn)); + } + + @ApiOperation(value = "售后线下退款") + @ApiImplicitParams({ + @ApiImplicitParam(name = "afterSaleSn", value = "售后sn", required = true, paramType = "path"), + @ApiImplicitParam(name = "remark", value = "备注", paramType = "query") + }) + @PutMapping(value = "/refund/{afterSaleSn}") + public ResultMessage refund(@NotNull(message = "请选择售后单") @PathVariable String afterSaleSn, + @RequestParam String remark) { + + return ResultUtil.data(afterSaleService.refund(afterSaleSn, remark)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/trade/AfterSaleReasonManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/AfterSaleReasonManagerController.java new file mode 100644 index 00000000..01b7f696 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/AfterSaleReasonManagerController.java @@ -0,0 +1,75 @@ +package cn.lili.controller.trade; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.AfterSaleReason; +import cn.lili.modules.order.order.service.AfterSaleReasonService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 管理端,售后原因接口 + * + * @author Bulbasaur + * @date: 2021/1/6 14:11 + */ +@RestController +@RequestMapping("/manager/afterSaleReason") +@Api(tags = "管理端,售后原因接口") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AfterSaleReasonManagerController { + + /** + * 售后原因 + */ + private final AfterSaleReasonService afterSaleReasonService; + + @ApiOperation(value = "查看售后原因") + @ApiImplicitParam(name = "id", value = "售后原因ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage get(@PathVariable String id) { + + return ResultUtil.data(afterSaleReasonService.getById(id)); + } + + @ApiOperation(value = "分页获取售后原因") + @GetMapping(value = "/getByPage") + @ApiImplicitParam(name = "serviceType", value = "售后类型", required = true, dataType = "String", paramType = "query") + public ResultMessage> getByPage(PageVO page, @RequestParam String serviceType) { + return ResultUtil.data(afterSaleReasonService.page(PageUtil.initPage(page),new QueryWrapper().eq("service_Type", serviceType))); + } + + @ApiOperation(value = "添加售后原因") + @PostMapping + public ResultMessage save(@Valid AfterSaleReason afterSaleReason) { + afterSaleReasonService.save(afterSaleReason); + return ResultUtil.data(afterSaleReason); + } + + @ApiOperation(value = "修改售后原因") + @ApiImplicitParam(name = "id", value = "关键词ID", required = true, dataType = "String", paramType = "path") + @PutMapping("update/{id}") + public ResultMessage update(@Valid AfterSaleReason afterSaleReason, @PathVariable("id") String id) { + afterSaleReason.setId(id); + return ResultUtil.data(afterSaleReasonService.editAfterSaleReason(afterSaleReason)); + } + + @ApiOperation(value = "删除售后原因") + @ApiImplicitParam(name = "id", value = "售后原因ID", required = true, dataType = "String", paramType = "path") + @DeleteMapping(value = "/delByIds/{id}") + public ResultMessage delAllByIds(@PathVariable String id) { + afterSaleReasonService.removeById(id); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/trade/OrderComplaintManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/OrderComplaintManagerController.java new file mode 100644 index 00000000..6e4373c3 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/OrderComplaintManagerController.java @@ -0,0 +1,115 @@ +package cn.lili.controller.trade; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.OrderComplaint; +import cn.lili.modules.order.order.entity.enums.CommunicationOwnerEnum; +import cn.lili.modules.order.order.entity.enums.OrderComplaintStatusEnum; +import cn.lili.modules.order.order.entity.vo.OrderComplaintCommunicationVO; +import cn.lili.modules.order.order.entity.vo.OrderComplaintOperationParams; +import cn.lili.modules.order.order.entity.vo.OrderComplaintSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderComplaintVO; +import cn.lili.modules.order.order.service.OrderComplaintCommunicationService; +import cn.lili.modules.order.order.service.OrderComplaintService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 管理端,交易投诉接口 + * + * @author paulG + * @date: 2020/12/5 + */ +@RestController +@Api(tags = "管理端,交易投诉接口") +@RequestMapping("/manager/complain") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderComplaintManagerController { + + /** + * 交易投诉 + */ + private final OrderComplaintService orderComplaintService; + + /** + * 交易投诉沟通 + */ + private final OrderComplaintCommunicationService orderComplaintCommunicationService; + + @ApiOperation(value = "通过id获取") + @ApiImplicitParam(name = "id", value = "投诉单ID", required = true, paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(orderComplaintService.getOrderComplainById(id)); + } + + @ApiOperation(value = "分页获取") + @GetMapping + public ResultMessage> get(OrderComplaintSearchParams searchParams, PageVO pageVO) { + return ResultUtil.data(orderComplaintService.getOrderComplainByPage(searchParams, pageVO)); + } + + @ApiOperation(value = "更新数据") + @PutMapping + public ResultMessage update(OrderComplaintVO orderComplainVO) { + if (orderComplaintService.updateOrderComplain(orderComplainVO)) { + return ResultUtil.data(orderComplainVO); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "添加交易投诉对话") + @ApiImplicitParams({ + @ApiImplicitParam(name = "complainId", value = "投诉单ID", required = true, paramType = "query"), + @ApiImplicitParam(name = "content", value = "内容", required = true, paramType = "query") + }) + @PostMapping("/communication") + public ResultMessage addCommunication(@RequestParam String complainId,@RequestParam String content) { + AuthUser currentUser = UserContext.getCurrentUser(); + OrderComplaintCommunicationVO communicationVO = new OrderComplaintCommunicationVO(complainId, content, CommunicationOwnerEnum.PLATFORM.name(), currentUser.getId(), currentUser.getUsername()); + if (orderComplaintCommunicationService.addCommunication(communicationVO)) { + return ResultUtil.data(communicationVO); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改状态") + @PutMapping(value = "/status") + public ResultMessage updateStatus(OrderComplaintOperationParams orderComplainVO) { + if (orderComplaintService.updateOrderComplainByStatus(orderComplainVO) != null) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + + @ApiOperation(value = "仲裁") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "投诉单ID", required = true, paramType = "path"), + @ApiImplicitParam(name = "arbitrationResult", value = "仲裁结果", required = true, paramType = "query") + }) + @PutMapping(value = "/complete/{id}") + public ResultMessage complete(@PathVariable String id,String arbitrationResult) { + //新建对象 + OrderComplaintOperationParams orderComplaintOperationParams =new OrderComplaintOperationParams(); + orderComplaintOperationParams.setComplainId(id); + orderComplaintOperationParams.setArbitrationResult(arbitrationResult); + orderComplaintOperationParams.setComplainStatus(OrderComplaintStatusEnum.COMPLETE.name()); + + //修改状态 + if (orderComplaintService.updateOrderComplainByStatus(orderComplaintOperationParams) != null) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/trade/OrderLogManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/OrderLogManagerController.java new file mode 100644 index 00000000..7ea1c6f5 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/OrderLogManagerController.java @@ -0,0 +1,51 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.order.trade.entity.dos.OrderLog; +import cn.lili.modules.order.trade.service.OrderLogService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 管理端,订单日志管理接口 + * + * @author Chopper + * @date 2020/11/17 4:34 下午 + */ +@Transactional +@RestController +@Api(tags = "管理端,订单日志管理接口") +@RequestMapping("/manager/orderLog") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderLogManagerController { + + private final OrderLogService orderLogService; + + @GetMapping(value = "/get/{id}") + @ApiOperation(value = "通过id获取") + public ResultMessage get(@PathVariable String id) { + + return ResultUtil.data(orderLogService.getById(id)); + } + + @GetMapping(value = "/getByPage") + @ApiOperation(value = "分页获取") + public ResultMessage> getByPage(OrderLog entity, + SearchVO searchVo, + PageVO page) { + return ResultUtil.data(orderLogService.page(PageUtil.initPage(page), PageUtil.initWrapper(entity, searchVo))); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/trade/OrderManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/OrderManagerController.java new file mode 100644 index 00000000..98fd1488 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/OrderManagerController.java @@ -0,0 +1,97 @@ +package cn.lili.controller.trade; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dto.MemberAddressDTO; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.dto.OrderSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import cn.lili.modules.order.order.service.OrderPriceService; +import cn.lili.modules.order.order.service.OrderService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import springfox.documentation.annotations.ApiIgnore; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; + +/** + * 管理端,订单API + * + * @author Chopper + * @date 2020/11/17 4:34 下午 + */ +@RestController +@RequestMapping("/manager/orders") +@Api(tags = "管理端,订单API") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderManagerController { + + //订单 + private final OrderService orderService; + //订单价格 + private final OrderPriceService orderPriceService; + + + @ApiOperation(value = "查询订单列表分页") + @GetMapping + public ResultMessage> queryMineOrder(OrderSearchParams orderSearchParams) { + return ResultUtil.data(orderService.queryByParams(orderSearchParams)); + } + + @ApiOperation(value = "订单明细") + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{orderSn}") + public ResultMessage detail(@PathVariable String orderSn) { + return ResultUtil.data(orderService.queryDetail(orderSn)); + } + + + @ApiOperation(value = "确认收款") + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") + @PostMapping(value = "/{orderSn}/pay") + public ResultMessage payOrder(@PathVariable String orderSn) { + orderPriceService.adminPayOrder(orderSn); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "修改收货人信息") + @ApiImplicitParam(name = "orderSn", value = "订单sn", required = true, dataType = "String", paramType = "path") + @PostMapping(value = "/update/{orderSn}/consignee") + public ResultMessage consignee(@NotNull(message = "参数非法") @PathVariable String orderSn, + @Valid MemberAddressDTO memberAddressDTO) { + return ResultUtil.data(orderService.updateConsignee(orderSn, memberAddressDTO)); + } + + @ApiOperation(value = "修改订单价格") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单sn", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "price", value = "订单价格", required = true, dataType = "Double", paramType = "query"), + }) + @PutMapping(value = "/update/{orderSn}/price") + public ResultMessage updateOrderPrice(@PathVariable String orderSn, + @NotNull(message = "订单价格不能为空") @RequestParam Double price) { + return ResultUtil.data(orderPriceService.updatePrice(orderSn, price)); + } + + + @ApiOperation(value = "取消订单") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "reason", value = "取消原因", required = true, dataType = "String", paramType = "query") + }) + @PostMapping(value = "/{orderSn}/cancel") + public ResultMessage cancel(@ApiIgnore @PathVariable String orderSn, @RequestParam String reason) { + return ResultUtil.data(orderService.cancel(orderSn, reason)); + } + + +} \ No newline at end of file diff --git a/manager-api/src/main/java/cn/lili/controller/trade/PaymentLogManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/PaymentLogManagerController.java new file mode 100644 index 00000000..a4009bd5 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/PaymentLogManagerController.java @@ -0,0 +1,45 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.order.order.entity.dos.Order; +import cn.lili.modules.order.order.entity.vo.PaymentLog; +import cn.lili.modules.payment.service.PaymentService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * 管理端,收款日志接口 + * + * @author Chopper + * @date 2020/11/17 4:34 下午 + */ +@RestController +@Api(tags = "管理端,收款日志接口") +@RequestMapping("/manager/paymentLog") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PaymentLogManagerController { + + private final PaymentService paymentService; + + + @GetMapping + @ApiOperation(value = "分页获取支付日志") + public ResultMessage> getByPage(Order order, + SearchVO searchVo, + PageVO page) { + return ResultUtil.data(paymentService.page(PageUtil.initPage(page), PageUtil.initWrapper(order, searchVo))); + } +} diff --git a/manager-api/src/main/java/cn/lili/controller/trade/ReceiptManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/ReceiptManagerController.java new file mode 100644 index 00000000..09168b8e --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/ReceiptManagerController.java @@ -0,0 +1,39 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dto.OrderReceiptDTO; +import cn.lili.modules.order.order.entity.dto.ReceiptSearchParams; +import cn.lili.modules.order.order.service.ReceiptService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +/** + * 管理端,发票记录接口 + * + * @author paulG + * @date 2020/11/17 4:34 下午 + **/ +@RestController +@Api(tags = "管理端,发票记录接口") +@RequestMapping("/manager/receipt") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ReceiptManagerController { + + private final ReceiptService receiptService; + + + @ApiOperation(value = "获取发票分页信息") + @GetMapping + public ResultMessage> getPage(ReceiptSearchParams searchParams, PageVO pageVO) { + return ResultUtil.data(this.receiptService.getReceiptData(searchParams, pageVO)); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/trade/RechargeManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/RechargeManagerController.java new file mode 100644 index 00000000..28300ed3 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/RechargeManagerController.java @@ -0,0 +1,41 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.trade.entity.dos.Recharge; +import cn.lili.modules.order.trade.entity.vo.RechargeQueryVO; +import cn.lili.modules.order.trade.service.RechargeService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + + +/** + * 管理端,预存款充值记录接口 + * + * @author pikachu + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "管理端,预存款充值记录接口") +@RequestMapping("/manager/recharge") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RechargeManagerController { + + private final RechargeService rechargeService; + + @ApiOperation(value = "分页获取预存款充值记录") + @GetMapping + public ResultMessage> getByPage(PageVO page, RechargeQueryVO rechargeQueryVO) { + //构建查询 返回数据 + IPage rechargePage = rechargeService.rechargePage(page, rechargeQueryVO); + return ResultUtil.data(rechargePage); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/trade/RefundLogManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/RefundLogManagerController.java new file mode 100644 index 00000000..57d8f464 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/RefundLogManagerController.java @@ -0,0 +1,48 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.payment.entity.RefundLog; +import cn.lili.modules.payment.service.RefundLogService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 管理端,退款日志接口 + * + * @author Chopper + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "管理端,退款日志接口") +@RequestMapping("/manager/refundLog") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RefundLogManagerController { + + private final RefundLogService refundLogService; + + @GetMapping(value = "/{id}") + @ApiOperation(value = "查看退款日志详情") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(refundLogService.getById(id)); + } + + @GetMapping + @ApiOperation(value = "分页获取退款日志") + public ResultMessage> getByPage(RefundLog entity, SearchVO searchVo, PageVO page) { + return ResultUtil.data(refundLogService.page(PageUtil.initPage(page), PageUtil.initWrapper(entity, searchVo))); + } + +} diff --git a/manager-api/src/main/java/cn/lili/controller/trade/WalletLogManagerController.java b/manager-api/src/main/java/cn/lili/controller/trade/WalletLogManagerController.java new file mode 100644 index 00000000..fb257d72 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/controller/trade/WalletLogManagerController.java @@ -0,0 +1,41 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.trade.entity.dos.WalletLog; +import cn.lili.modules.order.trade.entity.vo.DepositQueryVO; +import cn.lili.modules.order.trade.service.WalletLogService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 管理端,预存款充值记录接口 + * + * @author pikachu + * @date: 2020/11/16 10:07 下午 + */ +@RestController +@Api(tags = "管理端,预存款充值记录接口") +@RequestMapping("/manager/wallet/log") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class WalletLogManagerController { + + private final WalletLogService walletLogService; + + @ApiOperation(value = "分页获取预存款充值记录") + @GetMapping + public ResultMessage> getByPage(PageVO page, DepositQueryVO depositQueryVO) { + //构建查询 返回数据 + IPage depositLogPage = walletLogService.depositLogPage(page, depositQueryVO); + return ResultUtil.data(depositLogPage); + } +} diff --git a/manager-api/src/main/java/cn/lili/security/ManagerAuthenticationFilter.java b/manager-api/src/main/java/cn/lili/security/ManagerAuthenticationFilter.java new file mode 100755 index 00000000..40c56a0e --- /dev/null +++ b/manager-api/src/main/java/cn/lili/security/ManagerAuthenticationFilter.java @@ -0,0 +1,136 @@ +package cn.lili.security; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.enums.SecurityEnum; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.token.SecretKeyUtil; +import cn.lili.common.utils.ResponseUtil; +import com.google.gson.Gson; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +import javax.naming.NoPermissionException; +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @author Chopper + */ +@Slf4j +public class ManagerAuthenticationFilter extends BasicAuthenticationFilter { + + private final Cache cache; + + public ManagerAuthenticationFilter(AuthenticationManager authenticationManager, + Cache cache) { + super(authenticationManager); + this.cache = cache; + } + + @SneakyThrows + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { + + //从header中获取jwt + String jwt = request.getHeader(SecurityEnum.HEADER_TOKEN.getValue()); + // 如果没有token 则return + if (StrUtil.isBlank(jwt)) { + chain.doFilter(request, response); + return; + } + + //获取用户信息,存入context + UsernamePasswordAuthenticationToken authentication = getAuthentication(jwt, response); + //自定义权限过滤 + customAuthentication(request, response, authentication); + SecurityContextHolder.getContext().setAuthentication(authentication); + chain.doFilter(request, response); + } + + /** + * 自定义权限过滤 + * + * @param request + * @param authentication + */ + private void customAuthentication(HttpServletRequest request, HttpServletResponse response, UsernamePasswordAuthenticationToken authentication) throws NoPermissionException { + AuthUser authUser = (AuthUser) authentication.getDetails(); + Map> permission = (Map>) cache.get(CachePrefix.PERMISSION_LIST.getPrefix(UserEnums.MANAGER) + authUser.getId()); + if (authUser.getIsSuper()) { + return; + } else { + //用户是否拥有权限判定œ + //获取数据权限 +// if (request.getMethod().equals(RequestMethod.GET.name())) { +// if (!PatternMatchUtils.simpleMatch(permission.get(PermissionEnum.SUPER).toArray(new String[0]), request.getRequestURI()) || +// PatternMatchUtils.simpleMatch(permission.get(PermissionEnum.QUERY).toArray(new String[0]), request.getRequestURI())) { +// +// ResponseUtil.output(response, ResponseUtil.resultMap(false, 401, "抱歉,您没有访问权限")); +// throw new NoPermissionException("权限不足"); +// } +// } else { +// if (!PatternMatchUtils.simpleMatch(permission.get(PermissionEnum.SUPER).toArray(new String[0]), request.getRequestURI())) { +// +// ResponseUtil.output(response, ResponseUtil.resultMap(false, 401, "抱歉,您没有访问权限")); +// throw new NoPermissionException("权限不足"); +// } +// } + return; + } + } + + /** + * 获取token信息 + * + * @param jwt + * @param response + * @return + */ + private UsernamePasswordAuthenticationToken getAuthentication(String jwt, HttpServletResponse response) { + + try { + Claims claims + = Jwts.parser() + .setSigningKey(SecretKeyUtil.generalKeyByDecoders()) + .parseClaimsJws(jwt).getBody(); + //获取存储在claims中的用户信息 + String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString(); + AuthUser authUser = new Gson().fromJson(json, AuthUser.class); + + // 校验redis中是否有权限 + if (cache.hasKey(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.MANAGER) + jwt)) { + //用户角色 + List auths = new ArrayList<>(); + auths.add(new SimpleGrantedAuthority("ROLE_" + authUser.getRole().name())); + //构造返回信息 + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authUser.getUsername(), null, auths); + authentication.setDetails(authUser); + return authentication; + } + ResponseUtil.output(response, 403, ResponseUtil.resultMap(false, 403, "登录已失效,请重新登录")); + return null; + } catch (ExpiredJwtException e) { + log.debug("user analysis exception:", e); + } catch (Exception e) { + log.error("user analysis exception:", e); + } + return null; + } +} + diff --git a/manager-api/src/main/java/cn/lili/security/ManagerSecurityConfig.java b/manager-api/src/main/java/cn/lili/security/ManagerSecurityConfig.java new file mode 100644 index 00000000..2f0d6b56 --- /dev/null +++ b/manager-api/src/main/java/cn/lili/security/ManagerSecurityConfig.java @@ -0,0 +1,80 @@ +package cn.lili.security; + +import cn.lili.common.cache.Cache; +import cn.lili.common.security.CustomAccessDeniedHandler; +import cn.lili.config.properties.IgnoredUrlsProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.web.cors.CorsConfigurationSource; + +/** + * spring Security 核心配置类 Manager安全配置中心 + * + * @author Chopper + * @date 2020/11/14 16:20 + */ +@Slf4j +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ManagerSecurityConfig extends WebSecurityConfigurerAdapter { + + + /** + * 忽略验权配置 + */ + private final IgnoredUrlsProperties ignoredUrlsProperties; + + /** + * spring security -》 权限不足处理 + */ + private final CustomAccessDeniedHandler accessDeniedHandler; + + + private final Cache cache; + + private final CorsConfigurationSource corsConfigurationSource; + + @Override + protected void configure(HttpSecurity http) throws Exception { + + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = http + .authorizeRequests(); + // 配置的url 不需要授权 + for (String url : ignoredUrlsProperties.getUrls()) { + log.error(url); + registry.antMatchers(url).permitAll(); + } + registry + .and() + // 禁止网页iframe + .headers().frameOptions().disable() + .and() + .authorizeRequests() + // 任何请求 + .anyRequest() + // 需要身份认证 + .authenticated() + .and() + // 允许跨域 + .cors().configurationSource(corsConfigurationSource).and() + // 关闭跨站请求防护 + .csrf().disable() + // 前后端分离采用JWT 不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + // 自定义权限拒绝处理类 + .exceptionHandling().accessDeniedHandler(accessDeniedHandler) + .and() + // 添加JWT认证过滤器 + .addFilter(new ManagerAuthenticationFilter(authenticationManager(), cache)); + } + +} diff --git a/manager-api/src/main/resources/application.yml b/manager-api/src/main/resources/application.yml new file mode 100644 index 00000000..b04e7d93 --- /dev/null +++ b/manager-api/src/main/resources/application.yml @@ -0,0 +1,302 @@ +server: + port: 8887 + + servlet: + context-path: / + + # 正式部署时候,解开此处配置,防止文件夹被清除导致的文件上传失败问题 + # multipart: + # location: /Users/lifenlong/Desktop/ceshi + tomcat: + uri-encoding: UTF-8 + threads: + min-spare: 50 + max: 1000 + +# 与Spring Boot 2一样,默认情况下,大多数端点都不通过http公开,我们公开了所有端点。对于生产,您应该仔细选择要公开的端点。 +management: + # health: + # elasticsearch: + # enabled: false + # datasource: + # enabled: false + endpoints: + web: + exposure: + include: '*' +spring: + # 要在其中注册的Spring Boot Admin Server的URL。 + boot: + admin: + client: + url: http://127.0.0.1:8000 + # mongodb + data: + mongodb: + host: 127.0.0.1 + port: 27017 + database: lilishop + username: root + password: lilishop + authentication-database: admin + # replica-set-name: mongoreplset + cache: + type: redis + + jpa: + # 自动生成表结构 + generate-ddl: true + open-in-view: false + # Redis + redis: + host: 127.0.0.1 + port: 6379 + password: lilishop + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: 20 + # 连接池中的最大空闲连接 默认 8 + max-idle: 10 + # 连接池中的最小空闲连接 默认 8 + min-idle: 8 + # 文件大小上传配置 + servlet: + multipart: + max-file-size: 20MB + max-request-size: 20MB + jackson: + time-zone: GMT+8 + serialization: + #关闭jackson 对json做解析 + fail-on-empty-beans: false + + shardingsphere: + datasource: + # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 + names: default-datasource + default-datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: lilishop + maxActive: 20 + initialSize: 5 + maxWait: 60000 + minIdle: 5 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 + poolPreparedStatements: false + #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 + maxOpenPreparedStatements: -1 + #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,log4j2 + #通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + #合并多个DruidDataSource的监控数据 + useGlobalDataSourceStat: true + loginUsername: druid + loginPassword: druid + # sharding: + # default-data-source-name: default-datasource + # #需要拆分的表,可以设置多个 在 li_order 级别即可 + # tables: + # #需要进行分表的逻辑表名 + # li_order: + # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 + # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} + # table-strategy: + # # 分表策略,根据创建日期 + # standard: + # sharding-column: create_time + # #分表策略 + # precise-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + # #范围查询实现 + # range-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + props: + #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 + sql: + show: true + +# 忽略鉴权url +ignored: + urls: + - /editor-app/** + - /actuator** + - /actuator/** + - /MP_verify_qSyvBPhDsPdxvOhC.txt + - /weixin/** + - /source/** + - /buyer/mini-program/** + - /buyer/cashier/** + - /buyer/pageData/** + - /buyer/article/** + - /buyer/goods/** + - /buyer/category/** + - /buyer/shop/** + - /buyer/connect/** + - /buyer/members/smsLogin + - /buyer/members/refresh/* + - /buyer/members/refresh** + - /buyer/promotion/pintuan + - /buyer/promotion/seckill + - /buyer/memberEvaluation/**/goodsEvaluation + - /buyer/memberEvaluation/**/evaluationNumber + - /store/login/** + - /manager/user/login + - /manager/user/refresh/** + - /manager/elasticsearch + - /druid/** + - /swagger-ui.html + - /doc.html + - /swagger-resources/** + - /swagger/** + - /**/**.js + - /**/**.png + - /**/**.css + - /webjars/** + - /v2/api-docs + - /configuration/ui + - /boot-admin + statics: + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + +# Swagger界面内容配置 +swagger: + title: lili API接口文档 + description: lili Api Documentation + version: 1.0.0 + termsOfServiceUrl: https://pickmall.cn + contact: + name: lili + url: https://pickmall.cn + email: admin@pickmall.com + +# Mybatis-plus +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + #缓存开启 + cache-enabled: true + #日志 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 日志 +logging: + # 输出级别 + level: + cn.lili: info + # org.hibernate: debug + # org.springframework: debug + # org.springframework.data.mongodb.core: debug + file: + # 指定路径 + path: lili-logs + # 最大保存天数 + max-history: 7 + # 每个文件最大大小 + max-size: 5MB +#加密参数 +jasypt: + encryptor: + password: lili + +lili: + system: + isDemoSite: true + statistics: + # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 + onlineMember: 48 + # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 + currentOnlineUpdate: 600 + #qq lbs 申请 + lbs: + key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT + sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 + #域名 + domain: + pc: https://pc.b2b2c.pickmall.cn + wap: https://m.b2b2c.pickmall.cn + store: https://store.b2b2c.pickmall.cn + admin: https://admin.b2b2c.pickmall.cn + #api地址 + api: + buyer: https://buyer-api.pickmall.cn + common: https://common-api.pickmall.cn + manager: https://admin-api.pickmall.cn + store: https://store-api.pickmall.cn + + # jwt 细节设定 + jwt-setting: + # token过期时间(分钟) + tokenExpireTime: 60 + + # 使用Spring @Cacheable注解失效时间 + cache: + # 过期时间 单位秒 永久不过期设为-1 + timeout: 1500 + #多线程配置 + thread: + corePoolSize: 5 + maxPoolSize: 50 + queueCapacity: 50 + data: + elasticsearch: + cluster-name: elasticsearch + cluster-nodes: 127.0.0.1:9200 + index: + number-of-replicas: 0 + number-of-shards: 3 + index-prefix: lili + schema: http + # account: + # username: elastic + # password: LiLiShopES + + rocketmq: + promotion-topic: lili_promotion_topic + promotion-group: lili_promotion_group + msg-ext-topic: lili_msg_topic + msg-ext-group: lili_msg_group + goods-topic: lili_goods_topic + goods-group: lili_goods_group + order-topic: lili_order_topic + order-group: lili_order_group + member-topic: lili_member_topic + member-group: lili_member_group + other-topic: lili_other_topic + other-group: lili_other_group + notice-topic: lili_notice_topic + notice-group: lili_notice_group + notice-send-topic: lili_send_notice_topic + notice-send-group: lili_send_notice_group + after-sale-topic: lili_after_sale_topic + after-sale-group: lili_after_sale_group +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: lili_group + send-message-timeout: 30000 + +xxl: + job: + admin: + addresses: http://127.0.0.1:9001/xxl-job-admin + executor: + appname: xxl-job-executor-lilishop + address: + ip: + port: 8891 + logpath: ./xxl-job/executor + logretentiondays: 7 \ No newline at end of file diff --git a/manager-api/src/main/resources/banner.txt b/manager-api/src/main/resources/banner.txt new file mode 100644 index 00000000..be6731c5 --- /dev/null +++ b/manager-api/src/main/resources/banner.txt @@ -0,0 +1,19 @@ + ___ ___ ___ ___ ________ ________ _______ ________ _____ ______ ___ __ ________ ________ ___ __ +|\ \ |\ \|\ \ |\ \ |\ _____\\ __ \|\ ___ \ |\ __ \|\ _ \ _ \|\ \ |\ \|\ __ \|\ __ \|\ \|\ \ +\ \ \ \ \ \ \ \ \ \ \ ____________\ \ \__/\ \ \|\ \ \ __/|\ \ \|\ \ \ \\\__\ \ \ \ \ \ \ \ \ \|\ \ \ \|\ \ \ \/ /|_ + \ \ \ \ \ \ \ \ \ \ \|\____________\ \ __\\ \ _ _\ \ \_|/_\ \ __ \ \ \\|__| \ \ \ \ __\ \ \ \ \\\ \ \ _ _\ \ ___ \ + \ \ \____\ \ \ \ \____\ \ \|____________|\ \ \_| \ \ \\ \\ \ \_|\ \ \ \ \ \ \ \ \ \ \ \ \|\__\_\ \ \ \\\ \ \ \\ \\ \ \\ \ \ + \ \_______\ \__\ \_______\ \__\ \ \__\ \ \__\\ _\\ \_______\ \__\ \__\ \__\ \ \__\ \____________\ \_______\ \__\\ _\\ \__\\ \__\ + \|_______|\|__|\|_______|\|__| \|__| \|__|\|__|\|_______|\|__|\|__|\|__| \|__|\|____________|\|_______|\|__|\|__|\|__| \|__| + + + + ___ ___ ___ ___ ________ ___ ___ ________ ________ + |\ \ |\ \|\ \ |\ \ |\ ____\|\ \|\ \|\ __ \|\ __ \ + \ \ \ \ \ \ \ \ \ \ \ ____________\ \ \___|\ \ \\\ \ \ \|\ \ \ \|\ \ + \ \ \ \ \ \ \ \ \ \ \|\____________\ \_____ \ \ __ \ \ \\\ \ \ ____\ + \ \ \____\ \ \ \ \____\ \ \|____________|\|____|\ \ \ \ \ \ \ \\\ \ \ \___| + \ \_______\ \__\ \_______\ \__\ ____\_\ \ \__\ \__\ \_______\ \__\ + \|_______|\|__|\|_______|\|__| |\_________\|__|\|__|\|_______|\|__| + \|_________| + diff --git a/manager-api/src/test/java/cn/lili/test/CacheTest/CacheTest.java b/manager-api/src/test/java/cn/lili/test/CacheTest/CacheTest.java new file mode 100644 index 00000000..d0ed486a --- /dev/null +++ b/manager-api/src/test/java/cn/lili/test/CacheTest/CacheTest.java @@ -0,0 +1,126 @@ +package cn.lili.test.CacheTest; + +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.modules.statistics.util.StatisticsSuffix; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.DefaultTypedTuple; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Date; +import java.util.Random; +import java.util.Set; + +/** + * @author Chopper + * @version v1.0 + * @Description: + * @since v7.0 + * 2021/1/15 16:25 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +class CacheTest { + + @Autowired + private Cache cache; + + String KEY = "test1"; + + /** + * 计数器测试 + * + * @throws InterruptedException + */ + @Test + void testCache() throws InterruptedException { + + System.out.println(cache.incr(KEY, 3)); + System.out.println(cache.incr(KEY, 1)); + System.out.println(cache.incr(KEY, 1)); + Thread.sleep(2000); + System.out.println(cache.incr(KEY, 1)); + Thread.sleep(1000); + System.out.println(cache.incr(KEY, 1)); + Thread.sleep(10000); + + System.out.println(cache.incr(KEY, 1)); + + } + + /** + * 缓存中的流量统计数据模拟 + */ + @Test + void pageViewInit() { + String storeUV = "{STORE_UV}_2021-4-12-1376369067769724928"; + String storePV = "{STORE_PV}_2021-4-12-1376369067769724928"; + String UV = "{UV}_2021-4-12"; + String PV = "{PV}_2021-4-12"; + Random random = new Random(); + for (int i = 0; i < 1000; i++) { + //PV + cache.incr(PV, 60 * 60 * 48); + cache.incr(storePV, 60 * 60 * 48); + //店铺UV 统计,则需要对id去重复,所以如下处理 + cache.cumulative(storeUV, "192.168.0.1" + random.nextInt(100)); + //平台UV统计 + cache.cumulative(UV, "192.168.0.1" + random.nextInt(100)); + } + } + + + /** + * 流量单元测试 + *

+ * 模拟1000次请求发,查看这块执行时间,单节点性能简单尝试同时还有redis连接池问题,这个只是简单压力模拟 + *

+ * 执行结果 + * 1.251 + * 1.167 + * 1.363 + */ + @Test + void testPageViewStatistics() { + Date start = new Date(); + System.out.println(start.getTime()); + for (int i = 0; i < 1000; i++) { + + //PV 统计48小时过期 留下一定时间予以统计累计数据库 + cache.incr(CachePrefix.PV.getPrefix() + StatisticsSuffix.suffix(i / 100 + ""), 60 * 60 * 48); + + //店铺UV 统计,则需要对id去重复,所以如下处理 + cache.cumulative(CachePrefix.UV.getPrefix() + StatisticsSuffix.suffix(i / 100 + "1321312312312312321312"), "192.168.0.1" + i); + + //平台UV统计 + cache.cumulative(CachePrefix.UV.getPrefix() + StatisticsSuffix.suffix(), "192.168.0.1" + i); + } + + Date end = new Date(); + System.out.println(end.getTime()); + System.out.println(end.getTime() - start.getTime()); + } + + @Test + void testZincrby() { + cache.incrementScore("searchHotWord", "Chrome"); + Assertions.assertTrue(true); + } + + @Test + void testReverseRangeWithScores() { + Set searchHotWord = cache.reverseRangeWithScores("searchHotWord", 0, 100); + for (Object o : searchHotWord) { + DefaultTypedTuple str = (DefaultTypedTuple) o; + System.out.println(str.getScore()); + System.out.println(str.getValue()); + System.out.println("----------"); + } + Assertions.assertTrue(true); + } + +} diff --git a/manager-api/src/test/java/cn/lili/test/elasticsearch/EsTest.java b/manager-api/src/test/java/cn/lili/test/elasticsearch/EsTest.java new file mode 100644 index 00000000..3da38b66 --- /dev/null +++ b/manager-api/src/test/java/cn/lili/test/elasticsearch/EsTest.java @@ -0,0 +1,258 @@ +package cn.lili.test.elasticsearch; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.enums.GoodsAuthEnum; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.promotion.service.PromotionService; +import cn.lili.modules.search.entity.dos.EsGoodsAttribute; +import cn.lili.modules.search.entity.dos.EsGoodsIndex; +import cn.lili.modules.search.entity.dos.EsGoodsRelatedInfo; +import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO; +import cn.lili.modules.search.repository.EsGoodsIndexRepository; +import cn.lili.modules.search.service.EsGoodsIndexService; +import cn.lili.modules.search.service.EsGoodsSearchService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @author paulG + * @since 2020/10/14 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class EsTest { + + @Autowired + private EsGoodsIndexService esGoodsIndexService; + + @Autowired + private EsGoodsIndexRepository goodsIndexRepository; + + @Autowired + private EsGoodsSearchService goodsSearchService; + + @Autowired + private GoodsSkuService goodsSkuService; + + @Autowired + private StringRedisTemplate stringRedisTemplate; + + @Autowired + private PromotionService promotionService; + + + @Test + void searchGoods() { + EsGoodsSearchDTO goodsSearchDTO = new EsGoodsSearchDTO(); +// goodsSearchDTO.setKeyword("黄"); + goodsSearchDTO.setProp("IETF_HTTP/3"); +// goodsSearchDTO.setPrice("100_20000"); +// goodsSearchDTO.setStoreCatId(1L); +// goodsSearchDTO.setBrandId(123L); +// goodsSearchDTO.setCategoryId(2L); +// goodsSearchDTO.setNameIds(Arrays.asList("1344113311566553088", "1344113367694729216")); + PageVO pageVo = new PageVO(); + pageVo.setPageNumber(0); + pageVo.setPageSize(100); + pageVo.setOrder("desc"); + pageVo.setNotConvert(true); + Page esGoodsIndices = goodsSearchService.searchGoods(goodsSearchDTO, pageVo); + Assertions.assertNotNull(esGoodsIndices); + esGoodsIndices.getContent().forEach(System.out::println); +// esGoodsIndices.getContent().forEach(i -> { +// if (i.getPromotionMap() != null){ +// String s = i.getPromotionMap().keySet().parallelStream().filter(j -> j.contains(PromotionTypeEnum.FULL_DISCOUNT.name())).findFirst().orElse(null); +// if (s != null) { +// FullDiscount basePromotion = (FullDiscount) i.getPromotionMap().get(s); +// System.out.println(basePromotion); +// } +// } +// }); + + } + + @Test + void aggregationSearch() { + EsGoodsSearchDTO goodsSearchDTO = new EsGoodsSearchDTO(); + // goodsSearchDTO.setKeyword("电脑"); + // goodsSearchDTO.setProp("颜色_故宫文创@版本_小新Pro13s"); +// goodsSearchDTO.setCategoryId("2"); +// goodsSearchDTO.setPrice("100_20000"); + PageVO pageVo = new PageVO(); + pageVo.setPageNumber(0); + pageVo.setPageSize(10); + pageVo.setOrder("desc"); + EsGoodsRelatedInfo selector = goodsSearchService.getSelector(goodsSearchDTO, pageVo); + Assertions.assertNotNull(selector); + System.out.println(JSONUtil.toJsonStr(selector)); + + } + + @Test + void init() { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(GoodsSku::getIsAuth, GoodsAuthEnum.PASS.name()); + queryWrapper.eq(GoodsSku::getMarketEnable, GoodsStatusEnum.UPPER.name()); + List list = goodsSkuService.list(queryWrapper); + List esGoodsIndices = new ArrayList<>(); + for (GoodsSku goodsSku : list) { + EsGoodsIndex index = new EsGoodsIndex(goodsSku); + Map goodsCurrentPromotionMap = promotionService.getGoodsCurrentPromotionMap(index); + index.setPromotionMap(goodsCurrentPromotionMap); + esGoodsIndices.add(index); + stringRedisTemplate.opsForValue().set(GoodsSkuService.getStockCacheKey(goodsSku.getId()), goodsSku.getQuantity().toString()); + } + esGoodsIndexService.initIndex(esGoodsIndices); + Assertions.assertTrue(true); + } + + @Test + void addIndex() { + List esGoodsAttributeList = new ArrayList<>(); + EsGoodsAttribute attribute = new EsGoodsAttribute(); + attribute.setType(0); + attribute.setName("颜色"); + attribute.setValue("16.1英寸 6核R5 16G 512G 高色域"); + esGoodsAttributeList.add(attribute); + attribute = new EsGoodsAttribute(); + attribute.setType(0); + attribute.setName("版本"); + attribute.setValue("RedmiBook 18英寸 深空灰"); + esGoodsAttributeList.add(attribute); + EsGoodsIndex goodsIndex = initGoodsIndexData("122", "0|2", "140", "142", "A142", "RedmiBook 18 锐龙版 超轻薄全面屏(6核R5-4500U 16G 512G 100% sRGB高色域)灰 手提 笔记本电脑 小米 红米 ", "131", "小米自营旗舰店", 10000D); + goodsIndex.setAttrList(esGoodsAttributeList); + + //GoodsSku goodsSkuByIdFromCache = goodsSkuService.getGoodsSkuByIdFromCache("121"); + //EsGoodsIndex goodsIndex = new EsGoodsIndex(goodsSkuByIdFromCache); + + + esGoodsIndexService.addIndex(goodsIndex); + + Assertions.assertTrue(true); + } + + @Test + void searchAll() { + Iterable all = goodsIndexRepository.findAll(); + Assertions.assertNotNull(all); + all.forEach(System.out::println); + } + + @Test + void updateIndex() { +// EsGoodsIndex goodsIndex = new EsGoodsIndex(); +// goodsIndex.setId("121"); +// goodsIndex.setBrandId("113"); +// goodsIndex.setGoodsId("113"); +// goodsIndex.setCategoryPath("0|1"); +// goodsIndex.setBuyCount(100); +// goodsIndex.setCommentNum(100); +// goodsIndex.setGoodsName("惠普(HP)战66 三代AMD版14英寸轻薄笔记本电脑(锐龙7nm 六核R5-4500U 16G 512G 400尼特高色域一年上门 )"); +// goodsIndex.setGrade(100D); +// goodsIndex.setHighPraiseNum(100); +// goodsIndex.setIntro("I'd like a cup of tea, please"); +// goodsIndex.setIsAuth("1"); +// goodsIndex.setMarketEnable("1"); +// goodsIndex.setMobileIntro("I want something cold to drink"); +// goodsIndex.setPoint(100); +// goodsIndex.setPrice(100D); +// goodsIndex.setSelfOperated(true); +// goodsIndex.setStoreId("113"); +// goodsIndex.setStoreName("惠普自营官方旗舰店"); +// goodsIndex.setStoreCategoryPath("1"); +// goodsIndex.setThumbnail("picture"); +// goodsIndex.setSn("A113"); +// Map promotionMap = new HashMap<>(); +// Coupon coupon = new Coupon(); +// coupon.setStoreId("113"); +// coupon.setStoreName("惠普自营官方旗舰店"); +// coupon.setPromotionStatus(PromotionStatusEnum.START.name()); +// coupon.setReceivedNum(0); +// coupon.setConsumeLimit(11D); +// coupon.setCouponLimitNum(10); +// coupon.setCouponName("满11减10"); +// coupon.setCouponType(CouponTypeEnum.PRICE.name()); +// coupon.setGetType(CouponGetEnum.FREE.name()); +// coupon.setPrice(10D); +// promotionMap.put(PromotionTypeEnum.COUPON.name(), coupon); +// goodsIndex.setPromotionMap(promotionMap); +// List esGoodsAttributeList = new ArrayList<>(); +// EsGoodsAttribute attribute = new EsGoodsAttribute(); +// attribute.setType(0); +// attribute.setName("颜色"); +// attribute.setValue("14英寸"); +// esGoodsAttributeList.add(attribute); +// esGoodsAttributeList.add(attribute); +// attribute = new EsGoodsAttribute(); +// attribute.setName("版本"); +// attribute.setValue("【战66新品】R5-4500 8G 256G"); +// esGoodsAttributeList.add(attribute); +// attribute = new EsGoodsAttribute(); +// attribute.setName("配置"); +// attribute.setValue("i5 8G 512G 2G独显"); +// esGoodsAttributeList.add(attribute); +// goodsIndex.setAttrList(esGoodsAttributeList); +// GoodsSku goodsSkuByIdFromCache = goodsSkuService.getGoodsSkuByIdFromCache("121"); +// EsGoodsIndex goodsIndex = new EsGoodsIndex(goodsSkuByIdFromCache); + EsGoodsIndex byId = esGoodsIndexService.findById("121"); + byId.setPromotionMap(null); + esGoodsIndexService.updateIndex(byId); + Assertions.assertTrue(true); + } + + @Test + void deleteIndex() { + esGoodsIndexService.deleteIndex(null); + Assertions.assertTrue(true); + } + + @Test + void cleanPromotion() { + esGoodsIndexService.cleanInvalidPromotion(); + Assertions.assertTrue(true); + } + + + private EsGoodsIndex initGoodsIndexData(String brandId, String categoryPath, String goodsId, String id, String sn, String goodsName, String storeId, String storeName, Double price) { + EsGoodsIndex goodsIndex = new EsGoodsIndex(); + goodsIndex.setBuyCount(99); + goodsIndex.setCommentNum(99); + goodsIndex.setGrade(100D); + goodsIndex.setHighPraiseNum(100); + goodsIndex.setIntro("I'd like a cup of tea, please"); + goodsIndex.setIsAuth("1"); + goodsIndex.setMarketEnable("1"); + goodsIndex.setMobileIntro("I want something cold to drink"); + goodsIndex.setPoint(0); + goodsIndex.setSelfOperated(true); + goodsIndex.setThumbnail("picture"); + goodsIndex.setStoreCategoryPath("1"); + + goodsIndex.setId(id); + goodsIndex.setBrandId(brandId); + goodsIndex.setGoodsId(goodsId); + goodsIndex.setCategoryPath(categoryPath); + goodsIndex.setGoodsName(goodsName); + goodsIndex.setPrice(price); + goodsIndex.setSn(sn); + goodsIndex.setStoreId(storeId); + goodsIndex.setStoreName(storeName); + return goodsIndex; + } + + +} diff --git a/manager-api/src/test/java/cn/lili/test/order/OrderServiceTest.java b/manager-api/src/test/java/cn/lili/test/order/OrderServiceTest.java new file mode 100644 index 00000000..deb13a82 --- /dev/null +++ b/manager-api/src/test/java/cn/lili/test/order/OrderServiceTest.java @@ -0,0 +1,33 @@ +package cn.lili.test.order; + +import cn.lili.modules.order.order.service.OrderService; +import org.junit.jupiter.api.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; + +/** + * @author paulG + * @since 2020/12/1 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class OrderServiceTest { + + @Autowired + private OrderService orderService; + + + @Test + void QueryParam() { +// OrderSearchParams orderSearchParams = new OrderSearchParams(); +// orderSearchParams.setPageSize(0); +// orderSearchParams.setPageNumber(10); +// IPage orderVOIPage = orderService.queryByParams(orderSearchParams); +// Assertions.assertNotNull(orderVOIPage); +// orderVOIPage.getRecords().forEach(System.out::println); + } + + +} diff --git a/manager-api/src/test/java/cn/lili/test/promotion/CouponTest.java b/manager-api/src/test/java/cn/lili/test/promotion/CouponTest.java new file mode 100644 index 00000000..157b9c14 --- /dev/null +++ b/manager-api/src/test/java/cn/lili/test/promotion/CouponTest.java @@ -0,0 +1,203 @@ +package cn.lili.test.promotion; + +import cn.lili.common.vo.PageVO; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.promotion.entity.dos.Coupon; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.enums.*; +import cn.lili.modules.promotion.entity.vos.CouponSearchParams; +import cn.lili.modules.promotion.entity.vos.CouponVO; +import cn.lili.modules.promotion.service.CouponService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.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; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author paulG + * @since 2020/10/29 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class CouponTest { + + @Autowired + private CouponService couponService; + + @Autowired + private GoodsSkuService goodsSkuService; + + @Autowired + private RocketMQTemplate rocketMQTemplate; + + @Autowired + private RocketmqCustomProperties rocketmqCustomProperties; + + @Test + void addCoupon() { + CouponVO couponVO = new CouponVO(); + couponVO.setCouponName("Coupon V" + couponVO.getId()); + couponVO.setCouponType(CouponTypeEnum.DISCOUNT.name()); + couponVO.setDescription(couponVO.getCouponName() + " are expensive"); + couponVO.setGetType(CouponGetEnum.FREE.name()); + couponVO.setPromotionStatus(PromotionStatusEnum.NEW.name()); +// couponVO.setStoreId("0"); +// couponVO.setStoreName("platform"); + couponVO.setStoreId("131"); + couponVO.setStoreName("小米自营旗舰店"); + couponVO.setPublishNum(1000); + couponVO.setCouponLimitNum(0); + couponVO.setConsumeThreshold(500D); +// couponVO.setPrice(200D); + couponVO.setCouponDiscount(0.1D); + + couponVO.setScopeType(CouponScopeTypeEnum.PORTION_GOODS.name()); + couponVO.setScopeId("121"); + couponVO.setStartTime(cn.hutool.core.date.DateUtil.parse("2020-11-30 15:58:00")); + couponVO.setEndTime(cn.hutool.core.date.DateUtil.parse("2020-12-30 23:50:00")); + + if (couponVO.getCouponType().equals(CouponTypeEnum.DISCOUNT.name())) { + couponVO.setPromotionName(couponVO.getCouponDiscount() + "折券"); + } else { + couponVO.setPromotionName(couponVO.getPrice() + "元券"); + } + List promotionGoodsList = new ArrayList<>(); +// GoodsSku sku121 = goodsSkuService.getGoodsSkuByIdFromCache("121"); + PromotionGoods promotionGoods = new PromotionGoods(); + promotionGoods.setPrice(0.0); + promotionGoods.setLimitNum(0); + promotionGoods.setNum(1000); + promotionGoods.setStartTime(couponVO.getStartTime()); + promotionGoods.setEndTime(couponVO.getEndTime()); + promotionGoods.setTitle(couponVO.getPromotionName()); + promotionGoods.setPromotionId(couponVO.getId()); + promotionGoods.setQuantity(1000); + promotionGoods.setPromotionStatus(couponVO.getPromotionStatus()); + promotionGoods.setPromotionType(PromotionTypeEnum.COUPON.name()); + promotionGoodsList.add(promotionGoods); +// +// GoodsSku sku50112 = goodsSkuService.getGoodsSkuByIdFromCache("50112"); +// promotionGoods = new PromotionGoods(sku50112); +// promotionGoods.setPrice(80000d); +// promotionGoods.setLimitNum(0); +// promotionGoods.setPromotionQuantity(1000); +// promotionGoods.setNum(1000); +// promotionGoods.setStartTime(couponVO.getStartTime()); +// promotionGoods.setEndTime(couponVO.getEndTime()); +// promotionGoods.setTitle(couponVO.getPromotionName()); +// promotionGoods.setPromotionStatus(couponVO.getPromotionStatus()); +// promotionGoodsList.add(promotionGoods); +// + couponVO.setPromotionGoodsList(promotionGoodsList); + Assertions.assertNotNull(couponService.add(couponVO)); + } + + @Test + void update() { + CouponVO couponVO = new CouponVO(); + couponVO.setId("1326081397400297472"); + couponVO.setCouponName("Coupon V" + couponVO.getId()); + couponVO.setCouponType(CouponTypeEnum.DISCOUNT.name()); + couponVO.setDescription(couponVO.getId() + " is expensive"); + couponVO.setGetType(CouponGetEnum.FREE.name()); + couponVO.setPromotionStatus(PromotionStatusEnum.START.name()); + couponVO.setStoreId("132"); + couponVO.setStoreName("联想自营旗舰店"); + couponVO.setStoreCommission(99.99D); + couponVO.setPublishNum(1000); + couponVO.setCouponLimitNum(0); + couponVO.setCouponDiscount(10D); + couponVO.setPrice(0D); + + couponVO.setScopeType(CouponScopeTypeEnum.PORTION_GOODS.name()); + couponVO.setScopeId("134,133"); + couponVO.setStartTime(cn.hutool.core.date.DateUtil.parse("2020-11-10 17:01:00")); + couponVO.setEndTime(cn.hutool.core.date.DateUtil.parse("2020-11-10 17:10:00")); + + if (couponVO.getCouponType().equals(CouponTypeEnum.DISCOUNT.name())) { + couponVO.setPromotionName(couponVO.getCouponDiscount() + "折券"); + } else { + couponVO.setPromotionName(couponVO.getPrice() + "元券"); + } + + List promotionGoodsList = new ArrayList<>(); + PromotionGoods promotionGoods = new PromotionGoods(); + promotionGoods.setSkuId("134"); + promotionGoods.setGoodsName("联想(Lenovo)YOGA S740商务办公本 英特尔酷睿i5 14英寸超轻薄笔记本电脑(i5 16G 512G 独显 雷电3 WiFi6)灰"); + promotionGoods.setPrice(20000d); + promotionGoods.setStoreId("132"); + promotionGoods.setStoreName("联想自营旗舰店"); + promotionGoods.setLimitNum(0); + promotionGoods.setQuantity(1000); + promotionGoods.setThumbnail("thumbnail"); + promotionGoods.setNum(1000); + promotionGoods.setStartTime(couponVO.getStartTime()); + promotionGoods.setEndTime(couponVO.getEndTime()); + promotionGoods.setTitle(couponVO.getPromotionName()); + promotionGoods.setPromotionStatus(couponVO.getPromotionStatus()); + promotionGoodsList.add(promotionGoods); + + promotionGoods = new PromotionGoods(); + promotionGoods.setSkuId("133"); + promotionGoods.setGoodsName("联想(Lenovo)小新Pro13s“锦绣前程”故宫文创版13.3英寸轻薄笔记本电脑(I5 16G 512G 2.5K 100%sRGB)"); + promotionGoods.setPrice(100000d); + promotionGoods.setStoreId("132"); + promotionGoods.setStoreName("联想自营旗舰店"); + promotionGoods.setLimitNum(0); + promotionGoods.setQuantity(1000); + promotionGoods.setThumbnail("thumbnail"); + promotionGoods.setNum(1000); + promotionGoods.setStartTime(couponVO.getStartTime()); + promotionGoods.setEndTime(couponVO.getEndTime()); + promotionGoods.setTitle(couponVO.getPromotionName()); + promotionGoods.setPromotionStatus(couponVO.getPromotionStatus()); + promotionGoodsList.add(promotionGoods); + + couponVO.setPromotionGoodsList(promotionGoodsList); + Assertions.assertNotNull(couponService.updateCoupon(couponVO)); + } + + @Test + void searchFromMongo() { + CouponSearchParams queryParam = new CouponSearchParams(); + queryParam.setStoreId(""); + PageVO pageVo = new PageVO(); + pageVo.setPageNumber(0); + pageVo.setPageSize(10); + IPage couponsByPageFromMongo = couponService.getCouponsByPageFromMongo(queryParam, pageVo); + Assertions.assertNotNull(couponsByPageFromMongo); + couponsByPageFromMongo.getRecords().forEach(System.out::println); + } + + @Test + void searchFromMysql() { + CouponSearchParams queryParam = new CouponSearchParams(); + + PageVO pageVo = new PageVO(); + pageVo.setPageNumber(0); + pageVo.setPageSize(10); + IPage coupons = couponService.getCouponsByPage(queryParam, pageVo); + Assertions.assertNotNull(coupons); + coupons.getRecords().forEach(System.out::println); + } + + @Test + void delete() { +// Assertions.assertTrue(couponService.deleteCoupon("1326001296591577088")); + GoodsStatusEnum goodsStatusEnum = GoodsStatusEnum.DOWN; + System.out.println("name:: " + goodsStatusEnum.name()); + System.out.println("description:: " + goodsStatusEnum.description()); + Assertions.assertTrue(true); + } + + +} diff --git a/manager-api/src/test/java/cn/lili/test/promotion/FullDiscountTest.java b/manager-api/src/test/java/cn/lili/test/promotion/FullDiscountTest.java new file mode 100644 index 00000000..854ea0aa --- /dev/null +++ b/manager-api/src/test/java/cn/lili/test/promotion/FullDiscountTest.java @@ -0,0 +1,138 @@ +package cn.lili.test.promotion; + +import cn.hutool.core.util.RandomUtil; +import cn.hutool.json.JSONUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.promotion.entity.dos.FullDiscount; +import cn.lili.modules.promotion.entity.dos.PromotionGoods; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.FullDiscountSearchParams; +import cn.lili.modules.promotion.service.FullDiscountService; +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.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; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author paulG + * @since 2020/10/22 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class FullDiscountTest { + + @Autowired + private FullDiscountService fullDiscountService; + + @Autowired + private GoodsSkuService goodsSkuService; + + @Test + void addFullDiscount() { + FullDiscountVO fullDiscountVO = new FullDiscountVO(); + fullDiscountVO.setStoreId("131"); + fullDiscountVO.setStoreName("小米自营旗舰店"); + fullDiscountVO.setNumber(1); + fullDiscountVO.setDescription("full discount test " + RandomUtil.randomNumber()); + fullDiscountVO.setIsFullMinus(true); + fullDiscountVO.setFullMoney(130D); + fullDiscountVO.setFullMinus(100D); + fullDiscountVO.setPromotionStatus(PromotionStatusEnum.NEW.name()); + fullDiscountVO.setIsFreeFreight(true); + + fullDiscountVO.setPromotionName("FullDiscount-" + fullDiscountVO.getId()); + fullDiscountVO.setTitle("满" + fullDiscountVO.getFullMoney() + "减" + fullDiscountVO.getFullMinus()); + fullDiscountVO.setStartTime(cn.hutool.core.date.DateUtil.parse("2020-11-30 10:35:00")); + fullDiscountVO.setEndTime(cn.hutool.core.date.DateUtil.parse("2020-12-25 23:20:00")); + + List promotionGoodsLis = new ArrayList<>(); + GoodsSku sku121 = goodsSkuService.getGoodsSkuByIdFromCache("121"); + PromotionGoods promotionGoods = new PromotionGoods(sku121); + promotionGoods.setPrice(sku121.getPrice()); + promotionGoods.setLimitNum(100); + promotionGoods.setStartTime(fullDiscountVO.getStartTime()); + promotionGoods.setEndTime(fullDiscountVO.getEndTime()); + promotionGoods.setNum(10); + promotionGoods.setQuantity(100); + promotionGoods.setPromotionId(fullDiscountVO.getId()); + promotionGoods.setPromotionStatus(PromotionStatusEnum.NEW.name()); + promotionGoods.setPromotionType(PromotionTypeEnum.FULL_DISCOUNT.name()); + promotionGoods.setTitle("满" + fullDiscountVO.getFullMoney() + "减" + fullDiscountVO.getFullMinus()); + promotionGoodsLis.add(promotionGoods); + fullDiscountVO.setPromotionGoodsList(promotionGoodsLis); + + Assertions.assertNotNull(fullDiscountService.addFullDiscount(fullDiscountVO)); + } + + @Test + void searchFromMongo() { + PageVO pageVo = new PageVO(); + pageVo.setPageSize(10); + pageVo.setPageNumber(0); + pageVo.setNotConvert(true); + pageVo.setSort("startTime"); + pageVo.setOrder("asc"); + + IPage fullDiscountByPageFromMongo = fullDiscountService.getFullDiscountByPageFromMongo(new FullDiscountSearchParams(), null); + + Assertions.assertNotNull(fullDiscountByPageFromMongo); + FullDiscount fullDiscount = JSONUtil.toBean(JSONUtil.parseObj(fullDiscountByPageFromMongo.getPages()), FullDiscount.class); + System.out.println(fullDiscount); +// fullDiscountByPageFromMongo.forEach(System.out::println); + } + + @Test + void update() { + FullDiscountVO fullDiscountVO = new FullDiscountVO(); + fullDiscountVO.setId("1325981729404248064"); + fullDiscountVO.setStoreId("132"); + fullDiscountVO.setStoreName("联想自营旗舰店"); + fullDiscountVO.setNumber(1); + fullDiscountVO.setDescription("Not worth"); + fullDiscountVO.setIsFullMinus(true); + fullDiscountVO.setFullMoney(100D); + fullDiscountVO.setFullMinus(80D); + fullDiscountVO.setPromotionStatus(PromotionStatusEnum.NEW.name()); + fullDiscountVO.setIsFreeFreight(true); + + fullDiscountVO.setPromotionName("FullDiscount-" + fullDiscountVO.getId()); + fullDiscountVO.setTitle("满" + fullDiscountVO.getFullMoney() + "减" + fullDiscountVO.getFullMinus()); + fullDiscountVO.setStartTime(cn.hutool.core.date.DateUtil.parse("2020-11-10 10:15:00")); + fullDiscountVO.setEndTime(cn.hutool.core.date.DateUtil.parse("2020-11-10 10:30:00")); + + List promotionGoodsLis = new ArrayList<>(); + PromotionGoods promotionGoods = new PromotionGoods(); + promotionGoods.setSkuId("134"); + promotionGoods.setPromotionStatus(PromotionStatusEnum.NEW.name()); + promotionGoods.setPrice(18000D); + promotionGoods.setStartTime(fullDiscountVO.getStartTime()); + promotionGoods.setEndTime(fullDiscountVO.getEndTime()); + promotionGoods.setNum(1); + promotionGoods.setQuantity(100); + promotionGoods.setPromotionType(PromotionTypeEnum.FULL_DISCOUNT.name()); + promotionGoods.setTitle("满" + fullDiscountVO.getFullMoney() + "减" + fullDiscountVO.getFullMinus()); + promotionGoods.setLimitNum(100); + promotionGoods.setPromotionId("200"); + promotionGoods.setStoreId("132"); + promotionGoodsLis.add(promotionGoods); + fullDiscountVO.setPromotionGoodsList(promotionGoodsLis); + Assertions.assertNotNull(fullDiscountService.modifyFullDiscount(fullDiscountVO)); + } + + @Test + void delete() { + Assertions.assertTrue(fullDiscountService.deleteFullDiscount("1325995092947525632")); + } + + +} diff --git a/manager-api/src/test/java/cn/lili/test/promotion/PromotionPriceTest.java b/manager-api/src/test/java/cn/lili/test/promotion/PromotionPriceTest.java new file mode 100644 index 00000000..3e8ffaeb --- /dev/null +++ b/manager-api/src/test/java/cn/lili/test/promotion/PromotionPriceTest.java @@ -0,0 +1,60 @@ +package cn.lili.test.promotion; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dto.BasePromotion; +import cn.lili.modules.promotion.entity.dto.PromotionGoodsDTO; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.service.PromotionPriceService; +import cn.lili.modules.promotion.service.PromotionService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.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; + +import java.util.Map; + +/** + * @author paulG + * @since 2020/11/23 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class PromotionPriceTest { + + @Autowired + private PromotionPriceService promotionPriceService; + + @Autowired + private PromotionService promotionService; + + @Autowired + private PromotionGoodsService promotionGoodsServiceService; + + @Test + void testSeckillPrice() { + Map currentPromotion = promotionService.getCurrentPromotion(); + for (Map.Entry entry : currentPromotion.entrySet()) { + BasePromotion promotion = (BasePromotion) entry.getValue(); + System.out.println(entry.getKey() + "-" + promotion.getId()); + } + Assertions.assertTrue(true); + } + + @Test + void testSeckillPrice1() { + IPage promotionGoods = promotionGoodsServiceService.getCurrentPromotionGoods(PromotionTypeEnum.FULL_DISCOUNT.name(), new PageVO()); + + ResultMessage> data = ResultUtil.data(promotionGoods); + String s = JSONUtil.toJsonStr(data); + System.out.println(s); + Assertions.assertTrue(true); + } + +} diff --git a/manager-api/src/test/java/cn/lili/test/promotion/SeckillTest.java b/manager-api/src/test/java/cn/lili/test/promotion/SeckillTest.java new file mode 100644 index 00000000..835b0716 --- /dev/null +++ b/manager-api/src/test/java/cn/lili/test/promotion/SeckillTest.java @@ -0,0 +1,93 @@ +package cn.lili.test.promotion; + +import cn.hutool.core.date.DateUtil; +import cn.lili.modules.promotion.entity.enums.PromotionApplyStatusEnum; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.enums.SeckillApplyStatusEnum; +import cn.lili.modules.promotion.entity.vos.SeckillApplyVO; +import cn.lili.modules.promotion.entity.vos.SeckillVO; +import cn.lili.modules.promotion.service.SeckillApplyService; +import cn.lili.modules.promotion.service.SeckillService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.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; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author paulG + * @since 2020/10/29 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class SeckillTest { + + @Autowired + private SeckillService seckillService; + + @Autowired + private SeckillApplyService seckillApplyService; + + @Test + void add() { + SeckillVO seckillVO = new SeckillVO(); + seckillVO.setId("123456"); + seckillVO.setStoreIds("132"); + seckillVO.setSeckillApplyStatus(SeckillApplyStatusEnum.NOT_APPLY.name()); + seckillVO.setPromotionStatus(PromotionStatusEnum.NEW.name()); + seckillVO.setApplyEndTime(DateUtil.parse("2020-11-13 23:50:00")); + seckillVO.setStartTime(DateUtil.parse("2020-11-14 12:00:00")); + seckillVO.setEndTime(DateUtil.parse("2020-11-14 18:00:00")); + seckillVO.setHours("13,14,15,16,17"); + seckillVO.setPromotionName("Seckill" + seckillVO.getId()); + seckillVO.setSeckillRule("rule" + seckillVO.getId()); + seckillVO.setStoreId("0"); + seckillVO.setStoreName("platform"); + Assertions.assertTrue(seckillService.saveSeckill(seckillVO)); + } + + @Test + void addApply() { + List seckillApplyVOS = new ArrayList<>(); + SeckillApplyVO seckillApplyVO = new SeckillApplyVO(); + seckillApplyVO.setGoodsName("Apple MacBook Pro 13.3 新款八核M1芯片 8G 256G SSD 深空灰 笔记本电脑 轻薄本 MYD82CH/A"); + seckillApplyVO.setSkuId("50111"); + seckillApplyVO.setOriginalPrice(20000D); + seckillApplyVO.setPrice(19000D); + seckillApplyVO.setPromotionApplyStatus(PromotionApplyStatusEnum.APPLY.name()); + seckillApplyVO.setQuantity(100); + seckillApplyVO.setSalesNum(0); + seckillApplyVO.setSeckillId("123456"); + seckillApplyVO.setStoreId("501"); + seckillApplyVO.setStoreName("Apple产品自营旗舰店"); + seckillApplyVO.setTimeLine(17); + seckillApplyVOS.add(seckillApplyVO); + seckillApplyVO = new SeckillApplyVO(); + seckillApplyVO.setGoodsName("RedmiBook 16 锐龙版 超轻薄全面屏(6核R5-4500U 16G 512G 100% sRGB高色域)灰 手提 笔记本电脑 小米 红米"); + seckillApplyVO.setSkuId("141"); + seckillApplyVO.setOriginalPrice(10000D); + seckillApplyVO.setPrice(9000D); + seckillApplyVO.setPromotionApplyStatus(PromotionApplyStatusEnum.APPLY.name()); + seckillApplyVO.setQuantity(100); + seckillApplyVO.setSalesNum(0); + seckillApplyVO.setSeckillId("123456"); + seckillApplyVO.setStoreId("131"); + seckillApplyVO.setStoreName("小米自营旗舰店"); + seckillApplyVO.setTimeLine(16); + seckillApplyVOS.add(seckillApplyVO); + seckillApplyService.addSeckillApply("123456", "501", seckillApplyVOS); + Assertions.assertTrue(true); + } + + + @Test + void audit() { + seckillApplyService.auditBatchApply(new String[]{"1327169604003061760"}, "123456", PromotionApplyStatusEnum.PASS.name(), ""); + Assertions.assertTrue(true); + } + +} diff --git a/manager-api/src/test/java/cn/lili/test/rocketmq/MsgExtRocketMqTest.java b/manager-api/src/test/java/cn/lili/test/rocketmq/MsgExtRocketMqTest.java new file mode 100644 index 00000000..f5f34bf1 --- /dev/null +++ b/manager-api/src/test/java/cn/lili/test/rocketmq/MsgExtRocketMqTest.java @@ -0,0 +1,39 @@ +package cn.lili.test.rocketmq; + +import cn.lili.common.rocketmq.RocketmqSendCallbackBuilder; +import cn.lili.common.rocketmq.tags.MqOrderTagsEnum; +import cn.lili.config.rocketmq.RocketmqCustomProperties; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author paulG + * @since 2021/1/15 + **/ +@RunWith(SpringRunner.class) +@SpringBootTest +class MsgExtRocketMqTest { + + @Autowired + private RocketMQTemplate rocketMQTemplate; + + @Autowired + private RocketmqCustomProperties rocketmqCustomProperties; + + @Test + void searchAll() { + String destination = rocketmqCustomProperties.getOrderTopic() + ":" + MqOrderTagsEnum.STATUS_CHANGE.name(); + Message message = MessageBuilder.withPayload("Context").build(); + rocketMQTemplate.asyncSend(destination, message, RocketmqSendCallbackBuilder.commonCallback()); + rocketMQTemplate.send(destination, message); + Assertions.assertTrue(true); + } + +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..b957fccf --- /dev/null +++ b/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.3.4.RELEASE + + + cn.lili + lili-shop-parent + 1.0.1 + + + registry.cn-beijing.aliyuncs.com/lili-images + 0.0.5 + + + framework + buyer-api + manager-api + seller-api + socket-api + common-api + consumer + + + + pom + + + + + + org.codehaus.mojo + versions-maven-plugin + 2.7 + + false + + + + + com.spotify + docker-maven-plugin + 1.2.2 + + ${docker-registry}/${project.build.finalName}:${images-version} + java + ["java", "-jar", "/${project.build.finalName}.jar"] + true + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + + + + + + + + central + aliyun maven + http://maven.aliyun.com/nexus/content/groups/public/ + default + + + true + + + + false + + + + \ No newline at end of file diff --git a/seller-api/pom.xml b/seller-api/pom.xml new file mode 100644 index 00000000..934079d4 --- /dev/null +++ b/seller-api/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + seller-api + + cn.lili + lili-shop-parent + 1.0.1 + + + + + cn.lili + framework + 1.0.1 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/seller-api/src/main/java/cn/lili/StoreApiApplication.java b/seller-api/src/main/java/cn/lili/StoreApiApplication.java new file mode 100644 index 00000000..26ced53e --- /dev/null +++ b/seller-api/src/main/java/cn/lili/StoreApiApplication.java @@ -0,0 +1,38 @@ +package cn.lili; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.core.task.TaskExecutor; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +/** + * 商家后台 API + * + * @author Chopper + * @date 2020/11/16 10:03 下午 + */ +@SpringBootApplication +@EnableJpaAuditing +@EnableCaching +@EnableAsync +public class StoreApiApplication { + + @Primary + @Bean + public TaskExecutor primaryTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + return executor; + } + + public static void main(String[] args) { + + System.setProperty("es.set.netty.runtime.available.processors", "false"); + SpringApplication.run(StoreApiApplication.class, args); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/distribution/DistributionGoodsStoreController.java b/seller-api/src/main/java/cn/lili/controller/distribution/DistributionGoodsStoreController.java new file mode 100644 index 00000000..1b663820 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/distribution/DistributionGoodsStoreController.java @@ -0,0 +1,71 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dos.DistributionGoods; +import cn.lili.modules.distribution.entity.dos.DistributionSelectedGoods; +import cn.lili.modules.distribution.entity.dto.DistributionGoodsSearchParams; +import cn.lili.modules.distribution.entity.vos.DistributionGoodsVO; +import cn.lili.modules.distribution.service.DistributionGoodsService; +import cn.lili.modules.distribution.service.DistributionSelectedGoodsService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 店铺端,分销商品接口 + * + * @author Bulbasaur + * @date 2020/11/16 10:06 下午 + */ +@RestController +@Api(tags = "店铺端,分销商品接口") +@RequestMapping("/store/distributionGoods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionGoodsStoreController { + + /** + * 分销商品 + */ + private final DistributionGoodsService distributionGoodsService; + + /** + * 已选择分销商品 + */ + private final DistributionSelectedGoodsService distributionSelectedGoodsService; + + @ApiOperation(value = "获取分销商商品列表") + @GetMapping + public ResultMessage> distributionGoods(DistributionGoodsSearchParams distributionGoodsSearchParams) { + + return ResultUtil.data(distributionGoodsService.goodsPage(distributionGoodsSearchParams)); + } + + @ApiOperation(value = "选择商品参与分销") + @ApiImplicitParam(name = "skuId", value = "规格ID", required = true, dataType = "String", paramType = "path") + @PutMapping(value = "/checked/{skuId}") + public ResultMessage distributionCheckGoods(@NotNull(message = "规格ID不能为空") @PathVariable String skuId, + @NotNull(message = "佣金金额不能为空") @RequestParam Double commission) { + return ResultUtil.data(distributionGoodsService.checked(skuId, commission)); + } + + @ApiOperation(value = "取消分销商品") + @ApiImplicitParam(name = "id", value = "分销商商品ID", required = true, paramType = "path") + @DeleteMapping(value = "/cancel/{id}") + public ResultMessage cancel(@NotNull @PathVariable String id) { + //清除分销商已选择分销商品 + distributionSelectedGoodsService.remove(new QueryWrapper().eq("distribution_goods_id", id)); + //清除分销商品 + distributionGoodsService.removeById(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/distribution/DistributionOrderStoreController.java b/seller-api/src/main/java/cn/lili/controller/distribution/DistributionOrderStoreController.java new file mode 100644 index 00000000..04c69440 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/distribution/DistributionOrderStoreController.java @@ -0,0 +1,46 @@ +package cn.lili.controller.distribution; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.distribution.entity.dos.DistributionOrder; +import cn.lili.modules.distribution.entity.vos.DistributionOrderSearchParams; +import cn.lili.modules.distribution.service.DistributionOrderService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +/** + * 店铺端,分销订单接口 + * + * @author Bulbasaur + * @date 2020/11/16 10:06 下午 + */ +@RestController +@Api(tags = "店铺端,分销订单接口") +@RequestMapping("/store/distributionOrder") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DistributionOrderStoreController { + + /** + * 分销订单 + */ + private final DistributionOrderService distributionOrderService; + + @ApiOperation(value = "获取分销订单列表") + @GetMapping + public ResultMessage> distributionOrder(DistributionOrderSearchParams distributionOrderSearchParams) { + + //获取当前登录商家账号-查询当前店铺的分销订单 + distributionOrderSearchParams.setStoreId(UserContext.getCurrentUser().getId()); + //查询分销订单列表 + IPage distributionOrderPage = distributionOrderService.getDistributionOrderPage(distributionOrderSearchParams); + return ResultUtil.data(distributionOrderPage); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/CategoryParameterGroupStoreController.java b/seller-api/src/main/java/cn/lili/controller/goods/CategoryParameterGroupStoreController.java new file mode 100644 index 00000000..9a2affbb --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/CategoryParameterGroupStoreController.java @@ -0,0 +1,65 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.CategoryParameterGroup; +import cn.lili.modules.goods.entity.dos.Parameters; +import cn.lili.modules.goods.entity.vos.ParameterGroupVO; +import cn.lili.modules.goods.service.CategoryParameterGroupService; +import cn.lili.modules.goods.service.ParametersService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 店铺端,分类绑定参数组管理接口 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@RestController +@Api(tags = "店铺端,分类绑定参数组管理接口") +@RequestMapping("/store/goods/category/parameters") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategoryParameterGroupStoreController { + + private final ParametersService parametersService; + private final CategoryParameterGroupService categoryParameterGroupService; + + @ApiOperation(value = "查询某分类下绑定的参数信息") + @GetMapping(value = "/{category_id}") + @ApiImplicitParam(name = "category_id", value = "分类id", required = true, dataType = "String", paramType = "path") + public List getCategoryParam(@PathVariable("category_id") String categoryId) { + return categoryParameterGroupService.getCategoryParams(categoryId); + } + + @ApiOperation(value = "编辑或更新数据") + @PostMapping(value = "/save") + public ResultMessage saveOrUpdate(CategoryParameterGroup categoryParameterGroup) { + + if (categoryParameterGroupService.save(categoryParameterGroup)) { + return ResultUtil.data(categoryParameterGroup); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "通过id删除参数组") + @DeleteMapping(value = "/{id}") + public ResultMessage delAllByIds(@PathVariable String id) { + //删除参数 + parametersService.remove(new QueryWrapper().eq("group_id", id)); + //删除参数组 + categoryParameterGroupService.removeById(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/CategorySpecificationStoreController.java b/seller-api/src/main/java/cn/lili/controller/goods/CategorySpecificationStoreController.java new file mode 100644 index 00000000..2b6122e9 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/CategorySpecificationStoreController.java @@ -0,0 +1,73 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.CategorySpecification; +import cn.lili.modules.goods.entity.vos.CategorySpecificationVO; +import cn.lili.modules.goods.entity.vos.GoodsSpecValueVO; +import cn.lili.modules.goods.service.CategorySpecificationService; +import cn.lili.modules.goods.service.SpecificationService; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 店铺端,商品分类规格接口 + * + * @author pikachu + * @date 2020-02-27 15:18:56 + */ +@RestController +@Api(tags = "店铺端,商品分类规格接口") +@RequestMapping("/store/goods/category/spec") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategorySpecificationStoreController { + + private final CategorySpecificationService categorySpecificationService; + + private final SpecificationService specificationService; + + + @ApiOperation(value = "查询某分类下绑定的规格信息") + @GetMapping(value = "/{category_id}") + @ApiImplicitParam(name = "category_id", value = "分类id", required = true, dataType = "String", paramType = "path") + public List getCategorySpec(@PathVariable("category_id") String categoryId) { + return categorySpecificationService.getCategorySpecList(categoryId); + } + + @ApiOperation(value = "查询某分类下绑定的规格信息,商品操作使用") + @GetMapping(value = "/goods/{category_id}") + @ApiImplicitParam(name = "category_id", value = "分类id", required = true, dataType = "String", paramType = "path") + public List getSpec(@PathVariable("category_id") String categoryId) { + return specificationService.getGoodsSpecValue(categoryId); + } + + + @ApiOperation(value = "保存某分类下绑定的规格信息") + @PostMapping(value = "/{category_id}") + @ApiImplicitParams({ + @ApiImplicitParam(name = "category_id", value = "分类id", required = true, paramType = "path", dataType = "String"), + @ApiImplicitParam(name = "category_specs", value = "规格id数组", required = true, paramType = "query", dataType = "String[]") + }) + public ResultMessage saveCategoryBrand(@PathVariable("category_id") String categoryId, @RequestParam("category_specs") String[] categorySpecs) { + //删除分类规格绑定信息 + this.categorySpecificationService.remove(new QueryWrapper().eq("category_id", categoryId)); + //绑定规格信息 + for (String specId : categorySpecs) { + CategorySpecification categoryBrand = new CategorySpecification(categoryId, specId); + categorySpecificationService.save(categoryBrand); + } + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/CategoryStoreController.java b/seller-api/src/main/java/cn/lili/controller/goods/CategoryStoreController.java new file mode 100644 index 00000000..e8901915 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/CategoryStoreController.java @@ -0,0 +1,70 @@ +package cn.lili.controller.goods; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.vos.CategoryBrandVO; +import cn.lili.modules.goods.entity.vos.CategoryVO; +import cn.lili.modules.goods.service.CategoryBrandService; +import cn.lili.modules.goods.service.CategoryService; +import cn.lili.modules.store.service.StoreDetailService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 店铺端,商品分类接口 + * + * @author Chopper + * @date 2021/2/20 2:26 下午 + */ +@RestController +@Api(tags = "店铺端,商品分类接口") +@RequestMapping("/store/goods/category") +@CacheConfig(cacheNames = "category") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CategoryStoreController { + + /** + * 分类 + */ + private final CategoryService categoryService; + /** + * 分类品牌 + */ + private final CategoryBrandService categoryBrandService; + /** + * 店铺详情 + */ + private final StoreDetailService storeDetailService; + + @ApiOperation(value = "获取店铺经营的分类") + @GetMapping(value = "/all") + public ResultMessage> getListAll() { + //获取店铺经营范围 + String goodsManagementCategory = storeDetailService.getStoreDetail(UserContext.getCurrentUser().getStoreId()).getGoodsManagementCategory(); + return ResultUtil.data(this.categoryService.getStoreCategory(goodsManagementCategory.split(","))); + } + + @ApiOperation(value = "获取所选分类关联的品牌信息") + @GetMapping(value = "/{categoryId}/brands") + @ApiImplicitParams({ + @ApiImplicitParam(name = "categoryId", value = "分类id", required = true, paramType = "path"), + }) + public List queryBrands(@PathVariable String categoryId) { + return this.categoryBrandService.getCategoryBrandList(categoryId); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/DraftGoodsStoreController.java b/seller-api/src/main/java/cn/lili/controller/goods/DraftGoodsStoreController.java new file mode 100644 index 00000000..5f936c5e --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/DraftGoodsStoreController.java @@ -0,0 +1,77 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.DraftGoods; +import cn.lili.modules.goods.entity.dto.DraftGoodsDTO; +import cn.lili.modules.goods.entity.dto.DraftGoodsSearchParams; +import cn.lili.modules.goods.entity.vos.DraftGoodsVO; +import cn.lili.modules.goods.service.DraftGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 店铺端,草稿商品接口 + * + * @author paulG + * @date 2021/2/20 2:26 下午 + * + */ +@RestController +@Api(tags = "店铺端,草稿商品接口") +@RequestMapping("/store/draft/goods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class DraftGoodsStoreController { + + private final DraftGoodsService draftGoodsService; + + + @ApiOperation(value = "分页获取草稿商品列表") + @GetMapping(value = "/page") + public ResultMessage> getDraftGoodsByPage(DraftGoodsSearchParams searchParams) { + searchParams.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(draftGoodsService.getDraftGoods(searchParams)); + } + + @ApiOperation(value = "获取草稿商品") + @GetMapping(value = "/{id}") + public ResultMessage getDraftGoods(@PathVariable String id) { + DraftGoodsVO draftGoods = draftGoodsService.getDraftGoods(id); + if (!UserContext.getCurrentUser().getStoreId().equals(draftGoods.getStoreId())) { + return ResultUtil.error(ResultCode.USER_AUTHORITY_ERROR); + } + return ResultUtil.data(draftGoods); + } + + @ApiOperation(value = "保存草稿商品") + @PostMapping(value = "/save", consumes = "application/json", produces = "application/json") + public ResultMessage saveDraftGoods(@RequestBody DraftGoodsDTO draftGoodsVO) { + if (draftGoodsVO.getStoreId() == null) { + AuthUser currentUser = UserContext.getCurrentUser(); + draftGoodsVO.setStoreId(currentUser.getStoreId()); + } else if (draftGoodsVO.getStoreId() != null && !UserContext.getCurrentUser().getStoreId().equals(draftGoodsVO.getStoreId())) { + return ResultUtil.error(ResultCode.USER_AUTHORITY_ERROR); + } + draftGoodsService.saveGoodsDraft(draftGoodsVO); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "删除草稿商品") + @DeleteMapping(value = "/{id}") + public ResultMessage deleteDraftGoods(@PathVariable String id) { + DraftGoods draftGoods = draftGoodsService.getById(id); + if (!draftGoods.getStoreId().equals(UserContext.getCurrentUser().getStoreId())) { + return ResultUtil.error(ResultCode.USER_AUTHORITY_ERROR); + } + draftGoodsService.deleteGoodsDraft(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/GoodsGalleryController.java b/seller-api/src/main/java/cn/lili/controller/goods/GoodsGalleryController.java new file mode 100644 index 00000000..cfb68471 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/GoodsGalleryController.java @@ -0,0 +1,86 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.goods.entity.dos.GoodsGallery; +import cn.lili.modules.goods.service.GoodsGalleryService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 店铺端,商品相册接口 + * + * @author pikachu + * @date 2020-03-13 11:18:56 + */ +@RestController +@Api(tags = "店铺端,商品相册接口") +@RequestMapping("/store/goodsGallery") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsGalleryController { + + private final GoodsGalleryService goodsGalleryService; + + @ApiOperation(value = "通过id获取") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + + GoodsGallery goodsGallery = goodsGalleryService.getById(id); + return ResultUtil.data(goodsGallery); + } + + @ApiOperation(value = "获取全部数据") + @GetMapping(value = "/getAll") + public ResultMessage> getAll() { + + List list = goodsGalleryService.list(); + return ResultUtil.data(list); + } + + @ApiOperation(value = "分页获取") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(GoodsGallery entity, + SearchVO searchVo, + PageVO page) { + IPage data = goodsGalleryService.page(PageUtil.initPage(page), PageUtil.initWrapper(entity, searchVo)); + return ResultUtil.data(data); + } + + @ApiOperation(value = "添加商品相册") + @PostMapping(value = "/save") + public ResultMessage save(GoodsGallery goodsGallery) { + + if (goodsGalleryService.save(goodsGallery)) { + return ResultUtil.data(goodsGallery); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改商品相册") + @PostMapping(value = "/update") + public ResultMessage update(GoodsGallery goodsGallery) { + + if (goodsGalleryService.updateById(goodsGallery)) { + return ResultUtil.data(goodsGallery); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "批量删除") + @DeleteMapping(value = "/delByIds/{ids}") + public ResultMessage delAllByIds(@PathVariable List ids) { + + goodsGalleryService.removeByIds(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/GoodsLabelStoreController.java b/seller-api/src/main/java/cn/lili/controller/goods/GoodsLabelStoreController.java new file mode 100644 index 00000000..fe1a3407 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/GoodsLabelStoreController.java @@ -0,0 +1,69 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.store.entity.dos.StoreGoodsLabel; +import cn.lili.modules.store.entity.vos.StoreGoodsLabelVO; +import cn.lili.modules.store.service.StoreGoodsLabelService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +/** + * 店铺端,店铺分类接口 + * + * @author Bulbasaur + * @date 2020/11/17 2:32 下午 + */ +@Api(tags = "店铺端,店铺分类接口") +@RestController +@RequestMapping("/store/goods/label") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsLabelStoreController { + + /** + * 店铺分类 + */ + private final StoreGoodsLabelService storeGoodsLabelService; + + @ApiOperation(value = "获取当前店铺商品分类列表") + @GetMapping + public ResultMessage> list() { + return ResultUtil.data(storeGoodsLabelService.listByStoreId(UserContext.getCurrentUser().getStoreId())); + } + + @ApiImplicitParam(name = "id", value = "店铺商品分类ID", required = true, paramType = "path") + @ApiOperation(value = "获取店铺商品分类详情") + @GetMapping("/get/{id}") + public ResultMessage getStoreGoodsLabel(@PathVariable String id) { + return ResultUtil.data(storeGoodsLabelService.getById(id)); + } + + @ApiOperation(value = "添加店铺商品分类") + @PostMapping + public ResultMessage add(StoreGoodsLabel storeGoodsLabel) { + return ResultUtil.data(storeGoodsLabelService.addStoreGoodsLabel(storeGoodsLabel)); + } + + @ApiOperation(value = "修改店铺商品分类") + @PutMapping + public ResultMessage edit(StoreGoodsLabel storeGoodsLabel) { + return ResultUtil.data(storeGoodsLabelService.editStoreGoodsLabel(storeGoodsLabel)); + } + + @ApiImplicitParam(name = "id", value = "店铺商品分类ID", required = true, paramType = "path") + @ApiOperation(value = "删除店铺商品分类") + @DeleteMapping("/{id}") + public ResultMessage delete(@PathVariable String id) { + storeGoodsLabelService.removeStoreGoodsLabel(id); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/GoodsStoreController.java b/seller-api/src/main/java/cn/lili/controller/goods/GoodsStoreController.java new file mode 100644 index 00000000..7c037a1a --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/GoodsStoreController.java @@ -0,0 +1,170 @@ +package cn.lili.controller.goods; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.Goods; +import cn.lili.modules.goods.entity.dos.GoodsSku; +import cn.lili.modules.goods.entity.dto.GoodsOperationDTO; +import cn.lili.modules.goods.entity.dto.GoodsSearchParams; +import cn.lili.modules.goods.entity.dto.GoodsSkuStockDTO; +import cn.lili.modules.goods.entity.enums.GoodsStatusEnum; +import cn.lili.modules.goods.entity.vos.GoodsSkuVO; +import cn.lili.modules.goods.entity.vos.GoodsVO; +import cn.lili.modules.goods.entity.vos.StockWarningVO; +import cn.lili.modules.goods.service.GoodsService; +import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.store.service.StoreDetailService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 店铺端,商品接口 + * + * @author pikachu + * @date 2020-02-23 15:18:56 + */ +@RestController +@Api(tags = "店铺端,商品接口") +@RequestMapping("/store/goods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsStoreController { + + //商品 + private final GoodsService goodsService; + //商品sku + private final GoodsSkuService goodsSkuService; + //店铺详情 + private final StoreDetailService storeDetailService; + + @ApiOperation(value = "分页获取商品列表") + @GetMapping(value = "/list") + public ResultMessage> getByPage(GoodsSearchParams goodsSearchParams) { + + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + goodsSearchParams.setStoreId(tokenUser.getStoreId()); + return ResultUtil.data(goodsService.queryByParams(goodsSearchParams)); + } + + @ApiOperation(value = "分页获取商品Sku列表") + @GetMapping(value = "/sku/list") + public ResultMessage> getSkuByPage(GoodsSearchParams goodsSearchParams) { + + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + goodsSearchParams.setStoreId(tokenUser.getStoreId()); + return ResultUtil.data(goodsSkuService.getGoodsSkuByPage(goodsSearchParams)); + } + + @ApiOperation(value = "分页获取库存告警商品列表") + @GetMapping(value = "/list/stock") + public ResultMessage getWarningStockByPage(GoodsSearchParams goodsSearchParams) { + //获取当前登录商家账号 + AuthUser tokenUser = UserContext.getCurrentUser(); + Integer stockWarnNum = storeDetailService.getStoreDetail(tokenUser.getStoreId()).getStockWarning(); + goodsSearchParams.setStoreId(tokenUser.getStoreId()); + goodsSearchParams.setQuantity(storeDetailService.getStoreDetail(tokenUser.getStoreId()).getStockWarning()); + goodsSearchParams.setMarketEnable(GoodsStatusEnum.UPPER.name()); + IPage goodsSku = goodsSkuService.getGoodsSkuByPage(goodsSearchParams); + StockWarningVO stockWarning = new StockWarningVO(stockWarnNum, goodsSku); + return ResultUtil.data(stockWarning); + } + + + @ApiOperation(value = "通过id获取") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + AuthUser tokenUser = UserContext.getCurrentUser(); + GoodsVO goods = goodsService.getGoodsVO(id); + if (tokenUser.getStoreId().equals(goods.getStoreId())) { + return ResultUtil.data(goods); + } + return ResultUtil.error(ResultCode.USER_AUTHORITY_ERROR); + } + + @ApiOperation(value = "新增商品") + @PostMapping(value = "/create", consumes = "application/json", produces = "application/json") + public ResultMessage save(@RequestBody GoodsOperationDTO goodsOperationDTO) { + + goodsService.addGoods(goodsOperationDTO); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "修改商品") + @PutMapping(value = "/update/{goodsId}", consumes = "application/json", produces = "application/json") + public ResultMessage update(@RequestBody GoodsOperationDTO goodsOperationDTO, @PathVariable String goodsId) { + goodsService.editGoods(goodsOperationDTO, goodsId); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "下架商品", notes = "下架商品时使用") + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, paramType = "query", allowMultiple = true) + @PutMapping(value = "/under") + public ResultMessage underGoods(@RequestParam List goodsId) { + + if (goodsService.updateGoodsMarketAble(goodsId, GoodsStatusEnum.DOWN, "商家下架")) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "上架商品", notes = "上架商品时使用") + @PutMapping(value = "/up") + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, paramType = "query", allowMultiple = true) + public ResultMessage unpGoods(@RequestParam List goodsId) { + if (goodsService.updateGoodsMarketAble(goodsId, GoodsStatusEnum.UPPER, "")) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "删除商品") + @PutMapping(value = "/delete") + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, paramType = "query", allowMultiple = true) + public ResultMessage deleteGoods(@RequestParam List goodsId) { + if (goodsService.deleteGoods(goodsId)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "设置商品运费模板") + @PutMapping(value = "/freight") + @ApiImplicitParams({ + @ApiImplicitParam(name = "goodsId", value = "商品ID", required = true, paramType = "query", allowMultiple = true), + @ApiImplicitParam(name = "freightPayer", value = "运费承担者", required = true, paramType = "query"), + @ApiImplicitParam(name = "templateId", value = "运费模板ID", required = true, paramType = "query") + }) + public ResultMessage freight(@RequestParam List goodsId, @RequestParam String freightPayer, @RequestParam String templateId) { + if (goodsService.freight(goodsId, freightPayer, templateId)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "根据goodsId分页获取商品规格列表") + @GetMapping(value = "/sku/{goodsId}/list") + public ResultMessage> getSkuByList(@PathVariable String goodsId) { + + return ResultUtil.data(goodsSkuService.getGoodsListByGoodsId(goodsId)); + } + + @ApiOperation(value = "修改商品库存") + @PutMapping(value = "/update/stocks", consumes = "application/json") + public ResultMessage updateStocks(@RequestBody List updateStockList) { + goodsSkuService.updateStocks(updateStockList); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/GoodsUnitStoreController.java b/seller-api/src/main/java/cn/lili/controller/goods/GoodsUnitStoreController.java new file mode 100644 index 00000000..5e5d032f --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/GoodsUnitStoreController.java @@ -0,0 +1,43 @@ +package cn.lili.controller.goods; + + +import cn.lili.common.utils.PageUtil; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.GoodsUnit; +import cn.lili.modules.goods.service.GoodsUnitService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 店铺端,商品计量单位接口 + * + * @author Bulbasaur + * @date 2020/11/26 16:15 + */ +@RestController +@Api(tags = "店铺端,商品计量单位接口") +@RequestMapping("/store/goods/unit") +@Transactional(rollbackFor = Exception.class) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsUnitStoreController { + + private final GoodsUnitService goodsUnitService; + + + @ApiOperation(value = "分页获取商品计量单位") + @GetMapping + public ResultMessage> getByPage(PageVO pageVO) { + return ResultUtil.data(goodsUnitService.page(PageUtil.initPage(pageVO))); + } + + +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/SpecValuesStoreController.java b/seller-api/src/main/java/cn/lili/controller/goods/SpecValuesStoreController.java new file mode 100644 index 00000000..2afcd64c --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/SpecValuesStoreController.java @@ -0,0 +1,56 @@ +package cn.lili.controller.goods; + + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.SpecValues; +import cn.lili.modules.goods.service.SpecValuesService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 店铺端,规格项管理接口 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@RestController +@Api(tags = "店铺端,规格项管理接口") +@RequestMapping("/store/goods/spec-values") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SpecValuesStoreController { + + private final SpecValuesService specValuesService; + + @GetMapping(value = "/values/{id}") + @ApiImplicitParam(name = "id", value = "规格项ID", required = true, dataType = "String", paramType = "path") + @ApiOperation(value = "查询规格值列表") + public ResultMessage> list(@PathVariable("id") String id, String specVal, PageVO pageVo) { + return ResultUtil.data(specValuesService.queryByParams(id, specVal, pageVo)); + } + + @ApiOperation(value = "保存规格值") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "商品规格ID", required = true, paramType = "path"), + @ApiImplicitParam(name = "specValue", value = "商品项", required = true, allowMultiple = true, paramType = "query") + }) + @PostMapping(value = "/save/{id}") + public ResultMessage> saveSpecValue(@PathVariable("id") String specId, + @NotNull(message = "至少添加一个规格值") @RequestParam(value = "spec_value") String[] specValue) { + //重新添加 + List list = specValuesService.addSpecValue(specId, specValue); + return ResultUtil.data(list); + } + + +} diff --git a/seller-api/src/main/java/cn/lili/controller/goods/SpecificationStoreController.java b/seller-api/src/main/java/cn/lili/controller/goods/SpecificationStoreController.java new file mode 100644 index 00000000..2c53173f --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/goods/SpecificationStoreController.java @@ -0,0 +1,66 @@ +package cn.lili.controller.goods; + + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.goods.entity.dos.Specification; +import cn.lili.modules.goods.entity.dto.SpecificationSearchParams; +import cn.lili.modules.goods.entity.vos.SpecificationVO; +import cn.lili.modules.goods.service.SpecificationService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + + +/** + * 店铺端,规格管理接口 + * + * @author pikachu + * @date 2020-02-18 15:18:56 + */ +@RestController +@Api(tags = "店铺端,规格管理接口") +@RequestMapping("/store/goods/spec") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SpecificationStoreController { + + private final SpecificationService specificationService; + + + @GetMapping(value = "/page") + @ApiOperation(value = "分页获取") + public ResultMessage> getByPage(SpecificationSearchParams searchParams, PageVO pageVo) { + searchParams.setDeleteFlag(false); + return ResultUtil.data(specificationService.getSpecificationByPage(searchParams, pageVo)); + } + + @PostMapping + @ApiOperation(value = "添加规格") + public ResultMessage save(@Valid SpecificationVO parameters) { + if (parameters.getStoreId() == null) { + parameters.setStoreId(UserContext.getCurrentUser().getId()); + } + if (specificationService.addSpecification(parameters) != null) { + return ResultUtil.data(parameters); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @DeleteMapping(value = "/{ids}") + @ApiImplicitParam(name = "ids", value = "规格ID", required = true, dataType = "String", allowMultiple = true, paramType = "path") + @ApiOperation(value = "批量删除") + public ResultMessage delAllByIds(@PathVariable List ids) { + specificationService.deleteSpecification(ids); + return ResultUtil.success(ResultCode.SUCCESS); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/member/StoreUserController.java b/seller-api/src/main/java/cn/lili/controller/member/StoreUserController.java new file mode 100644 index 00000000..5ce3b034 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/member/StoreUserController.java @@ -0,0 +1,47 @@ +package cn.lili.controller.member; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + + +/** + * 店铺端,管理员接口 + * + * @author Chopper + * @date 2020/11/16 10:57 + */ +@RestController +@Api(tags = "店铺端,管理员接口") +@RequestMapping("/store/user") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreUserController { + + private final MemberService memberService; + + + @GetMapping(value = "/info") + @ApiOperation(value = "获取当前登录用户接口") + public ResultMessage getUserInfo() { + AuthUser tokenUser = UserContext.getCurrentUser(); + if (tokenUser != null) { + Member member = memberService.findByUsername(tokenUser.getUsername()); + member.setPassword(null); + return ResultUtil.data(member); + } + return ResultUtil.error(ResultCode.USER_NOT_LOGIN); + } + + +} diff --git a/seller-api/src/main/java/cn/lili/controller/other/ArticleStoreController.java b/seller-api/src/main/java/cn/lili/controller/other/ArticleStoreController.java new file mode 100644 index 00000000..0af0cc9f --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/other/ArticleStoreController.java @@ -0,0 +1,54 @@ +package cn.lili.controller.other; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dos.Article; +import cn.lili.modules.page.entity.dto.ArticleSearchParams; +import cn.lili.modules.page.entity.vos.ArticleVO; +import cn.lili.modules.page.service.ArticleService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 店铺端,文章接口 + * + * @author pikachu + * @date 2020-05-06 15:18:56 + */ +@RestController +@Api(tags = "店铺端,文章接口") +@RequestMapping("/store/article") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ArticleStoreController { + + /** + * 文章 + */ + private final ArticleService articleService; + + @ApiOperation(value = "分页获取") + @ApiImplicitParams({ + @ApiImplicitParam(name = "categoryId", value = "文章分类ID", paramType = "query") + }) + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(ArticleSearchParams articleSearchParams) { + return ResultUtil.data(articleService.articlePage(articleSearchParams)); + } + + @ApiOperation(value = "查看文章") + @ApiImplicitParam(name = "id", value = "文章ID", required = true, paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage
get(@PathVariable String id) { + + return ResultUtil.data(articleService.getById(id)); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/passport/StorePassportController.java b/seller-api/src/main/java/cn/lili/controller/passport/StorePassportController.java new file mode 100644 index 00000000..e0825958 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/passport/StorePassportController.java @@ -0,0 +1,66 @@ +package cn.lili.controller.passport; + + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dos.Member; +import cn.lili.modules.member.service.MemberService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 店铺端,商家登录接口 + * + * @author Chopper + * @date 2020/12/22 15:02 + */ + +@RestController +@Api(tags = "店铺端,商家登录接口 ") +@RequestMapping("/store/login") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StorePassportController { + + /** + * 会员 + */ + private final MemberService memberService; + + + @ApiOperation(value = "登录接口") + @ApiImplicitParams({ + @ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query"), + @ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query") + }) + @PostMapping("/userLogin") + public ResultMessage userLogin(@NotNull(message = "用户名不能为空") @RequestParam String username, + @NotNull(message = "密码不能为空") @RequestParam String password) { + return ResultUtil.data(this.memberService.usernameStoreLogin(username, password)); + } + + @ApiOperation(value = "修改密码") + @ApiImplicitParams({ + @ApiImplicitParam(name = "password", value = "旧密码", required = true, paramType = "query"), + @ApiImplicitParam(name = "newPassword", value = "新密码", required = true, paramType = "query") + }) + @PostMapping("/modifyPass") + public ResultMessage modifyPass(@NotNull(message = "旧密码不能为空") @RequestParam String password, + @NotNull(message = "新密码不能为空") @RequestParam String newPassword) { + return ResultUtil.data(memberService.modifyPass(password, newPassword)); + } + + @ApiOperation(value = "刷新token") + @GetMapping("/refresh/{refreshToken}") + public ResultMessage refreshToken(@NotNull(message = "刷新token不能为空") @PathVariable String refreshToken) { + return ResultUtil.data(this.memberService.refreshStoreToken(refreshToken)); + } + + +} diff --git a/seller-api/src/main/java/cn/lili/controller/promotion/CouponStoreController.java b/seller-api/src/main/java/cn/lili/controller/promotion/CouponStoreController.java new file mode 100644 index 00000000..8a7a64d8 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/promotion/CouponStoreController.java @@ -0,0 +1,114 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.exception.ServiceException; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dos.Coupon; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.vos.CouponSearchParams; +import cn.lili.modules.promotion.entity.vos.CouponVO; +import cn.lili.modules.promotion.service.CouponService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 店铺端,优惠券接口 + * + * @author paulG + * @date 2020/8/28 + **/ +@RestController +@Api(tags = "店铺端,优惠券接口") +@RequestMapping("/store/promotion/coupon") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class CouponStoreController { + + private final CouponService couponService; + + @GetMapping + @ApiOperation(value = "获取优惠券列表") + public ResultMessage> getCouponList(CouponSearchParams queryParam, PageVO page) { + page.setNotConvert(true); + AuthUser currentUser = UserContext.getCurrentUser(); + queryParam.setStoreId(currentUser.getStoreId()); + IPage coupons = couponService.getCouponsByPageFromMongo(queryParam, page); + return ResultUtil.data(coupons); + } + + @ApiOperation(value = "获取优惠券详情") + @GetMapping("/{couponId}") + public ResultMessage getCouponList(@PathVariable String couponId) { + AuthUser currentUser = UserContext.getCurrentUser(); + Coupon coupon = couponService.getCouponDetailFromMongo(couponId); + if (coupon == null || !coupon.getStoreId().equals(currentUser.getStoreId())) { + return ResultUtil.error(ResultCode.USER_AUTHORITY_ERROR); + } + return ResultUtil.data(coupon); + } + + @ApiOperation(value = "添加优惠券") + @PostMapping(consumes = "application/json", produces = "application/json") + public ResultMessage addCoupon(@RequestBody CouponVO couponVO) { + AuthUser currentUser = UserContext.getCurrentUser(); + couponVO.setStoreId(currentUser.getStoreId()); + couponVO.setStoreName(currentUser.getStoreName()); + if (couponService.add(couponVO) != null) { + return ResultUtil.data(couponVO); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @PutMapping(consumes = "application/json", produces = "application/json") + @ApiOperation(value = "修改优惠券") + public ResultMessage updateCoupon(@RequestBody CouponVO couponVO) { + AuthUser currentUser = UserContext.getCurrentUser(); + couponVO.setStoreId(currentUser.getStoreId()); + couponVO.setStoreName(currentUser.getStoreName()); + couponVO.setPromotionStatus(PromotionStatusEnum.NEW.name()); + Coupon byId = couponService.getById(couponVO.getId()); + if (!currentUser.getStoreId().equals(byId.getStoreId())) { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + CouponVO coupon = couponService.updateCoupon(couponVO); + if (coupon != null) { + + return ResultUtil.data(coupon); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @DeleteMapping(value = "/{ids}") + @ApiOperation(value = "批量删除") + public ResultMessage delAllByIds(@PathVariable List ids) { + AuthUser currentUser = UserContext.getCurrentUser(); + Coupon byId = couponService.getById(ids.get(0)); + if (!currentUser.getStoreId().equals(byId.getStoreId())) { + throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR); + } + for (String id : ids) { + couponService.deleteCoupon(id); + } + return ResultUtil.success(ResultCode.SUCCESS); + } + + @ApiOperation(value = "修改优惠券状态") + @PutMapping("/status") + public ResultMessage updateCouponStatus(String couponIds, String promotionStatus) { + String[] split = couponIds.split(","); + if (couponService.updateCouponStatus(Arrays.asList(split), PromotionStatusEnum.valueOf(promotionStatus))) { + return ResultUtil.success(ResultCode.COUPON_EDIT_STATUS_SUCCESS); + } + return ResultUtil.error(ResultCode.COUPON_EDIT_STATUS_ERROR); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/promotion/FullDiscountStoreController.java b/seller-api/src/main/java/cn/lili/controller/promotion/FullDiscountStoreController.java new file mode 100644 index 00000000..9101f601 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/promotion/FullDiscountStoreController.java @@ -0,0 +1,79 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dos.FullDiscount; +import cn.lili.modules.promotion.entity.enums.PromotionStatusEnum; +import cn.lili.modules.promotion.entity.vos.FullDiscountSearchParams; +import cn.lili.modules.promotion.service.FullDiscountService; +import cn.lili.modules.order.cart.entity.vo.FullDiscountVO; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 店铺端,满额活动接口 + * + * @author paulG + * @date 2020/8/19 + **/ +@RestController +@Api(tags = "店铺端,满额活动接口") +@RequestMapping("/store/promotion/fullDiscount") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FullDiscountStoreController { + + private final FullDiscountService fullDiscountService; + + @ApiOperation(value = "新增满优惠活动") + @PostMapping(consumes = "application/json", produces = "application/json") + public ResultMessage addFullDiscount(@RequestBody FullDiscountVO fullDiscountVO) { + AuthUser currentUser = UserContext.getCurrentUser(); + fullDiscountVO.setStoreId(currentUser.getStoreId()); + fullDiscountVO.setStoreName(currentUser.getStoreName()); + fullDiscountVO.setPromotionStatus(PromotionStatusEnum.NEW.name()); + FullDiscount fullDiscount = fullDiscountService.addFullDiscount(fullDiscountVO); + return ResultUtil.data(fullDiscount); + } + + @ApiOperation(value = "通过id获取") + @GetMapping("/{id}") + public ResultMessage get(@PathVariable String id) { + FullDiscountVO fullDiscount = fullDiscountService.getFullDiscount(id); + return ResultUtil.data(fullDiscount); + } + + @ApiOperation(value = "根据条件分页查询满优惠活动") + @GetMapping + public ResultMessage> getFullDiscountByPage(FullDiscountSearchParams searchParams, PageVO page) { + String storeId = UserContext.getCurrentUser().getStoreId(); + searchParams.setStoreId(storeId); + IPage fullDiscountByPage = fullDiscountService.getFullDiscountByPageFromMongo(searchParams, page); + return ResultUtil.data(fullDiscountByPage); + } + + @ApiOperation(value = "修改满优惠活动") + @PutMapping(consumes = "application/json", produces = "application/json") + public ResultMessage editFullDiscount(@RequestBody FullDiscountVO fullDiscountVO) { + AuthUser currentUser = UserContext.getCurrentUser(); + fullDiscountVO.setStoreId(currentUser.getStoreId()); + fullDiscountVO.setStoreName(currentUser.getStoreName()); + fullDiscountService.modifyFullDiscount(fullDiscountVO); + return ResultUtil.success(ResultCode.FULL_DISCOUNT_EDIT_SUCCESS); + } + + @ApiOperation(value = "删除满优惠活动") + @DeleteMapping("/{id}") + public ResultMessage deleteFullDiscount(@PathVariable String id) { + fullDiscountService.deleteFullDiscount(id); + return ResultUtil.success(ResultCode.FULL_DISCOUNT_EDIT_DELETE); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/promotion/PintuanStoreController.java b/seller-api/src/main/java/cn/lili/controller/promotion/PintuanStoreController.java new file mode 100644 index 00000000..466667a4 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/promotion/PintuanStoreController.java @@ -0,0 +1,118 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dto.PromotionGoodsDTO; +import cn.lili.modules.promotion.entity.enums.PromotionTypeEnum; +import cn.lili.modules.promotion.entity.vos.PintuanSearchParams; +import cn.lili.modules.promotion.entity.vos.PintuanVO; +import cn.lili.modules.promotion.entity.vos.PromotionGoodsSearchParams; +import cn.lili.modules.promotion.service.PintuanService; +import cn.lili.modules.promotion.service.PromotionGoodsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; + +/** + * 店铺端,拼团管理接口 + * + * @author paulG + * @since 2020/10/9 + **/ +@RestController +@Api(tags = "店铺端,拼团管理接口") +@RequestMapping("/store/promotion/pintuan") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class PintuanStoreController { + + private final PintuanService pintuanService; + private final PromotionGoodsService promotionGoodsService; + + + @GetMapping + @ApiOperation(value = "根据条件分页查询拼团活动列表") + public ResultMessage> getPintuanByPage(PintuanSearchParams queryParam, PageVO pageVo) { + AuthUser currentUser = UserContext.getCurrentUser(); + queryParam.setStoreId(currentUser.getStoreId()); + IPage pintuanByPageFromMongo = pintuanService.getPintuanByPageFromMongo(queryParam, pageVo); + return ResultUtil.data(pintuanByPageFromMongo); + } + + @GetMapping(value = "/{id}") + @ApiOperation(value = "通过id获取") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(pintuanService.getPintuanByIdFromMongo(id)); + } + + @GetMapping("/goods/{pintuanId}") + @ApiOperation(value = "根据条件分页查询拼团活动商品列表") + public ResultMessage> getPintuanGoodsByPage(@PathVariable String pintuanId, PageVO pageVo) { + PromotionGoodsSearchParams searchParams = new PromotionGoodsSearchParams(); + searchParams.setPromotionId(pintuanId); + searchParams.setPromotionType(PromotionTypeEnum.PINTUAN.name()); + IPage promotionGoods = promotionGoodsService.getPromotionGoods(searchParams, pageVo); + return ResultUtil.data(promotionGoods); + } + + @PostMapping(consumes = "application/json", produces = "application/json") + @ApiOperation(value = "添加拼团活动") + public ResultMessage addPintuan(@RequestBody PintuanVO pintuan) { + AuthUser currentUser = UserContext.getCurrentUser(); + pintuan.setStoreId(currentUser.getStoreId()); + pintuan.setStoreName(currentUser.getStoreName()); + if (pintuanService.addPintuan(pintuan)) { + return ResultUtil.success(ResultCode.PINTUAN_ADD_SUCCESS); + } + return ResultUtil.error(ResultCode.PINTUAN_ADD_ERROR); + } + + @PutMapping(consumes = "application/json", produces = "application/json") + @ApiOperation(value = "修改拼团活动") + public ResultMessage editPintuan(@RequestBody PintuanVO pintuan) { + AuthUser currentUser = UserContext.getCurrentUser(); + pintuan.setStoreId(currentUser.getStoreId()); + pintuan.setStoreName(currentUser.getStoreName()); + if (pintuanService.modifyPintuan(pintuan)) { + return ResultUtil.success(ResultCode.PINTUAN_EDIT_SUCCESS); + } + return ResultUtil.error(ResultCode.PINTUAN_EDIT_ERROR); + } + + @PutMapping("/open/{pintuanId}") + @ApiOperation(value = "手动开启拼团活动") + public ResultMessage openPintuan(@PathVariable String pintuanId, Long startTime, Long endTime) { + if (pintuanService.openPintuan(pintuanId, new Date(startTime), new Date(endTime))) { + return ResultUtil.success(ResultCode.PINTUAN_MANUAL_OPEN_SUCCESS); + } + return ResultUtil.error(ResultCode.PINTUAN_MANUAL_OPEN_ERROR); + + } + + @PutMapping("/close/{pintuanId}") + @ApiOperation(value = "手动关闭拼团活动") + public ResultMessage closePintuan(@PathVariable String pintuanId) { + if (pintuanService.closePintuan(pintuanId)) { + return ResultUtil.success(ResultCode.PINTUAN_MANUAL_CLOSE_SUCCESS); + } + return ResultUtil.error(ResultCode.PINTUAN_MANUAL_CLOSE_ERROR); + } + + @DeleteMapping("/{pintuanId}") + @ApiOperation(value = "手动删除拼团活动") + public ResultMessage deletePintuan(@PathVariable String pintuanId) { + if (pintuanService.deletePintuan(pintuanId)) { + return ResultUtil.success(ResultCode.PINTUAN_DELETE_SUCCESS); + } + return ResultUtil.error(ResultCode.PINTUAN_DELETE_ERROR); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/promotion/SeckillStoreController.java b/seller-api/src/main/java/cn/lili/controller/promotion/SeckillStoreController.java new file mode 100644 index 00000000..458a2729 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/promotion/SeckillStoreController.java @@ -0,0 +1,85 @@ +package cn.lili.controller.promotion; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.promotion.entity.dos.Seckill; +import cn.lili.modules.promotion.entity.dos.SeckillApply; +import cn.lili.modules.promotion.entity.vos.SeckillApplyVO; +import cn.lili.modules.promotion.entity.vos.SeckillSearchParams; +import cn.lili.modules.promotion.entity.vos.SeckillVO; +import cn.lili.modules.promotion.service.SeckillApplyService; +import cn.lili.modules.promotion.service.SeckillService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 店铺端,限时抢购接口 + * + * @author paulG + * @date 2020/8/26 + **/ +@RestController +@Api(tags = "店铺端,限时抢购接口") +@RequestMapping("/store/promotion/seckill") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class SeckillStoreController { + + private final SeckillService seckillService; + private final SeckillApplyService seckillApplyService; + + @GetMapping + @ApiOperation(value = "获取限时抢购列表") + public ResultMessage> getSeckillPage(SeckillSearchParams queryParam, PageVO pageVo) { + IPage seckillPage = seckillService.getSeckillByPageFromMongo(queryParam, pageVo); + return ResultUtil.data(seckillPage); + } + + @GetMapping("/apply") + @ApiOperation(value = "获取限时抢购申请列表") + public ResultMessage> getSeckillApplyPage(SeckillSearchParams queryParam, PageVO pageVo) { + String storeId = UserContext.getCurrentUser().getStoreId(); + queryParam.setStoreId(storeId); + IPage seckillPage = seckillApplyService.getSeckillApplyFromMongo(queryParam, pageVo); + return ResultUtil.data(seckillPage); + } + + @GetMapping("/{seckillId}") + @ApiOperation(value = "获取限时抢购") + public ResultMessage getSeckill(@PathVariable String seckillId) { + return ResultUtil.data(seckillService.getSeckillByIdFromMongo(seckillId)); + } + + @GetMapping("/apply/{seckillApplyId}") + @ApiOperation(value = "获取限时抢购申请") + public ResultMessage getSeckillApply(@PathVariable String seckillApplyId) { + return ResultUtil.data(seckillApplyService.getById(seckillApplyId)); + } + + @PostMapping(path = "/apply/{seckillId}", consumes = "application/json", produces = "application/json") + @ApiOperation(value = "添加限时抢购申请") + public ResultMessage addSeckillApply(@PathVariable String seckillId, @RequestBody List applyVos) { + String storeId = UserContext.getCurrentUser().getStoreId(); + seckillApplyService.addSeckillApply(seckillId, storeId, applyVos); + return ResultUtil.success(ResultCode.SUCCESS); + } + + @DeleteMapping("/apply/{seckillId}/{ids}") + @ApiOperation(value = "删除限时抢购申请") + public ResultMessage deleteSeckillApply(@PathVariable("seckillId") String seckillId, @PathVariable("ids") String ids) { + String[] idsSplit = ids.split(","); + seckillApplyService.removeSeckillApplyByIds(seckillId, Arrays.asList(idsSplit)); + return ResultUtil.success(ResultCode.SUCCESS); + } + + +} diff --git a/seller-api/src/main/java/cn/lili/controller/settings/FreightTemplateStoreController.java b/seller-api/src/main/java/cn/lili/controller/settings/FreightTemplateStoreController.java new file mode 100644 index 00000000..1c349f7a --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/settings/FreightTemplateStoreController.java @@ -0,0 +1,67 @@ +package cn.lili.controller.settings; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.store.entity.vos.FreightTemplateVO; +import cn.lili.modules.store.service.FreightTemplateService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +/** + * 店铺端,运费模板接口 + * + * @author paulG + * @date 2020/8/26 + **/ +@RestController +@Api(tags = "店铺端,运费模板接口") +@RequestMapping("/store/freightTemplate") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class FreightTemplateStoreController { + + private final FreightTemplateService freightTemplateService; + + @ApiOperation(value = "商家运费模板列表") + @GetMapping + private ResultMessage> list() { + return ResultUtil.data(freightTemplateService.getFreightTemplateList(UserContext.getCurrentUser().getStoreId())); + } + + @ApiOperation(value = "获取商家运费模板详情") + @ApiImplicitParam(name = "id", value = "商家模板ID", required = true, paramType = "path") + @GetMapping("/{id}") + private ResultMessage list(@PathVariable String id) { + return ResultUtil.data(freightTemplateService.getFreightTemplate(id)); + } + + @ApiOperation(value = "添加商家运费模板") + @PostMapping + public ResultMessage add(@Valid @RequestBody FreightTemplateVO freightTemplateVO) { + return ResultUtil.data(freightTemplateService.addFreightTemplate(freightTemplateVO)); + } + + @ApiOperation(value = "修改商家运费模板") + @PutMapping("/{id}") + public ResultMessage edit(@PathVariable String id, @RequestBody @Valid FreightTemplateVO freightTemplateVO) { + return ResultUtil.data(freightTemplateService.editFreightTemplate(freightTemplateVO)); + } + + @ApiOperation(value = "删除商家运费模板") + @ApiImplicitParam(name = "id", value = "商家模板ID", required = true, paramType = "path") + @DeleteMapping("/{id}") + public ResultMessage edit(@PathVariable String id) { + if (freightTemplateService.removeFreightTemplate(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/settings/LogStoreController.java b/seller-api/src/main/java/cn/lili/controller/settings/LogStoreController.java new file mode 100644 index 00000000..e76db9aa --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/settings/LogStoreController.java @@ -0,0 +1,44 @@ +package cn.lili.controller.settings; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.common.vo.SearchVO; +import cn.lili.modules.permission.service.SystemLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +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; + + +/** + * 店铺端,日志管理接口 + * + * @author Chopper + * @date: 2020/11/22 14:23 + */ +@Transactional +@RestController +@Api(tags = "店铺端,日志管理接口") +@RequestMapping("/store/log") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class LogStoreController { + + private final SystemLogService systemLogService; + + @GetMapping(value = "/getAllByPage") + @ApiOperation(value = "分页获取全部") + public ResultMessage getAllByPage(@RequestParam(required = false) Integer type, + @RequestParam String key, + String operatorName, + SearchVO searchVo, + PageVO pageVo) { + return ResultUtil.data(systemLogService.queryLog(UserContext.getCurrentUser().getStoreId(), operatorName, key, searchVo, pageVo)); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/settings/LogisticsStoreController.java b/seller-api/src/main/java/cn/lili/controller/settings/LogisticsStoreController.java new file mode 100644 index 00000000..e8123017 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/settings/LogisticsStoreController.java @@ -0,0 +1,65 @@ +package cn.lili.controller.settings; + + +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.store.entity.dos.StoreLogistics; +import cn.lili.modules.system.entity.vo.StoreLogisticsVO; +import cn.lili.modules.system.service.StoreLogisticsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 店铺端,物流公司接口 + * + * @author Bulbasaur + * @date: 2020/11/22 14:23 + */ +@RestController +@Api(tags = "店铺端,物流公司接口") +@RequestMapping("/store/logistics") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class LogisticsStoreController { + + /** + * 物流公司 + */ + private final StoreLogisticsService storeLogisticsService; + + @ApiOperation(value = "获取商家物流公司列表,如果已选择则checked有值") + @GetMapping + public ResultMessage> get() { + return ResultUtil.data(storeLogisticsService.getStoreLogistics()); + } + + @ApiOperation(value = "获取商家已选择物流公司列表") + @GetMapping("/getChecked") + public ResultMessage> getChecked() { + return ResultUtil.data(storeLogisticsService.getStoreSelectedLogistics()); + } + + @ApiOperation(value = "选择物流公司") + @ApiImplicitParam(name = "logisticsId", value = "物流公司ID", required = true, paramType = "path") + @PostMapping("/{logisticsId}") + public ResultMessage checked(@PathVariable String logisticsId) { + return ResultUtil.data(storeLogisticsService.add(logisticsId)); + } + + + @ApiOperation(value = "取消选择物流公司") + @ApiImplicitParam(name = "id", value = "物流公司ID", required = true, paramType = "path") + @DeleteMapping(value = "/{id}") + public ResultMessage cancel(@PathVariable String id) { + storeLogisticsService.removeById(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/settings/StoreAddressController.java b/seller-api/src/main/java/cn/lili/controller/settings/StoreAddressController.java new file mode 100644 index 00000000..25936734 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/settings/StoreAddressController.java @@ -0,0 +1,78 @@ +package cn.lili.controller.settings; + + +import cn.lili.common.enums.MessageCode; +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.store.entity.dos.StoreAddress; +import cn.lili.modules.store.service.StoreAddressService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 店铺端,商家地址(自提点)接口 + * + * @author Bulbasaur + * @date: 2020/11/22 14:23 + */ +@RestController +@Api(tags = "店铺端,商家地址(自提点)接口") +@RequestMapping("/store/storeAddress") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreAddressController { + + /** + * 店铺自提点 + */ + private final StoreAddressService storeAddressService; + + @ApiOperation(value = "获取商家自提点分页") + @GetMapping + public ResultMessage> get(PageVO pageVo) { + return ResultUtil.data(storeAddressService.getStoreAddress(pageVo)); + } + + @ApiOperation(value = "获取商家自提点信息") + @ApiImplicitParam(name = "id", value = "自提点ID", required = true, paramType = "path") + @GetMapping("/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(storeAddressService.getById(id)); + } + + @ApiOperation(value = "添加") + @PostMapping + public ResultMessage add(@Valid StoreAddress storeAddress) { + storeAddress.setStoreId(UserContext.getCurrentUser().getStoreId()); + storeAddressService.save(storeAddress); + return ResultUtil.data(storeAddress); + } + + @ApiOperation(value = "编辑") + @ApiImplicitParam(name = "id", value = "自提点ID", required = true, paramType = "path") + @PutMapping("/{id}") + public ResultMessage edit(@PathVariable String id, @Valid StoreAddress storeAddress) { + storeAddress.setId(id); + storeAddress.setStoreId(UserContext.getCurrentUser().getStoreId()); + storeAddressService.updateById(storeAddress); + return ResultUtil.data(storeAddress); + } + + @ApiOperation(value = "删除") + @ApiImplicitParam(name = "id", value = "自提点ID", required = true, paramType = "path") + @DeleteMapping(value = "/{id}") + public ResultMessage delByIds(@PathVariable String id) { + storeAddressService.removeStoreAddress(id); + return ResultUtil.success(ResultCode.SUCCESS); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/settings/StoreMessageController.java b/seller-api/src/main/java/cn/lili/controller/settings/StoreMessageController.java new file mode 100644 index 00000000..4affe5d0 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/settings/StoreMessageController.java @@ -0,0 +1,110 @@ +package cn.lili.controller.settings; + + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.message.entity.dos.StoreMessage; +import cn.lili.modules.message.entity.enums.MessageStatusEnum; +import cn.lili.modules.message.entity.vos.StoreMessageQueryVO; +import cn.lili.modules.message.service.StoreMessageService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * 店铺端,消息接口 + * + * @author Bulbasaur + * @date: 2020/11/22 14:23 + */ +@RestController +@Api(tags = "店铺端,消息接口") +@RequestMapping("/store/message") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreMessageController { + + /** + * 商家消息 + */ + private final StoreMessageService storeMessageService; + + @ApiOperation(value = "获取商家消息") + @ApiImplicitParam(name = "status", value = "状态", required = true, paramType = "query") + @GetMapping + public ResultMessage> getPage(String status, PageVO pageVo) { + StoreMessageQueryVO storeMessageQueryVO = new StoreMessageQueryVO(); + storeMessageQueryVO.setStatus(status); + storeMessageQueryVO.setStoreId(UserContext.getCurrentUser().getStoreId()); + IPage page = storeMessageService.getPage(storeMessageQueryVO, pageVo); + return ResultUtil.data(page); + } + + + @ApiOperation(value = "获取商家消息总汇") + @GetMapping("/all") + public ResultMessage> getPage(PageVO pageVo) { + //返回值定义 + Map map = new HashMap<>(); + StoreMessageQueryVO storeMessageQueryVO = new StoreMessageQueryVO(); + storeMessageQueryVO.setStoreId(UserContext.getCurrentUser().getStoreId()); + //未读消息 + storeMessageQueryVO.setStatus(MessageStatusEnum.UN_READY.name()); + IPage page = storeMessageService.getPage(storeMessageQueryVO, pageVo); + map.put("UN_READY", page); + //已读消息 + storeMessageQueryVO.setStatus(MessageStatusEnum.ALREADY_READY.name()); + page = storeMessageService.getPage(storeMessageQueryVO, pageVo); + map.put("ALREADY_READY", page); + //回收站 + storeMessageQueryVO.setStatus(MessageStatusEnum.ALREADY_REMOVE.name()); + page = storeMessageService.getPage(storeMessageQueryVO, pageVo); + map.put("ALREADY_REMOVE", page); + return ResultUtil.data(map); + } + + @ApiOperation(value = "已读操作") + @ApiImplicitParam(name = "id", value = "店铺消息id", required = true, paramType = "path") + @PutMapping("/{id}/read") + public ResultMessage readMessage(@PathVariable String id) { + Boolean result = storeMessageService.editStatus(MessageStatusEnum.ALREADY_READY.name(), id); + return ResultUtil.data(result); + + } + + @ApiOperation(value = "回收站还原消息") + @ApiImplicitParam(name = "id", value = "店铺消息id", required = true, paramType = "path") + @PutMapping("/{id}/reduction") + public ResultMessage reductionMessage(@PathVariable String id) { + Boolean result = storeMessageService.editStatus(MessageStatusEnum.ALREADY_READY.name(), id); + return ResultUtil.data(result); + + } + + @ApiOperation(value = "删除操作") + @ApiImplicitParam(name = "id", value = "店铺消息id", required = true, paramType = "path") + @DeleteMapping("/{id}/delete") + public ResultMessage deleteMessage(@PathVariable String id) { + Boolean result = storeMessageService.editStatus(MessageStatusEnum.ALREADY_REMOVE.name(), id); + return ResultUtil.data(result); + + } + + @ApiOperation(value = "彻底删除操作") + @ApiImplicitParam(name = "id", value = "店铺消息id", required = true, paramType = "path") + @DeleteMapping("/{id}") + public ResultMessage disabled(@PathVariable String id) { + Boolean result = storeMessageService.deleteByMessageId(id); + return ResultUtil.data(result); + } + + +} diff --git a/seller-api/src/main/java/cn/lili/controller/settings/StorePageDataController.java b/seller-api/src/main/java/cn/lili/controller/settings/StorePageDataController.java new file mode 100644 index 00000000..2d2c3ffc --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/settings/StorePageDataController.java @@ -0,0 +1,90 @@ +package cn.lili.controller.settings; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.page.entity.dos.PageData; +import cn.lili.modules.page.entity.dto.PageDataDTO; +import cn.lili.modules.page.entity.enums.PageEnum; +import cn.lili.modules.page.entity.vos.PageDataListVO; +import cn.lili.modules.page.service.PageDataService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +/** + * 店铺端,页面接口 + * + * @author paulGao + * @date: 2020/11/22 14:23 + */ +@RestController +@Api(tags = "店铺端,页面接口") +@RequestMapping("/store/pageData") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StorePageDataController { + + private final PageDataService pageDataService; + + @ApiOperation(value = "页面列表") + @ApiImplicitParam(name = "pageClientType", value = "客户端类型", required = true, dataType = "String", paramType = "path") + @GetMapping("/{pageClientType}/pageDataList") + public ResultMessage> pageDataList(@PathVariable String pageClientType, PageVO pageVO) { + PageDataDTO pageDataDTO = new PageDataDTO(); + pageDataDTO.setPageType(PageEnum.STORE.name()); + pageDataDTO.setPageClientType(pageClientType); + pageDataDTO.setNum(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(pageDataService.getPageDataList(pageVO, pageDataDTO)); + } + + @ApiOperation(value = "获取页面信息") + @ApiImplicitParam(name = "id", value = "页面ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage getPageData(@PathVariable String id) { + return ResultUtil.data(pageDataService.getById(id)); + } + + @ApiOperation(value = "添加店铺首页") + @ApiImplicitParams({ + @ApiImplicitParam(name = "name", value = "页面名称", required = true, dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "pageClientType", value = "客户端类型", required = true, dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "pageData", value = "页面数据", required = true, dataType = "String", paramType = "query") + }) + @PostMapping("/save") + public ResultMessage savePageData(@RequestParam String name, @RequestParam String pageClientType, @RequestParam String pageData) { + return ResultUtil.data(pageDataService.addPageData(new PageData(name, pageClientType, pageData))); + } + + @ApiOperation(value = "修改首页") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "页面ID", required = true, dataType = "String", paramType = "path") + }) + @PutMapping("/update/{id}") + public ResultMessage updatePageData(@Valid PageData pageData, @PathVariable String id) { + pageData.setId(id); + return ResultUtil.data(pageDataService.updatePageData(pageData)); + } + + @ApiOperation(value = "发布页面") + @ApiImplicitParam(name = "id", value = "页面ID", required = true, dataType = "String", paramType = "path") + @PutMapping("/release/{id}") + public ResultMessage releasePageData(@PathVariable String id) { + return ResultUtil.data(pageDataService.releasePageData(id)); + } + + @ApiOperation(value = "删除页面") + @ApiImplicitParam(name = "id", value = "页面ID", required = true, dataType = "String", paramType = "path") + @DeleteMapping("/removePageData/{id}") + public ResultMessage removePageData(@PathVariable String id) { + return ResultUtil.data(pageDataService.removePageData(id)); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/settings/StoreSettingsController.java b/seller-api/src/main/java/cn/lili/controller/settings/StoreSettingsController.java new file mode 100644 index 00000000..cef6fa54 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/settings/StoreSettingsController.java @@ -0,0 +1,90 @@ +package cn.lili.controller.settings; + + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.store.entity.dos.StoreDetail; +import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO; +import cn.lili.modules.store.entity.dto.StoreSettingDTO; +import cn.lili.modules.store.entity.vos.StoreVO; +import cn.lili.modules.store.service.StoreDetailService; +import cn.lili.modules.store.service.StoreService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * 店铺端,店铺设置接口 + * + * @author Bulbasaur + * @date: 2020/11/22 14:23 + */ +@RestController +@Api(tags = "店铺端,店铺设置接口") +@RequestMapping("/store/settings") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreSettingsController { + + /** + * 店铺 + */ + private final StoreService storeService; + /** + * 店铺详情 + */ + private final StoreDetailService storeDetailService; + + @ApiOperation(value = "获取商家设置") + @GetMapping + public ResultMessage get() { + //获取当前登录商家内容 + return ResultUtil.data(storeService.getStoreDetail()); + } + + @ApiOperation(value = "修改商家设置") + @PutMapping + public ResultMessage edit(@Valid StoreSettingDTO storeSettingDTO) { + //修改商家设置 + if (storeDetailService.editStoreSetting(storeSettingDTO)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改店铺库存预警数量") + @ApiImplicitParam(name = "stockWarning", value = "库存预警数量", required = true, dataType = "Integer", paramType = "query") + @PutMapping("/updateStockWarning") + public ResultMessage updateStockWarning(Integer stockWarning) { + //修改商家设置 + if (storeDetailService.updateStockWarning(stockWarning)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "获取商家退货收件地址") + @GetMapping("/storeAfterSaleAddress") + public ResultMessage getStoreAfterSaleAddress() { + //获取当前登录商家内容 + return ResultUtil.data(storeDetailService.getStoreAfterSaleAddressDTO()); + } + + @ApiOperation(value = "修改商家退货收件地址") + @PutMapping("/storeAfterSaleAddress") + public ResultMessage editStoreAfterSaleAddress(@Valid StoreAfterSaleAddressDTO storeAfterSaleAddressDTO) { + //修改商家退货收件地址 + if (storeDetailService.editStoreAfterSaleAddressDTO(storeAfterSaleAddressDTO)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/statistics/GoodsStatisticsStoreController.java b/seller-api/src/main/java/cn/lili/controller/statistics/GoodsStatisticsStoreController.java new file mode 100644 index 00000000..4a171d92 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/statistics/GoodsStatisticsStoreController.java @@ -0,0 +1,42 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.statistics.model.dto.GoodsStatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.GoodsStatisticsDataVO; +import cn.lili.modules.statistics.service.GoodsStatisticsDataService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 店铺端,商品统计接口 + * + * @author Bulbasaur + * @date: 2020/11/22 14:23 + */ +@Api(tags = "店铺端,商品统计接口") +@RestController +@RequestMapping("/store/statistics/goods") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class GoodsStatisticsStoreController { + + /** + * 商品统计 + */ + private final GoodsStatisticsDataService goodsStatisticsDataService; + + @ApiOperation(value = "获取统计列表,排行前一百的数据") + @GetMapping + public ResultMessage> getByPage(GoodsStatisticsQueryParam statisticsQueryParam) { + statisticsQueryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(goodsStatisticsDataService.getGoodsStatisticsData(statisticsQueryParam, 100)); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/statistics/IndexStatisticsStoreController.java b/seller-api/src/main/java/cn/lili/controller/statistics/IndexStatisticsStoreController.java new file mode 100644 index 00000000..37579ef0 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/statistics/IndexStatisticsStoreController.java @@ -0,0 +1,54 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.statistics.model.dto.GoodsStatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.GoodsStatisticsDataVO; +import cn.lili.modules.statistics.model.vo.StoreIndexStatisticsVO; +import cn.lili.modules.statistics.service.GoodsStatisticsDataService; +import cn.lili.modules.statistics.service.IndexStatisticsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 店铺端,首页统计接口 + * + * @author Bulbasaur + * @date: 2020/12/9 19:04 + */ +@Api(tags = "店铺端,首页统计接口") +@RestController +@RequestMapping("/store/statistics/index") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class IndexStatisticsStoreController { + + /** + * 热卖商品统计 + */ + private final GoodsStatisticsDataService goodsStatisticsDataService; + /** + * 首页统计 + */ + private final IndexStatisticsService indexStatisticsService; + + @ApiOperation(value = "获取统计列表,排行前一百的数据") + @GetMapping("/top100") + public ResultMessage> getByPage(GoodsStatisticsQueryParam statisticsQueryParam) { + statisticsQueryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(goodsStatisticsDataService.getGoodsStatisticsData(statisticsQueryParam, 100)); + } + + @ApiOperation(value = "获取首页查询数据") + @GetMapping + public ResultMessage index() { + return ResultUtil.data(indexStatisticsService.storeIndexStatistics()); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/statistics/OrderStatisticsStoreController.java b/seller-api/src/main/java/cn/lili/controller/statistics/OrderStatisticsStoreController.java new file mode 100644 index 00000000..936614e1 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/statistics/OrderStatisticsStoreController.java @@ -0,0 +1,96 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import cn.lili.modules.order.order.service.AfterSaleService; +import cn.lili.modules.order.order.service.OrderService; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.OrderOverviewVO; +import cn.lili.modules.statistics.model.vo.OrderStatisticsDataVO; +import cn.lili.modules.statistics.service.OrderStatisticsDataService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 店铺端,订单统计接口 + * + * @author Bulbasaur + * @date: 2020/12/9 19:04 + */ +@Api(tags = "店铺端,订单统计接口") +@RestController +@RequestMapping("/store/statistics/order") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderStatisticsStoreController { + + /** + * 订单 + */ + private final OrderService orderService; + /** + * 售后 + */ + private final AfterSaleService afterSaleService; + /** + * 订单统计 + */ + private final OrderStatisticsDataService orderStatisticsDataService; + + @ApiOperation(value = "订单概览统计") + @GetMapping("/overview") + public ResultMessage overview(StatisticsQueryParam statisticsQueryParam) { + try { + statisticsQueryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(orderStatisticsDataService.overview(statisticsQueryParam)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @ApiOperation(value = "订单图表统计") + @GetMapping + public ResultMessage> statisticsChart(StatisticsQueryParam statisticsQueryParam) { + try { + statisticsQueryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(orderStatisticsDataService.statisticsChart(statisticsQueryParam)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + @ApiOperation(value = "订单统计") + @GetMapping("/order") + public ResultMessage> order(StatisticsQueryParam statisticsQueryParam, PageVO pageVO) { + + try { + statisticsQueryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(orderService.getStatistics(statisticsQueryParam, pageVO)); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + + @ApiOperation(value = "退单统计") + @GetMapping("/refund") + public ResultMessage> refund(StatisticsQueryParam statisticsQueryParam, PageVO pageVO) { + statisticsQueryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(afterSaleService.getStatistics(statisticsQueryParam, pageVO)); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/statistics/RefundOrderStatisticsStoreController.java b/seller-api/src/main/java/cn/lili/controller/statistics/RefundOrderStatisticsStoreController.java new file mode 100644 index 00000000..189148dd --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/statistics/RefundOrderStatisticsStoreController.java @@ -0,0 +1,47 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.RefundOrderStatisticsDataVO; +import cn.lili.modules.statistics.service.RefundOrderStatisticsService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +/** + * 店铺端,退款统计接口 + * + * @author Bulbasaur + * @date: 2020/12/9 19:04 + */ +@Api(tags = "店铺端,退款统计接口") +@RestController +@RequestMapping("/store/statistics/refund/order") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RefundOrderStatisticsStoreController { + + private final RefundOrderStatisticsService refundOrderStatisticsService; + + @ApiOperation(value = "获取退款统计列表") + @GetMapping("/getByPage") + public ResultMessage> getByPage(PageVO pageVO, StatisticsQueryParam statisticsQueryParam) { + statisticsQueryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(refundOrderStatisticsService.getRefundOrderStatisticsData(pageVO, statisticsQueryParam)); + } + + @ApiOperation(value = "获取退款统计金额") + @GetMapping("/getPrice") + public ResultMessage getPrice(StatisticsQueryParam statisticsQueryParam) { + statisticsQueryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + Double price = refundOrderStatisticsService.getRefundOrderStatisticsPrice(statisticsQueryParam); + return ResultUtil.data(price); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/statistics/ViewStatisticsStoreController.java b/seller-api/src/main/java/cn/lili/controller/statistics/ViewStatisticsStoreController.java new file mode 100644 index 00000000..ca1c8f86 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/statistics/ViewStatisticsStoreController.java @@ -0,0 +1,39 @@ +package cn.lili.controller.statistics; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.statistics.model.dto.StatisticsQueryParam; +import cn.lili.modules.statistics.model.vo.PlatformViewVO; +import cn.lili.modules.statistics.service.PlatformViewDataService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +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.RestController; + +import java.util.List; + +/** + * 店铺端,流量统计接口 + * + * @author Chopper + * @date 2021/2/9 11:19 + */ +@Api(tags = "店铺端,流量统计接口") +@RestController +@RequestMapping("/store/statistics/view") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ViewStatisticsStoreController { + + private final PlatformViewDataService platformViewDataService; + + @ApiOperation(value = "流量数据 表单获取") + @GetMapping("/list") + public ResultMessage> getByPage(StatisticsQueryParam queryParam) { + queryParam.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(platformViewDataService.list(queryParam)); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/trade/AfterSaleStoreController.java b/seller-api/src/main/java/cn/lili/controller/trade/AfterSaleStoreController.java new file mode 100644 index 00000000..d7c5f70f --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/trade/AfterSaleStoreController.java @@ -0,0 +1,85 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.AfterSale; +import cn.lili.modules.order.order.entity.vo.AfterSaleSearchParams; +import cn.lili.modules.order.order.entity.vo.AfterSaleVO; +import cn.lili.modules.order.order.service.AfterSaleService; +import cn.lili.modules.system.entity.vo.Traces; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + * 店铺端,售后管理接口 + * + * @author Chopper + * @date 2020/11/17 4:29 下午 + */ +@RestController +@Api(tags = "店铺端,售后管理接口") +@RequestMapping("/store/afterSale") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AfterSaleStoreController { + + private final AfterSaleService afterSaleService; + + @ApiOperation(value = "查看售后服务详情") + @ApiImplicitParam(name = "sn", value = "售后单号", required = true, paramType = "path") + @GetMapping(value = "/{sn}") + public ResultMessage get(@PathVariable String sn) { + return ResultUtil.data(afterSaleService.getAfterSale(sn)); + } + + @ApiOperation(value = "分页获取售后服务") + @GetMapping(value = "/page") + public ResultMessage> getByPage(AfterSaleSearchParams searchParams) { + return ResultUtil.data(afterSaleService.getAfterSalePages(searchParams)); + } + + @ApiOperation(value = "审核售后申请") + @ApiImplicitParams({ + @ApiImplicitParam(name = "afterSaleSn", value = "售后sn", required = true, paramType = "path"), + @ApiImplicitParam(name = "serviceStatus", value = "PASS:审核通过,REFUSE:审核未通过", required = true, paramType = "query"), + @ApiImplicitParam(name = "remark", value = "备注", paramType = "query"), + @ApiImplicitParam(name = "actualRefundPrice", value = "实际退款金额", paramType = "query") + }) + @PutMapping(value = "/review/{afterSaleSn}") + public ResultMessage review(@NotNull(message = "请选择售后单") @PathVariable String afterSaleSn, + @NotNull(message = "请审核") String serviceStatus, + String remark, + Double actualRefundPrice) { + + return ResultUtil.data(afterSaleService.review(afterSaleSn, serviceStatus, remark,actualRefundPrice)); + } + + @ApiOperation(value = "卖家确认收货") + @ApiImplicitParams({ + @ApiImplicitParam(name = "afterSaleSn", value = "售后sn", required = true, paramType = "path"), + @ApiImplicitParam(name = "serviceStatus", value = "PASS:审核通过,REFUSE:审核未通过", required = true, paramType = "query"), + @ApiImplicitParam(name = "remark", value = "备注", paramType = "query") + }) + @PutMapping(value = "/confirm/{afterSaleSn}") + public ResultMessage confirm(@NotNull(message = "请选择售后单") @PathVariable String afterSaleSn, + @NotNull(message = "请审核") String serviceStatus, + String remark) { + + return ResultUtil.data(afterSaleService.storeConfirm(afterSaleSn, serviceStatus, remark)); + } + + @ApiOperation(value = "查看买家退货物流踪迹") + @ApiImplicitParam(name = "sn", value = "售后单号", required = true, paramType = "path") + @GetMapping(value = "/getDeliveryTraces/{sn}") + public ResultMessage getDeliveryTraces(@PathVariable String sn) { + return ResultUtil.data(afterSaleService.deliveryTraces(sn)); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/trade/BillStoreController.java b/seller-api/src/main/java/cn/lili/controller/trade/BillStoreController.java new file mode 100644 index 00000000..846f8afa --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/trade/BillStoreController.java @@ -0,0 +1,76 @@ +package cn.lili.controller.trade; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.StoreFlow; +import cn.lili.modules.store.entity.dos.Bill; +import cn.lili.modules.store.entity.dto.BillSearchParams; +import cn.lili.modules.store.entity.vos.BillListVO; +import cn.lili.modules.store.service.BillService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 店铺端,结算单接口 + * + * @author Chopper + * @date 2020/11/17 4:29 下午 + */ +@RestController +@Api(tags = "店铺端,结算单接口") +@RequestMapping("/store/bill") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BillStoreController { + + private final BillService billService; + + @ApiOperation(value = "获取结算单分页") + @GetMapping(value = "/getByPage") + public ResultMessage> getByPage(BillSearchParams billSearchParams) { + return ResultUtil.data(billService.billPage(billSearchParams)); + } + + @ApiOperation(value = "通过id获取结算单") + @ApiImplicitParam(name = "id", value = "结算单ID", required = true, paramType = "path", dataType = "String") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(billService.getById(id)); + } + + @ApiOperation(value = "获取商家结算单流水分页") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "结算单ID", required = true, paramType = "path", dataType = "String"), + @ApiImplicitParam(name = "flowType", value = "流水类型:PAY、REFUND", paramType = "query", dataType = "String") + }) + @GetMapping(value = "/{id}/getStoreFlow") + public ResultMessage> getStoreFlow(@PathVariable String id, String flowType, PageVO pageVO) { + return ResultUtil.data(billService.getStoreFlow(id, flowType, pageVO)); + } + + @ApiOperation(value = "获取商家分销订单流水分页") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "结算单ID", required = true, paramType = "path", dataType = "String") + }) + @GetMapping(value = "/{id}/getDistributionFlow") + public ResultMessage> getDistributionFlow(@PathVariable String id, PageVO pageVO) { + return ResultUtil.data(billService.getDistributionFlow(id, pageVO)); + } + + @ApiOperation(value = "核对结算单") + @ApiImplicitParam(name = "id", value = "结算单ID", required = true, paramType = "path", dataType = "String") + @PutMapping(value = "/check/{id}") + public ResultMessage examine(@PathVariable String id) { + if (billService.check(id)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/trade/MemberEvaluationStoreController.java b/seller-api/src/main/java/cn/lili/controller/trade/MemberEvaluationStoreController.java new file mode 100644 index 00000000..8e017259 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/trade/MemberEvaluationStoreController.java @@ -0,0 +1,60 @@ +package cn.lili.controller.trade; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dto.StoreEvaluationQueryParams; +import cn.lili.modules.member.entity.vo.MemberEvaluationListVO; +import cn.lili.modules.member.entity.vo.MemberEvaluationVO; +import cn.lili.modules.member.service.MemberEvaluationService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 店铺端,商品评价管理接口 + * + * @author Bulbasaur + * @date 2020-02-25 14:10:16 + */ +@RestController +@Api(tags = "店铺端,商品评价管理接口") +@RequestMapping("/store/memberEvaluation") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class MemberEvaluationStoreController { + + private final MemberEvaluationService memberEvaluationService; + + @ApiOperation(value = "分页获取会员评论列表") + @GetMapping + public ResultMessage> getByPage(StoreEvaluationQueryParams storeEvaluationQueryParams) { + + return ResultUtil.data(memberEvaluationService.queryByParams(storeEvaluationQueryParams)); + } + + @ApiOperation(value = "通过id获取") + @ApiImplicitParam(name = "id", value = "评价ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(memberEvaluationService.queryById(id)); + } + + @ApiOperation(value = "回复评价") + @ApiImplicitParams({ + @ApiImplicitParam(name = "id", value = "评价ID", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "reply", value = "回复内容", required = true, dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "replyImage", value = "回复图片", dataType = "String", paramType = "query") + }) + @PutMapping(value = "/reply/{id}") + public ResultMessage reply(@PathVariable String id, @RequestParam String reply, @RequestParam String replyImage) { + if (memberEvaluationService.reply(id, reply, replyImage)) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/trade/OrderComplaintStoreController.java b/seller-api/src/main/java/cn/lili/controller/trade/OrderComplaintStoreController.java new file mode 100644 index 00000000..9f484bb7 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/trade/OrderComplaintStoreController.java @@ -0,0 +1,104 @@ +package cn.lili.controller.trade; + +import cn.lili.common.enums.ResultCode; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.OrderComplaint; +import cn.lili.modules.order.order.entity.enums.CommunicationOwnerEnum; +import cn.lili.modules.order.order.entity.vo.*; +import cn.lili.modules.order.order.service.OrderComplaintCommunicationService; +import cn.lili.modules.order.order.service.OrderComplaintService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 店铺端,交易投诉接口 + * + * @author paulG + * @date 2020/12/5 + **/ +@RestController +@Api(tags = "店铺端,交易投诉接口") +@RequestMapping("/store/complain") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderComplaintStoreController { + + /** + * 交易投诉 + */ + private final OrderComplaintService orderComplaintService; + + /** + * 投诉沟通 + */ + private final OrderComplaintCommunicationService orderComplaintCommunicationService; + + @ApiOperation(value = "通过id获取") + @ApiImplicitParam(name = "id", value = "投诉单ID", required = true, paramType = "path") + @GetMapping(value = "/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(orderComplaintService.getOrderComplainById(id)); + } + + @ApiOperation(value = "分页获取") + @GetMapping + public ResultMessage> get(OrderComplaintSearchParams searchParams, PageVO pageVO) { + searchParams.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(orderComplaintService.getOrderComplainByPage(searchParams, pageVO)); + } + + @ApiOperation(value = "添加交易投诉对话") + @ApiImplicitParams({ + @ApiImplicitParam(name = "complainId", value = "投诉单ID", required = true, paramType = "query"), + @ApiImplicitParam(name = "content", value = "内容", required = true, paramType = "query") + }) + @PostMapping("/communication") + public ResultMessage addCommunication(@RequestParam String complainId, @RequestParam String content) { + AuthUser currentUser = UserContext.getCurrentUser(); + OrderComplaintCommunicationVO communicationVO = new OrderComplaintCommunicationVO(complainId, content, CommunicationOwnerEnum.STORE.name(), currentUser.getStoreId(), currentUser.getUsername()); + if (orderComplaintCommunicationService.addCommunication(communicationVO)) { + return ResultUtil.data(communicationVO); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改申诉信息") + @PutMapping + public ResultMessage update(OrderComplaintVO orderComplainVO) { + orderComplainVO.setStoreId(UserContext.getCurrentUser().getId()); + if (orderComplaintService.updateOrderComplain(orderComplainVO)) { + return ResultUtil.data(orderComplainVO); + } + return ResultUtil.error(ResultCode.ERROR); + + } + + @ApiOperation(value = "申诉") + @PutMapping("/appeal") + public ResultMessage appeal(StoreAppealVO storeAppealVO) { + //申诉接口 + if (orderComplaintService.appeal(storeAppealVO)) { + return ResultUtil.data(orderComplaintService.getOrderComplainById(storeAppealVO.getOrderComplaintId())); + } + return ResultUtil.error(ResultCode.ERROR); + } + + @ApiOperation(value = "修改状态") + @PutMapping(value = "/status") + public ResultMessage updateStatus(OrderComplaintOperationParams orderComplainVO) { + if (orderComplaintService.updateOrderComplainByStatus(orderComplainVO) != null) { + return ResultUtil.success(ResultCode.SUCCESS); + } + return ResultUtil.error(ResultCode.ERROR); + } + +} diff --git a/seller-api/src/main/java/cn/lili/controller/trade/OrderLogStoreController.java b/seller-api/src/main/java/cn/lili/controller/trade/OrderLogStoreController.java new file mode 100644 index 00000000..4b4c9852 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/trade/OrderLogStoreController.java @@ -0,0 +1,39 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.trade.entity.dos.OrderLog; +import cn.lili.modules.order.trade.service.OrderLogService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 店铺端,订单日志接口 + * + * @author Chopper + * @date 2020/12/5 + **/ +@RestController +@Api(tags = "店铺端,订单日志接口") +@RequestMapping("/store/orderLog") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderLogStoreController { + + private final OrderLogService orderLogService; + + @ApiOperation(value = "通过订单编号获取订单日志") + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, paramType = "path") + @GetMapping(value = "/{orderSn}") + public ResultMessage> get(@PathVariable String orderSn) { + return ResultUtil.data(orderLogService.getOrderLog(orderSn)); + } +} diff --git a/seller-api/src/main/java/cn/lili/controller/trade/OrderStoreController.java b/seller-api/src/main/java/cn/lili/controller/trade/OrderStoreController.java new file mode 100644 index 00000000..83f03d06 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/trade/OrderStoreController.java @@ -0,0 +1,124 @@ +package cn.lili.controller.trade; + +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.member.entity.dto.MemberAddressDTO; +import cn.lili.modules.order.order.entity.dto.OrderSearchParams; +import cn.lili.modules.order.order.entity.vo.OrderDetailVO; +import cn.lili.modules.order.order.entity.vo.OrderSimpleVO; +import cn.lili.modules.order.order.service.OrderPriceService; +import cn.lili.modules.order.order.service.OrderService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 店铺端,订单接口 + * + * @author Chopper + * @date 2020/11/17 4:35 下午 + **/ +@RestController +@RequestMapping("/store/orders") +@Api(tags = "店铺端,订单接口") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class OrderStoreController { + + /** + * 订单 + */ + private final OrderService orderService; + + /** + * 订单价格 + */ + private final OrderPriceService orderPriceService; + + @ApiOperation(value = "查询订单列表") + @GetMapping + public ResultMessage> queryMineOrder(OrderSearchParams orderSearchParams) { + return ResultUtil.data(orderService.queryByParams(orderSearchParams)); + } + + + @ApiOperation(value = "订单明细") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") + }) + @GetMapping(value = "/{orderSn}") + public ResultMessage detail(@NotNull @PathVariable String orderSn) { + + return ResultUtil.data(orderService.queryDetail(orderSn)); + } + + @ApiOperation(value = "修改收货人信息") + @ApiImplicitParam(name = "orderSn", value = "订单sn", required = true, dataType = "String", paramType = "path") + @PostMapping(value = "/update/{orderSn}/consignee") + public ResultMessage consignee(@NotNull(message = "参数非法") @PathVariable String orderSn, + @Valid MemberAddressDTO memberAddressDTO) { + return ResultUtil.data(orderService.updateConsignee(orderSn, memberAddressDTO)); + } + + @ApiOperation(value = "修改订单价格") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单sn", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "orderPrice", value = "订单价格", required = true, dataType = "Double", paramType = "query"), + }) + @PutMapping(value = "/update/{orderSn}/price") + public ResultMessage updateOrderPrice(@PathVariable String orderSn, + @NotNull(message = "订单价格不能为空") @RequestParam Double orderPrice) { + return ResultUtil.data(orderPriceService.updatePrice(orderSn, orderPrice)); + } + + @ApiOperation(value = "订单发货") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单sn", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "logisticsNo", value = "发货单号", required = true, dataType = "String", paramType = "query"), + @ApiImplicitParam(name = "logisticsId", value = "物流公司", required = true, dataType = "String", paramType = "query") + }) + @PostMapping(value = "/{orderSn}/delivery") + public ResultMessage delivery(@NotNull(message = "参数非法") @PathVariable String orderSn, + @NotNull(message = "发货单号不能为空") String logisticsNo, + @NotNull(message = "请选择物流公司") String logisticsId) { + return ResultUtil.data(orderService.delivery(orderSn, logisticsNo, logisticsId)); + } + + @ApiOperation(value = "取消订单") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "reason", value = "取消原因", required = true, dataType = "String", paramType = "query") + }) + @PostMapping(value = "/{orderSn}/cancel") + public ResultMessage cancel(@PathVariable String orderSn, @RequestParam String reason) { + return ResultUtil.data(orderService.cancel(orderSn, reason)); + } + + @ApiOperation(value = "订单核验") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单sn", required = true, dataType = "String", paramType = "path"), + @ApiImplicitParam(name = "qrCode", value = "发货单号", required = true, dataType = "String", paramType = "query") + }) + @PostMapping(value = "/{orderSn}/take") + public ResultMessage take(@NotNull(message = "参数非法") @PathVariable String orderSn, + @NotNull(message = "核验码") String qrCode) { + return ResultUtil.data(orderService.take(orderSn, qrCode)); + } + + @ApiOperation(value = "查询物流踪迹") + @ApiImplicitParams({ + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") + }) + @PostMapping(value = "/getTraces/{orderSn}") + public ResultMessage getTraces(@NotBlank(message = "订单编号不能为空") @PathVariable String orderSn) { + return ResultUtil.data(orderService.getTraces(orderSn)); + } +} \ No newline at end of file diff --git a/seller-api/src/main/java/cn/lili/controller/trade/ReceiptStoreController.java b/seller-api/src/main/java/cn/lili/controller/trade/ReceiptStoreController.java new file mode 100644 index 00000000..2c24ba61 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/controller/trade/ReceiptStoreController.java @@ -0,0 +1,61 @@ +package cn.lili.controller.trade; + +import cn.lili.common.security.context.UserContext; +import cn.lili.common.utils.ResultUtil; +import cn.lili.common.vo.PageVO; +import cn.lili.common.vo.ResultMessage; +import cn.lili.modules.order.order.entity.dos.Receipt; +import cn.lili.modules.order.order.entity.dto.OrderReceiptDTO; +import cn.lili.modules.order.order.entity.dto.ReceiptSearchParams; +import cn.lili.modules.order.order.service.ReceiptService; +import com.baomidou.mybatisplus.core.metadata.IPage; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * 店铺端,发票接口 + * + * @author Bulbasaur + * @date 2020/11/28 14:09 + **/ +@RestController +@Api(tags = "店铺端,发票接口") +@RequestMapping("/store/receipt") +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ReceiptStoreController { + + private final ReceiptService receiptService; + + @ApiOperation(value = "分页获取") + @GetMapping + public ResultMessage> getByPage(PageVO page, ReceiptSearchParams receiptSearchParams) { + receiptSearchParams.setStoreId(UserContext.getCurrentUser().getStoreId()); + return ResultUtil.data(receiptService.getReceiptData(receiptSearchParams, page)); + } + + @ApiOperation(value = "通过id获取") + @ApiImplicitParam(name = "id", value = "发票ID", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/get/{id}") + public ResultMessage get(@PathVariable String id) { + return ResultUtil.data(receiptService.getById(id)); + } + + @ApiOperation(value = "开发票") + @ApiImplicitParam(name = "id", value = "发票ID", required = true, dataType = "String", paramType = "path") + @PostMapping(value = "/{id}/invoicing") + public ResultMessage invoicing(@PathVariable String id) { + return ResultUtil.data(receiptService.invoicing(id)); + } + + @ApiOperation(value = "通过订单编号获取") + @ApiImplicitParam(name = "orderSn", value = "订单编号", required = true, dataType = "String", paramType = "path") + @GetMapping(value = "/get/orderSn/{orderSn}") + public ResultMessage getByOrderSn(@PathVariable String orderSn) { + return ResultUtil.data(receiptService.getByOrderSn(orderSn)); + } + +} diff --git a/seller-api/src/main/java/cn/lili/security/StoreAuthenticationFilter.java b/seller-api/src/main/java/cn/lili/security/StoreAuthenticationFilter.java new file mode 100755 index 00000000..bb4d317b --- /dev/null +++ b/seller-api/src/main/java/cn/lili/security/StoreAuthenticationFilter.java @@ -0,0 +1,104 @@ +package cn.lili.security; + +import cn.hutool.core.util.StrUtil; +import cn.lili.common.cache.Cache; +import cn.lili.common.cache.CachePrefix; +import cn.lili.common.security.AuthUser; +import cn.lili.common.security.enums.SecurityEnum; +import cn.lili.common.security.enums.UserEnums; +import cn.lili.common.token.SecretKeyUtil; +import cn.lili.common.utils.ResponseUtil; +import com.google.gson.Gson; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Chopper + */ +@Slf4j +public class StoreAuthenticationFilter extends BasicAuthenticationFilter { + + private final Cache cache; + + public StoreAuthenticationFilter(AuthenticationManager authenticationManager, + Cache cache) { + super(authenticationManager); + this.cache = cache; + } + + @SneakyThrows + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + + String accessToken = request.getHeader(SecurityEnum.HEADER_TOKEN.getValue()); + if (StrUtil.isBlank(accessToken)) { + chain.doFilter(request, response); + return; + } + try { + UsernamePasswordAuthenticationToken authentication = getAuthentication(accessToken, response); + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (Exception e) { + log.error(e.getMessage()); + } + + chain.doFilter(request, response); + } + + + /** + * 获取token信息 + * + * @param jwt + * @param response + * @return + */ + private UsernamePasswordAuthenticationToken getAuthentication(String jwt, HttpServletResponse response) { + + try { + Claims claims + = Jwts.parser() + .setSigningKey(SecretKeyUtil.generalKeyByDecoders()) + .parseClaimsJws(jwt).getBody(); + //获取存储在claims中的用户信息 + String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString(); + AuthUser authUser = new Gson().fromJson(json, AuthUser.class); + + // 校验redis中是否有权限 + if (cache.hasKey(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.STORE) + jwt)) { + //用户角色 + List auths = new ArrayList<>(); + auths.add(new SimpleGrantedAuthority("ROLE_" + authUser.getRole().name())); + //构造返回信息 + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authUser.getUsername(), null, auths); + authentication.setDetails(authUser); + return authentication; + } + ResponseUtil.output(response, 403, ResponseUtil.resultMap(false, 403, "登录已失效,请重新登录")); + return null; + } catch (ExpiredJwtException e) { + log.debug("user analysis exception:", e); + } catch (Exception e) { + log.error("user analysis exception:", e); + } + return null; + } +} + diff --git a/seller-api/src/main/java/cn/lili/security/StoreSecurityConfig.java b/seller-api/src/main/java/cn/lili/security/StoreSecurityConfig.java new file mode 100644 index 00000000..33b57956 --- /dev/null +++ b/seller-api/src/main/java/cn/lili/security/StoreSecurityConfig.java @@ -0,0 +1,81 @@ +package cn.lili.security; + +import cn.lili.common.cache.Cache; +import cn.lili.common.security.CustomAccessDeniedHandler; +import cn.lili.common.utils.SpringContextUtil; +import cn.lili.config.properties.IgnoredUrlsProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.web.cors.CorsConfigurationSource; + +/** + * spring Security 核心配置类 Store安全配置中心 + * + * @author Chopper + * @date 2020/11/14 16:20 + */ +@Slf4j +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class StoreSecurityConfig extends WebSecurityConfigurerAdapter { + + + /** + * 忽略验权配置 + */ + private final IgnoredUrlsProperties ignoredUrlsProperties; + + /** + * spring security -》 权限不足处理 + */ + private final CustomAccessDeniedHandler accessDeniedHandler; + + + private final Cache cache; + + + @Override + protected void configure(HttpSecurity http) throws Exception { + + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = http + .authorizeRequests(); + // 配置的url 不需要授权 + for (String url : ignoredUrlsProperties.getUrls()) { + registry.antMatchers(url).permitAll(); + } + registry.and() + // 禁止网页iframe + .headers().frameOptions().disable() + .and() + .logout() + .permitAll() + .and() + .authorizeRequests() + // 任何请求 + .anyRequest() + // 需要身份认证 + .authenticated() + .and() + // 允许跨域 + .cors().configurationSource((CorsConfigurationSource) SpringContextUtil.getBean("corsConfigurationSource")).and() + // 关闭跨站请求防护 + .csrf().disable() + // 前后端分离采用JWT 不需要session + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + // 自定义权限拒绝处理类 + .exceptionHandling().accessDeniedHandler(accessDeniedHandler) + .and() + // 添加JWT认证过滤器 + .addFilter(new StoreAuthenticationFilter(authenticationManager(), cache)); + } + +} diff --git a/seller-api/src/main/resources/application.yml b/seller-api/src/main/resources/application.yml new file mode 100644 index 00000000..b6854c83 --- /dev/null +++ b/seller-api/src/main/resources/application.yml @@ -0,0 +1,301 @@ +server: + port: 8889 + + servlet: + context-path: / + + # 正式部署时候,解开此处配置,防止文件夹被清除导致的文件上传失败问题 + # multipart: + # location: /Users/lifenlong/Desktop/ceshi + tomcat: + uri-encoding: UTF-8 + threads: + min-spare: 50 + max: 1000 + +# 与Spring Boot 2一样,默认情况下,大多数端点都不通过http公开,我们公开了所有端点。对于生产,您应该仔细选择要公开的端点。 +management: + # health: + # elasticsearch: + # enabled: false + # datasource: + # enabled: false + endpoints: + web: + exposure: + include: '*' +spring: + # 要在其中注册的Spring Boot Admin Server的URL。 + boot: + admin: + client: + url: http://127.0.0.1:8000 + # mongodb + data: + mongodb: + host: 127.0.0.1 + port: 27017 + database: lilishop + username: root + password: lilishop + authentication-database: admin + # replica-set-name: mongoreplset + cache: + type: redis + + jpa: + # 自动生成表结构 + generate-ddl: true + open-in-view: false + # Redis + redis: + host: 127.0.0.1 + port: 6379 + password: lilishop + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: 20 + # 连接池中的最大空闲连接 默认 8 + max-idle: 10 + # 连接池中的最小空闲连接 默认 8 + min-idle: 8 + # 文件大小上传配置 + servlet: + multipart: + max-file-size: 20MB + max-request-size: 20MB + jackson: + time-zone: GMT+8 + serialization: + #关闭jackson 对json做解析 + fail-on-empty-beans: false + + shardingsphere: + datasource: + # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 + names: default-datasource + default-datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: lilishop + maxActive: 20 + initialSize: 5 + maxWait: 60000 + minIdle: 5 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 + poolPreparedStatements: false + #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 + maxOpenPreparedStatements: -1 + #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,log4j2 + #通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + #合并多个DruidDataSource的监控数据 + useGlobalDataSourceStat: true + loginUsername: druid + loginPassword: druid + # sharding: + # default-data-source-name: default-datasource + # #需要拆分的表,可以设置多个 在 li_order 级别即可 + # tables: + # #需要进行分表的逻辑表名 + # li_order: + # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 + # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} + # table-strategy: + # # 分表策略,根据创建日期 + # standard: + # sharding-column: create_time + # #分表策略 + # precise-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + # #范围查询实现 + # range-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + props: + #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 + sql: + show: true + +# 忽略鉴权url +ignored: + urls: + - /editor-app/** + - /actuator** + - /actuator/** + - /MP_verify_qSyvBPhDsPdxvOhC.txt + - /weixin/** + - /source/** + - /buyer/mini-program/** + - /buyer/cashier/** + - /buyer/pageData/** + - /buyer/article/** + - /buyer/goods/** + - /buyer/category/** + - /buyer/shop/** + - /buyer/connect/** + - /buyer/members/smsLogin + - /buyer/members/refresh/* + - /buyer/members/refresh** + - /buyer/promotion/pintuan + - /buyer/promotion/seckill + - /buyer/memberEvaluation/**/goodsEvaluation + - /buyer/memberEvaluation/**/evaluationNumber + - /store/login/** + - /manager/user/login + - /manager/user/refresh/** + - /druid/** + - /swagger-ui.html + - /doc.html + - /swagger-resources/** + - /swagger/** + - /**/**.js + - /**/**.png + - /**/**.css + - /webjars/** + - /v2/api-docs + - /configuration/ui + - /boot-admin + statics: + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + +# Swagger界面内容配置 +swagger: + title: lili API接口文档 + description: lili Api Documentation + version: 1.0.0 + termsOfServiceUrl: https://pickmall.cn + contact: + name: lili + url: https://pickmall.cn + email: admin@pickmall.com + +# Mybatis-plus +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + #缓存开启 + cache-enabled: true + #日志 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 日志 +logging: + # 输出级别 + level: + cn.lili: info + # org.hibernate: debug + # org.springframework: debug + # org.springframework.data.mongodb.core: debug + file: + # 指定路径 + path: lili-logs + # 最大保存天数 + max-history: 7 + # 每个文件最大大小 + max-size: 5MB +#加密参数 +jasypt: + encryptor: + password: lili + +lili: + system: + isDemoSite: true + statistics: + # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 + onlineMember: 48 + # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 + currentOnlineUpdate: 600 + #qq lbs 申请 + lbs: + key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT + sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 + #域名 + domain: + pc: https://pc.b2b2c.pickmall.cn + wap: https://m.b2b2c.pickmall.cn + store: https://store.b2b2c.pickmall.cn + admin: https://admin.b2b2c.pickmall.cn + #api地址 + api: + buyer: https://buyer-api.pickmall.cn + common: https://common-api.pickmall.cn + manager: https://admin-api.pickmall.cn + store: https://store-api.pickmall.cn + + # jwt 细节设定 + jwt-setting: + # token过期时间(分钟) + tokenExpireTime: 60 + + # 使用Spring @Cacheable注解失效时间 + cache: + # 过期时间 单位秒 永久不过期设为-1 + timeout: 1500 + #多线程配置 + thread: + corePoolSize: 5 + maxPoolSize: 50 + queueCapacity: 50 + data: + elasticsearch: + cluster-name: elasticsearch + cluster-nodes: 127.0.0.1:9200 + index: + number-of-replicas: 0 + number-of-shards: 3 + index-prefix: lili + schema: http + # account: + # username: elastic + # password: LiLiShopES + + rocketmq: + promotion-topic: lili_promotion_topic + promotion-group: lili_promotion_group + msg-ext-topic: lili_msg_topic + msg-ext-group: lili_msg_group + goods-topic: lili_goods_topic + goods-group: lili_goods_group + order-topic: lili_order_topic + order-group: lili_order_group + member-topic: lili_member_topic + member-group: lili_member_group + other-topic: lili_other_topic + other-group: lili_other_group + notice-topic: lili_notice_topic + notice-group: lili_notice_group + notice-send-topic: lili_send_notice_topic + notice-send-group: lili_send_notice_group + after-sale-topic: lili_after_sale_topic + after-sale-group: lili_after_sale_group +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: lili_group + send-message-timeout: 30000 + +xxl: + job: + admin: + addresses: http://127.0.0.1:9001/xxl-job-admin + executor: + appname: xxl-job-executor-lilishop + address: + ip: + port: 8891 + logpath: ./xxl-job/executor + logretentiondays: 7 \ No newline at end of file diff --git a/socket-api/pom.xml b/socket-api/pom.xml new file mode 100644 index 00000000..f798e24c --- /dev/null +++ b/socket-api/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + socket-api + + cn.lili + lili-shop-parent + 1.0.1 + + + + + cn.lili + framework + 1.0.1 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/socket-api/src/main/java/cn/lili/SocketApiApplication.java b/socket-api/src/main/java/cn/lili/SocketApiApplication.java new file mode 100644 index 00000000..947991d7 --- /dev/null +++ b/socket-api/src/main/java/cn/lili/SocketApiApplication.java @@ -0,0 +1,32 @@ +package cn.lili; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +/** + * @author Chopper + */ +@SpringBootApplication +public class SocketApiApplication extends WebMvcConfigurerAdapter { + + + public static void main(String[] args) { + + System.setProperty("es.set.netty.runtime.available.processors", "false"); + SpringApplication.run(SocketApiApplication.class, args); + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + + registry.addMapping("/**") + .allowCredentials(true) + .allowedHeaders("*") //允许任何头 + .allowedOrigins("*") //允许任何域名 + .allowedMethods("*"); //允许任何方法 + } + + +} diff --git a/socket-api/src/main/java/cn/lili/scocket/listener/MessageSendListener.java b/socket-api/src/main/java/cn/lili/scocket/listener/MessageSendListener.java new file mode 100644 index 00000000..a972848c --- /dev/null +++ b/socket-api/src/main/java/cn/lili/scocket/listener/MessageSendListener.java @@ -0,0 +1,68 @@ +package cn.lili.scocket.listener; + +import cn.hutool.json.JSONUtil; +import cn.lili.common.rocketmq.tags.OtherTagsEnum; +import cn.lili.common.utils.BeanUtil; +import cn.lili.modules.message.entity.dos.Message; +import cn.lili.modules.message.entity.enums.MessageShowType; +import cn.lili.modules.message.entity.vos.MessageShowVO; +import cn.lili.modules.message.mapper.StoreMessageMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.CrossOrigin; + +/** + * @author paulG + * @since 2020/12/9 + **/ +@Component +@CrossOrigin +@Slf4j +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@RocketMQMessageListener(topic = "${lili.data.rocketmq.other-topic}", consumerGroup = "${lili.data.rocketmq.other-group}") +public class MessageSendListener implements RocketMQListener { + + private final StoreMessageMapper storeMessageMapper; + @Autowired + private SimpMessagingTemplate simpMessagingTemplate; + + @Override + public void onMessage(MessageExt messageExt) { + log.info(messageExt.getTags()); + switch (OtherTagsEnum.valueOf(messageExt.getTags())) { + //站内消息提醒 + case MESSAGE: + System.out.println("消息提醒"); + sendNoticeMessage(messageExt); + break; + default: + break; + } + } + + /** + * 给商家发送站内信息 + * + * @param messageExt + */ + private void sendNoticeMessage(MessageExt messageExt) { + MessageShowVO messageVO = new MessageShowVO(); + Message message = JSONUtil.toBean(new String(messageExt.getBody()), Message.class); + //构建vo + BeanUtil.copyProperties(message, messageVO); + messageVO.setType(MessageShowType.NOTICE.name()); + if (message.getMessageRange().equals("ALL")) { + simpMessagingTemplate.convertAndSend("/topic/subscribe", messageVO); + } else { + for (String id : message.getUserIds()) { + simpMessagingTemplate.convertAndSendToUser("SHOP_" + id, "/queue/subscribe", messageVO); + } + } + } +} diff --git a/socket-api/src/main/java/cn/lili/scocket/listener/ScoketSecurityConfig.java b/socket-api/src/main/java/cn/lili/scocket/listener/ScoketSecurityConfig.java new file mode 100644 index 00000000..d317190a --- /dev/null +++ b/socket-api/src/main/java/cn/lili/scocket/listener/ScoketSecurityConfig.java @@ -0,0 +1,35 @@ +package cn.lili.scocket.listener; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; + +/** + * spring Security 核心配置类 Store安全配置中心 + * + * @author Chopper + * @version v4.0 + * @Description: + * @since 2020/11/14 16:20 + */ +@Slf4j +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ScoketSecurityConfig extends WebSecurityConfigurerAdapter { + + + @Override + protected void configure(HttpSecurity http) throws Exception { + + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = http + .authorizeRequests(); + registry.antMatchers("**").permitAll(); + } + +} diff --git a/socket-api/src/main/resources/application.yml b/socket-api/src/main/resources/application.yml new file mode 100644 index 00000000..c10a84e8 --- /dev/null +++ b/socket-api/src/main/resources/application.yml @@ -0,0 +1,298 @@ +server: + port: 8885 + servlet: + context-path: / + + # 正式部署时候,解开此处配置,防止文件夹被清除导致的文件上传失败问题 + # multipart: + # location: /Users/lifenlong/Desktop/ceshi + tomcat: + uri-encoding: UTF-8 + threads: + min-spare: 50 + max: 1000 + +# 与Spring Boot 2一样,默认情况下,大多数端点都不通过http公开,我们公开了所有端点。对于生产,您应该仔细选择要公开的端点。 +management: + # health: + # elasticsearch: + # enabled: false + # datasource: + # enabled: false + endpoints: + web: + exposure: + include: '*' +spring: + # 要在其中注册的Spring Boot Admin Server的URL。 + boot: + admin: + client: + url: http://127.0.0.1:8000 + # mongodb + data: + mongodb: + host: 127.0.0.1 + port: 27017 + database: lilishop + username: root + password: lilishop + authentication-database: admin + # replica-set-name: mongoreplset + cache: + type: redis + + jpa: + # 自动生成表结构 + generate-ddl: true + open-in-view: false + # Redis + redis: + host: 127.0.0.1 + port: 6379 + password: lilishop + lettuce: + pool: + # 连接池最大连接数(使用负值表示没有限制) 默认 8 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 + max-wait: 20 + # 连接池中的最大空闲连接 默认 8 + max-idle: 10 + # 连接池中的最小空闲连接 默认 8 + min-idle: 8 + # 文件大小上传配置 + servlet: + multipart: + max-file-size: 20MB + max-request-size: 20MB + jackson: + time-zone: GMT+8 + serialization: + #关闭jackson 对json做解析 + fail-on-empty-beans: false + + shardingsphere: + datasource: + # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 + names: default-datasource + default-datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai + username: root + password: lilishop + maxActive: 20 + initialSize: 5 + maxWait: 60000 + minIdle: 5 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: SELECT 1 FROM DUAL + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 + poolPreparedStatements: false + #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 + maxOpenPreparedStatements: -1 + #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 + filters: stat,wall,log4j2 + #通过connectProperties属性来打开mergeSql功能;慢SQL记录 + connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 + #合并多个DruidDataSource的监控数据 + useGlobalDataSourceStat: true + loginUsername: druid + loginPassword: druid + # sharding: + # default-data-source-name: default-datasource + # #需要拆分的表,可以设置多个 在 li_order 级别即可 + # tables: + # #需要进行分表的逻辑表名 + # li_order: + # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 + # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} + # table-strategy: + # # 分表策略,根据创建日期 + # standard: + # sharding-column: create_time + # #分表策略 + # precise-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + # #范围查询实现 + # range-algorithm-class-name: cn.lili.config.sharding.CreateTimeShardingTableAlgorithm + props: + #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 + sql: + show: true + +# 忽略鉴权url +ignored: + urls: + - /editor-app/** + - /actuator** + - /actuator/** + - /MP_verify_qSyvBPhDsPdxvOhC.txt + - /weixin/** + - /source/** + - /buyer/mini-program/** + - /buyer/cashier/** + - /buyer/pageData/** + - /buyer/article/** + - /buyer/goods/** + - /buyer/category/** + - /buyer/shop/** + - /buyer/connect/** + - /buyer/members/smsLogin + - /buyer/members/refresh/* + - /buyer/members/refresh** + - /buyer/promotion/pintuan + - /buyer/promotion/seckill + - /buyer/memberEvaluation/**/goodsEvaluation + - /buyer/memberEvaluation/**/evaluationNumber + - /store/login/** + - /manager/user/login + - /manager/user/refresh/** + - /druid/** + - /swagger-ui.html + - /doc.html + - /swagger-resources/** + - /swagger/** + - /**/**.js + - /**/**.png + - /**/**.css + - /webjars/** + - /v2/api-docs + - /configuration/ui + - /boot-admin + statics: + - /**/*.js + - /**/*.css + - /**/*.png + - /**/*.ico + +# Swagger界面内容配置 +swagger: + title: lili API接口文档 + description: lili Api Documentation + version: 1.0.0 + termsOfServiceUrl: https://pickmall.cn + contact: + name: lili + url: https://pickmall.cn + email: admin@pickmall.com + +# Mybatis-plus +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + #缓存开启 + cache-enabled: true + #日志 +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 日志 +logging: + # 输出级别 + level: + cn.lili: info + # org.hibernate: debug + # org.springframework: debug + # org.springframework.data.mongodb.core: debug + file: + # 指定路径 + path: lili-logs + # 最大保存天数 + max-history: 7 + # 每个文件最大大小 + max-size: 5MB +#加密参数 +jasypt: + encryptor: + password: lili + +lili: + system: + isDemoSite: true + statistics: + # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 + onlineMember: 48 + # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 + currentOnlineUpdate: 600 + #qq lbs 申请 + lbs: + key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT + sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 + #域名 + domain: + pc: http://127.0.0.1:8888 + wap: http://127.0.0.1:8888 + seller: http://127.0.0.1:8888 + admin: http://127.0.0.1:8888 + #api地址 + api: + buyer: https://z171l91606.51mypc.cn/ + base: http://127.0.0.1:8888 + manager: http://127.0.0.1:8888 + seller: http://127.0.0.1:8888 + + # jwt 细节设定 + jwt-setting: + # token过期时间(分钟) + tokenExpireTime: 60 + + # 使用Spring @Cacheable注解失效时间 + cache: + # 过期时间 单位秒 永久不过期设为-1 + timeout: 1500 + #多线程配置 + thread: + corePoolSize: 5 + maxPoolSize: 50 + queueCapacity: 50 + data: + elasticsearch: + cluster-name: elasticsearch + cluster-nodes: 127.0.0.1:9200 + index: + number-of-replicas: 0 + number-of-shards: 3 + index-prefix: lili + schema: http + # account: + # username: elastic + # password: LiLiShopES + + rocketmq: + promotion-topic: lili_promotion_topic + promotion-group: lili_promotion_group + msg-ext-topic: lili_msg_topic + msg-ext-group: lili_msg_group + goods-topic: lili_goods_topic + goods-group: lili_goods_group + order-topic: lili_order_topic + order-group: lili_order_group + member-topic: lili_member_topic + member-group: lili_member_group + other-topic: lili_other_topic + other-group: lili_other_group + notice-topic: lili_notice_topic + notice-group: lili_notice_group + notice-send-topic: lili_send_notice_topic + notice-send-group: lili_send_notice_group +rocketmq: + name-server: 127.0.0.1:9876 + producer: + group: lili_group + send-message-timeout: 30000 + +xxl: + job: + admin: + addresses: http://127.0.0.1:9001/xxl-job-admin + executor: + appname: xxl-job-executor-lilishop + address: + ip: + port: 8891 + logpath: ./xxl-job/executor + logretentiondays: 7 \ No newline at end of file diff --git a/xxl-job/Dockerfile b/xxl-job/Dockerfile new file mode 100644 index 00000000..ebf227bf --- /dev/null +++ b/xxl-job/Dockerfile @@ -0,0 +1,12 @@ +FROM java:8 +MAINTAINER Chopper +ADD xxl-job-admin-2.3.0-SNAPSHOT.jar /xxl-job.jar +ADD application.properties /application.properties +EXPOSE 9001 +RUN "java -jar /xxl-job.jar --spring.config.location=/application.properties" +#"Args": [ +# "-c", +# "java -jar /app.jar $0 $@", +# "--spring.config.location=application.properties" +#], +#ENTRYPOINT ["java","-jar","xxl-job.jar"] \ No newline at end of file diff --git a/xxl-job/README.md b/xxl-job/README.md new file mode 100644 index 00000000..983b99e0 --- /dev/null +++ b/xxl-job/README.md @@ -0,0 +1,3 @@ +**默认登录账号 admin 密码 111111** + +###### **启动命令 java -jar xxl-job-admin-2.3.0-SNAPSHOT.jar --spring.config.location=application.properties** \ No newline at end of file diff --git a/xxl-job/XXLJOB-README.md b/xxl-job/XXLJOB-README.md new file mode 100644 index 00000000..49d97c48 --- /dev/null +++ b/xxl-job/XXLJOB-README.md @@ -0,0 +1,557 @@ +

+ +

XXL-JOB

+

+ XXL-JOB, a distributed task scheduling framework. +
+ -- Home Page -- +
+
+ + + + + + + + + + + + + + + + + + + + + +

+

+ + +## Introduction +XXL-JOB is a distributed task scheduling framework. +It's core design goal is to develop quickly and learn simple, lightweight, and easy to expand. +Now, it's already open source, and many companies use it in production environments, real "out-of-the-box". + +XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。 + + +## Documentation +- [中文文档](https://www.xuxueli.com/xxl-job/) +- [English Documentation](https://www.xuxueli.com/xxl-job/en/) + + +## Communication +- [社区交流](https://www.xuxueli.com/page/community.html) + + +## Features +- 1、简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手; +- 2、动态:支持动态修改任务状态、启动/停止任务,以及终止运行中任务,即时生效; +- 3、调度中心HA(中心式):调度采用中心式设计,“调度中心”自研调度组件并支持集群部署,可保证调度中心HA; +- 4、执行器HA(分布式):任务分布式执行,任务"执行器"支持集群部署,可保证任务执行HA; +- 5、注册中心: 执行器会周期性自动注册任务, 调度中心将会自动发现注册的任务并触发执行。同时,也支持手动录入执行器地址; +- 6、弹性扩容缩容:一旦有新执行器机器上线或者下线,下次调度时将会重新分配任务; +- 7、触发策略:提供丰富的任务触发策略,包括:Cron触发、固定间隔触发、固定延时触发、API(事件)触发、人工触发、父子任务触发; +- 8、调度过期策略:调度中心错过调度时间的补偿处理策略,包括:忽略、立即补偿触发一次等; +- 9、阻塞处理策略:调度过于密集执行器来不及处理时的处理策略,策略包括:单机串行(默认)、丢弃后续调度、覆盖之前调度; +- 10、任务超时控制:支持自定义任务超时时间,任务运行超时将会主动中断任务; +- 11、任务失败重试:支持自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;其中分片任务支持分片粒度的失败重试; +- 12、任务失败告警;默认提供邮件方式失败告警,同时预留扩展接口,可方便的扩展短信、钉钉等告警方式; +- 13、路由策略:执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等; +- 14、分片广播任务:执行器集群部署时,任务路由策略选择"分片广播"情况下,一次任务调度将会广播触发集群中所有执行器执行一次任务,可根据分片参数开发分片任务; +- 15、动态分片:分片广播任务以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。 +- 16、故障转移:任务路由策略选择"故障转移"情况下,如果执行器集群中某一台机器故障,将会自动Failover切换到一台正常的执行器发送调度请求。 +- 17、任务进度监控:支持实时监控任务进度; +- 18、Rolling实时日志:支持在线查看调度结果,并且支持以Rolling方式实时查看执行器输出的完整的执行日志; +- 19、GLUE:提供Web IDE,支持在线开发任务逻辑代码,动态发布,实时编译生效,省略部署上线的过程。支持30个版本的历史版本回溯。 +- 20、脚本任务:支持以GLUE模式开发和运行脚本任务,包括Shell、Python、NodeJS、PHP、PowerShell等类型脚本; +- 21、命令行任务:原生提供通用命令行任务Handler(Bean任务,"CommandJobHandler");业务方只需要提供命令行即可; +- 22、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行, 多个子任务用逗号分隔; +- 23、一致性:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行; +- 24、自定义任务参数:支持在线配置调度任务入参,即时生效; +- 25、调度线程池:调度系统多线程触发调度运行,确保调度精确执行,不被堵塞; +- 26、数据加密:调度中心和执行器之间的通讯进行数据加密,提升调度信息安全性; +- 27、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件; +- 28、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用; +- 29、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等; +- 30、全异步:任务调度流程全异步化设计实现,如异步调度、异步运行、异步回调等,有效对密集调度进行流量削峰,理论上支持任意时长任务的运行; +- 31、跨语言:调度中心与执行器提供语言无关的 RESTful API 服务,第三方任意语言可据此对接调度中心或者实现执行器。除此之外,还提供了 “多任务模式”和“httpJobHandler”等其他跨语言方案; +- 32、国际化:调度中心支持国际化设置,提供中文、英文两种可选语言,默认为中文; +- 33、容器化:提供官方docker镜像,并实时更新推送dockerhub,进一步实现产品开箱即用; +- 34、线程池隔离:调度线程池进行隔离拆分,慢任务自动降级进入"Slow"线程池,避免耗尽调度线程,提高系统稳定性; +- 35、用户管理:支持在线管理系统用户,存在管理员、普通用户两种角色; +- 36、权限控制:执行器维度进行权限控制,管理员拥有全量权限,普通用户需要分配执行器权限后才允许相关操作; + + +## Development +于2015年中,我在github上创建XXL-JOB项目仓库并提交第一个commit,随之进行系统结构设计,UI选型,交互设计…… + +于2015-11月,XXL-JOB终于RELEASE了第一个大版本V1.0, 随后我将之发布到OSCHINA,XXL-JOB在OSCHINA上获得了@红薯的热门推荐,同期分别达到了OSCHINA的“热门动弹”排行第一和git.oschina的开源软件月热度排行第一,在此特别感谢红薯,感谢大家的关注和支持。 + +于2015-12月,我将XXL-JOB发表到我司内部知识库,并且得到内部同事认可。 + +于2016-01月,我司展开XXL-JOB的内部接入和定制工作,在此感谢袁某和尹某两位同事的贡献,同时也感谢内部其他给与关注与支持的同事。 + +于2017-05-13,在上海举办的 "[第62期开源中国源创会](https://www.oschina.net/event/2236961)" 的 "放码过来" 环节,我登台对XXL-JOB做了演讲,台下五百位在场观众反响热烈([图文回顾](https://www.oschina.net/question/2686220_2242120) )。 + +于2017-10-22,又拍云 Open Talk 联合 Spring Cloud 中国社区举办的 "[进击的微服务实战派上海站](https://opentalk.upyun.com/303.html)",我登台对XXL-JOB做了演讲,现场观众反响热烈并在会后与XXL-JOB用户热烈讨论交流。 + +于2017-12-11,XXL-JOB有幸参会《[InfoQ ArchSummit全球架构师峰会](http://bj2017.archsummit.com/)》,并被拍拍贷架构总监"杨波老师"在专题 "[微服务原理、基础架构和开源实践](http://bj2017.archsummit.com/training/2)" 中现场介绍。 + +于2017-12-18,XXL-JOB参与"[2017年度最受欢迎中国开源软件](http://www.oschina.net/project/top_cn_2017?sort=1)"评比,在当时已录入的约九千个国产开源项目中角逐,最终进入了前30强。 + +于2018-01-15,XXL-JOB参与"[2017码云最火开源项目](https://www.oschina.net/news/92438/2017-mayun-top-50)"评比,在当时已录入的约六千五百个码云项目中角逐,最终进去了前20强。 + +于2018-04-14,iTechPlus在上海举办的 "[2018互联网开发者大会](http://www.itdks.com/eventlist/detail/2065)",我登台对XXL-JOB做了演讲,现场观众反响热烈并在会后与XXL-JOB用户热烈讨论交流。 + +于2018-05-27,在上海举办的 "[第75期开源中国源创会](https://www.oschina.net/event/2278742)" 的 "架构" 主题专场,我登台进行“基础架构与中间件图谱”主题演讲,台下上千位在场观众反响热烈([图文回顾](https://www.oschina.net/question/3802184_2280606) )。 + +于2018-12-05,XXL-JOB参与"[2018年度最受欢迎中国开源软件](https://www.oschina.net/project/top_cn_2018?sort=1)"评比,在当时已录入的一万多个开源项目中角逐,最终排名第19名。 + +于2019-12-10,XXL-JOB参与"[2019年度最受欢迎中国开源软件](https://www.oschina.net/project/top_cn_2019)"评比,在当时已录入的一万多个开源项目中角逐,最终排名"开发框架和基础组件类"第9名。 + +于2020-11-16,XXL-JOB参与"[2020年度最受欢迎中国开源软件](https://www.oschina.net/project/top_cn_2020)"评比,在当时已录入的一万多个开源项目中角逐,最终排名"开发框架和基础组件类"第8名。 + +> 我司大众点评目前已接入XXL-JOB,内部别名《Ferrari》(Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。 +据最新统计, 自2016-01-21接入至2017-12-01期间,该系统已调度约100万次,表现优异。新接入应用推荐使用最新版本,因为经过数十个版本的更新,系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升,核心功能更加稳定高效。 + +至今,XXL-JOB已接入多家公司的线上产品线,接入场景如电商业务,O2O业务和大数据作业等,截止最新统计时间为止,XXL-JOB已接入的公司包括不限于: + + - 1、大众点评【美团点评】 + - 2、山东学而网络科技有限公司; + - 3、安徽慧通互联科技有限公司; + - 4、人人聚财金服; + - 5、上海棠棣信息科技股份有限公司 + - 6、运满满【运满满】 + - 7、米其林 (中国区)【米其林】 + - 8、妈妈联盟 + - 9、九樱天下(北京)信息技术有限公司 + - 10、万普拉斯科技有限公司【一加手机】 + - 11、上海亿保健康管理有限公司 + - 12、海尔馨厨【海尔】 + - 13、河南大红包电子商务有限公司 + - 14、成都顺点科技有限公司 + - 15、深圳市怡亚通 + - 16、深圳麦亚信科技股份有限公司 + - 17、上海博莹科技信息技术有限公司 + - 18、中国平安科技有限公司【中国平安】 + - 19、杭州知时信息科技有限公司 + - 20、博莹科技(上海)有限公司 + - 21、成都依能股份有限责任公司 + - 22、湖南高阳通联信息技术有限公司 + - 23、深圳市邦德文化发展有限公司 + - 24、福建阿思可网络教育有限公司 + - 25、优信二手车【优信】 + - 26、上海悠游堂投资发展股份有限公司【悠游堂】 + - 27、北京粉笔蓝天科技有限公司 + - 28、中秀科技(无锡)有限公司 + - 29、武汉空心科技有限公司 + - 30、北京蚂蚁风暴科技有限公司 + - 31、四川互宜达科技有限公司 + - 32、钱包行云(北京)科技有限公司 + - 33、重庆欣才集团 + - 34、咪咕互动娱乐有限公司【中国移动】 + - 35、北京诺亦腾科技有限公司 + - 36、增长引擎(北京)信息技术有限公司 + - 37、北京英贝思科技有限公司 + - 38、刚泰集团 + - 39、深圳泰久信息系统股份有限公司 + - 40、随行付支付有限公司 + - 41、广州瀚农网络科技有限公司 + - 42、享点科技有限公司 + - 43、杭州比智科技有限公司 + - 44、圳临界线网络科技有限公司 + - 45、广州知识圈网络科技有限公司 + - 46、国誉商业上海有限公司 + - 47、海尔消费金融有限公司,嗨付、够花【海尔】 + - 48、广州巴图鲁信息科技有限公司 + - 49、深圳市鹏海运电子数据交换有限公司 + - 50、深圳市亚飞电子商务有限公司 + - 51、上海趣医网络有限公司 + - 52、聚金资本 + - 53、北京父母邦网络科技有限公司 + - 54、中山元赫软件科技有限公司 + - 55、中商惠民(北京)电子商务有限公司 + - 56、凯京集团 + - 57、华夏票联(北京)科技有限公司 + - 58、拍拍贷【拍拍贷】 + - 59、北京尚德机构在线教育有限公司 + - 60、任子行股份有限公司 + - 61、北京时态电子商务有限公司 + - 62、深圳卷皮网络科技有限公司 + - 63、北京安博通科技股份有限公司 + - 64、未来无线网 + - 65、厦门瓷禧网络有限公司 + - 66、北京递蓝科软件股份有限公司 + - 67、郑州创海软件科技公司 + - 68、北京国槐信息科技有限公司 + - 69、浪潮软件集团 + - 70、多立恒(北京)信息技术有限公司 + - 71、广州极迅客信息科技有限公司 + - 72、赫基(中国)集团股份有限公司 + - 73、海投汇 + - 74、上海润益创业孵化器管理股份有限公司 + - 75、汉纳森(厦门)数据股份有限公司 + - 76、安信信托 + - 77、岚儒财富 + - 78、捷道软件 + - 79、湖北享七网络科技有限公司 + - 80、湖南创发科技责任有限公司 + - 81、深圳小安时代互联网金融服务有限公司 + - 82、湖北享七网络科技有限公司 + - 83、钱包行云(北京)科技有限公司 + - 84、360金融【360】 + - 85、易企秀 + - 86、摩贝(上海)生物科技有限公司 + - 87、广东芯智慧科技有限公司 + - 88、联想集团【联想】 + - 89、怪兽充电 + - 90、行圆汽车 + - 91、深圳店店通科技邮箱公司 + - 92、京东【京东】 + - 93、米庄理财 + - 94、咖啡易融 + - 95、梧桐诚选 + - 96、恒大地产【恒大】 + - 97、昆明龙慧 + - 98、上海涩瑶软件 + - 99、易信【网易】 + - 100、铜板街 + - 101、杭州云若网络科技有限公司 + - 102、特百惠(中国)有限公司 + - 103、常山众卡运力供应链管理有限公司 + - 104、深圳立创电子商务有限公司 + - 105、杭州智诺科技股份有限公司 + - 106、北京云漾信息科技有限公司 + - 107、深圳市多银科技有限公司 + - 108、亲宝宝 + - 109、上海博卡软件科技有限公司 + - 110、智慧树在线教育平台 + - 111、米族金融 + - 112、北京辰森世纪 + - 113、云南滇医通 + - 114、广州市分领网络科技有限责任公司 + - 115、浙江微能科技有限公司 + - 116、上海馨飞电子商务有限公司 + - 117、上海宝尊电子商务有限公司 + - 118、直客通科技技术有限公司 + - 119、科度科技有限公司 + - 120、上海数慧系统技术有限公司 + - 121、我的医药网 + - 122、多粉平台 + - 123、铁甲二手机 + - 124、上海海新得数据技术有限公司 + - 125、深圳市珍爱网信息技术有限公司【珍爱网】 + - 126、小蜜蜂 + - 127、吉荣数科技 + - 128、上海恺域信息科技有限公司 + - 129、广州荔支网络有限公司【荔枝FM】 + - 130、杭州闪宝科技有限公司 + - 131、北京互联新网科技发展有限公司 + - 132、誉道科技 + - 133、山西兆盛房地产开发有限公司 + - 134、北京蓝睿通达科技有限公司 + - 135、月亮小屋(中国)有限公司【蓝月亮】 + - 136、青岛国瑞信息技术有限公司 + - 137、博雅云计算(北京)有限公司 + - 138、华泰证券香港子公司 + - 139、杭州东方通信软件技术有限公司 + - 140、武汉博晟安全技术股份有限公司 + - 141、深圳市六度人和科技有限公司 + - 142、杭州趣维科技有限公司(小影) + - 143、宁波单车侠之家科技有限公司【单车侠】 + - 144、丁丁云康信息科技(北京)有限公司 + - 145、云钱袋 + - 146、南京中兴力维 + - 147、上海矽昌通信技术有限公司 + - 148、深圳萨科科技 + - 149、中通服创立科技有限责任公司 + - 150、深圳市对庄科技有限公司 + - 151、上证所信息网络有限公司 + - 152、杭州火烧云科技有限公司【婚礼纪】 + - 153、天津青芒果科技有限公司【芒果头条】 + - 154、长飞光纤光缆股份有限公司 + - 155、世纪凯歌(北京)医疗科技有限公司 + - 156、浙江霖梓控股有限公司 + - 157、江西腾飞网络技术有限公司 + - 158、安迅物流有限公司 + - 159、肉联网 + - 160、北京北广梯影广告传媒有限公司 + - 161、上海数慧系统技术有限公司 + - 162、大志天成 + - 163、上海云鹊医 + - 164、上海云鹊医 + - 165、墨迹天气【墨迹天气】 + - 166、上海逸橙信息科技有限公司 + - 167、沅朋物联 + - 168、杭州恒生云融网络科技有限公司 + - 169、绿米联创 + - 170、重庆易宠科技有限公司 + - 171、安徽引航科技有限公司(乐职网) + - 172、上海数联医信企业发展有限公司 + - 173、良彬建材 + - 174、杭州求是同创网络科技有限公司 + - 175、荷马国际 + - 176、点雇网 + - 177、深圳市华星光电技术有限公司 + - 178、厦门神州鹰软件科技有限公司 + - 179、深圳市招商信诺人寿保险有限公司 + - 180、上海好屋网信息技术有限公司 + - 181、海信集团【海信】 + - 182、信凌可信息科技(上海)有限公司 + - 183、长春天成科技发展有限公司 + - 184、用友金融信息技术股份有限公司【用友】 + - 185、北京咖啡易融有限公司 + - 186、国投瑞银基金管理有限公司 + - 187、晋松(上海)网络信息技术有限公司 + - 188、深圳市随手科技有限公司【随手记】 + - 189、深圳水务科技有限公司 + - 190、易企秀【易企秀】 + - 191、北京磁云科技 + - 192、南京蜂泰互联网科技有限公司 + - 193、章鱼直播 + - 194、奖多多科技 + - 195、天津市神州商龙科技股份有限公司 + - 196、岩心科技 + - 197、车码科技(北京)有限公司 + - 198、贵阳市投资控股集团 + - 199、康旗股份 + - 200、龙腾出行 + - 201、杭州华量软件 + - 202、合肥顶岭医疗科技有限公司 + - 203、重庆表达式科技有限公司 + - 204、上海米道信息科技有限公司 + - 205、北京益友会科技有限公司 + - 206、北京融贯电子商务有限公司 + - 207、中国外汇交易中心 + - 208、中国外运股份有限公司 + - 209、中国上海晓圈教育科技有限公司 + - 210、普联软件股份有限公司 + - 211、北京科蓝软件股份有限公司 + - 212、江苏斯诺物联科技有限公司 + - 213、北京搜狐-狐友【搜狐】 + - 214、新大陆网商金融 + - 215、山东神码中税信息科技有限公司 + - 216、河南汇顺网络科技有限公司 + - 217、北京华夏思源科技发展有限公司 + - 218、上海东普信息科技有限公司 + - 219、上海鸣勃网络科技有限公司 + - 220、广东学苑教育发展有限公司 + - 221、深圳强时科技有限公司 + - 222、上海云砺信息科技有限公司 + - 223、重庆愉客行网络有限公司 + - 224、数云 + - 225、国家电网运检部 + - 226、杭州找趣 + - 227、浩鲸云计算科技股份有限公司 + - 228、科大讯飞【科大讯飞】 + - 229、杭州行装网络科技有限公司 + - 230、即有分期金融 + - 231、深圳法司德信息科技有限公司 + - 232、上海博复信息科技有限公司 + - 233、杭州云嘉云计算有限公司 + - 234、有家民宿(有家美宿) + - 235、北京赢销通软件技术有限公司 + - 236、浙江聚有财金融服务外包有限公司 + - 237、易族智汇(北京)科技有限公司 + - 238、合肥顶岭医疗科技开发有限公司 + - 239、车船宝(深圳)旭珩科技有限公司) + - 240、广州富力地产有限公司 + - 241、氢课(上海)教育科技有限公司 + - 242、武汉氪细胞网络技术有限公司 + - 243、杭州有云科技有限公司 + - 244、上海仙豆智能机器人有限公司 + - 245、拉卡拉支付股份有限公司【拉卡拉】 + - 246、虎彩印艺股份有限公司 + - 247、北京数微科技有限公司 + - 248、广东智瑞科技有限公司 + - 249、找钢网 + - 250、九机网 + - 251、杭州跑跑网络科技有限公司 + - 252、深圳未来云集 + - 253、杭州每日给力科技有限公司 + - 254、上海齐犇信息科技有限公司 + - 255、滴滴出行【滴滴】 + - 256、合肥云诊信息科技有限公司 + - 257、云知声智能科技股份有限公司 + - 258、南京坦道科技有限公司 + - 259、爱乐优(二手平台) + - 260、猫眼电影(私有化部署)【猫眼电影】 + - 261、美团大象(私有化部署)【美团大象】 + - 262、作业帮教育科技(北京)有限公司【作业帮】 + - 263、北京小年糕互联网技术有限公司 + - 264、山东矩阵软件工程股份有限公司 + - 265、陕西国驿软件科技有限公司 + - 266、君开信息科技 + - 267、村鸟网络科技有限责任公司 + - 268、云南国际信托有限公司 + - 269、金智教育 + - 270、珠海市筑巢科技有限公司 + - 271、上海百胜软件股份有限公司 + - 272、深圳市科盾科技有限公司 + - 273、哈啰出行【哈啰】 + - 274、途虎养车【途虎】 + - 275、卡思优派人力资源集团 + - 276、南京观为智慧软件科技有限公司 + - 277、杭州城市大脑科技有限公司 + - 278、猿辅导【猿辅导】 + - 279、洛阳健创网络科技有限公司 + - 280、魔力耳朵 + - 281、亿阳信通 + - 282、上海招鲤科技有限公司 + - 283、四川商旅无忧科技服务有限公司 + - 284、UU跑腿 + - 285、北京老虎证券【老虎证券】 + - 286、悠活省吧(北京)网络科技有限公司 + - 287、F5未来商店 + - 288、深圳环阳通信息技术有限公司 + - 289、遠傳電信 + - 290、作业帮(北京)教育科技有限公司【作业帮】 + - 291、成都科鸿智信科技有限公司 + - 292、北京木屋时代科技有限公司 + - 293、大学通(哈尔滨)科技有限责任公司 + - 294、浙江华坤道威数据科技有限公司 + - 295、吉祥航空【吉祥航空】 + - 296、南京圆周网络科技有限公司 + - 297、广州市洋葱omall电子商务 + - 298、天津联物科技有限公司 + - 299、跑哪儿科技(北京)有限公司 + - 300、深圳市美西西餐饮有限公司(喜茶) + - 301、平安不动产有限公司【平安】 + - 302、江苏中海昇物联科技有限公司 + - 303、湖南牙医帮科技有限公司 + - 304、重庆民航凯亚信息技术有限公司(易通航) + - 305、递易(上海)智能科技有限公司 + - 306、亚朵 + - 307、浙江新课堂教育股份有限公司 + - 308、北京蜂创科技有限公司 + - 309、德一智慧城市信息系统有限公司 + - 310、北京翼点科技有限公司 + - 311、湖南智数新维度信息科技有限公司 + - 312、北京玖扬博文文化发展有限公司 + - 313、上海宇珩信息科技有限公司 + - 314、全景智联(武汉)科技有限公司 + - 315、天津易客满国际物流有限公司 + - 316、南京爱福路汽车科技有限公司 + - 317、我房旅居集团 + - 318、湛江亲邻科技有限公司 + - 319、深圳市姜科网络有限公司 + - 320、青岛日日顺物流有限公司 + - 321、南京太川信息技术有限公司 + - 322、美图之家科技优先公司【美图】 + - 323、南京太川信息技术有限公司 + - 324、众薪科技(北京)有限公司 + - 325、武汉安安物联科技有限公司 + - 326、北京智客朗道网络科技有限公司 + - 327、深圳市超级猩猩健身管理管理有限公司 + - 328、重庆达志科技有限公司 + - 329、上海享评信息科技有限公司 + - 330、薪得付信息科技 + - 331、跟谁学 + - 332、中道(苏州)旅游网络科技有限公司 + - 333、广州小卫科技有限公司 + - 334、上海非码网络科技有限公司 + - 335、途家网网络技术(北京)有限公司【途家】 + - 336、广州辉凡信息科技有限公司 + - 337、天维尔信息科技股份有限公司 + - 338、上海极豆科技有限公司 + - 339、苏州触达信息技术有限公司 + - 340、北京热云科技有限公司 + - 341、中智企服(北京)科技有限公司 + - 342、易联云计算(杭州)有限责任公司 + - 343、青岛航空股份有限公司【青岛航空】 + - 344、山西博睿通科技有限公司 + - 345、网易杭州网络有限公司【网易】 + - 346、北京果果乐学科技有限公司 + - 347、百望股份有限公司 + - 348、中保金服(深圳)科技有限公司 + - 349、天津运友物流科技股份有限公司 + - 350、广东创能科技股份有限公司 + - 351、上海倚博信息科技有限公司 + - 352、深圳百果园实业(集团)股份有限公司 + - 353、广州细刻网络科技有限公司 + - 354、武汉鸿业众创科技有限公司 + - 355、金锡科技(广州)有限公司 + - 356、易瑞国际电子商务有限公司 + - 357、奇点云 + - 358、中视信息科技有限公司 + - 359、开源项目:datax-web + - 360、云知声智能科技股份有限公司 + - 361、开源项目:bboss + - 362、成都深驾科技有限公司 + - 363、FunPlus【趣加】 + - 364、杭州创匠信科技有限公司 + - 365、龙匠(北京)科技发展有限公司 + - 366、广州一链通互联网科技有限公司 + - 367、上海星艾网络科技有限公司 + - 368、虎博网络技术(上海)有限公司 + - 369、青岛优米信息技术有限公司 + - 370、八维通科技有限公司 + - 371、烟台合享智星数据科技有限公司 + - 372、东吴证券股份有限公司 + - 373、中通云仓股份有限公司【中通】 + - 374、北京加菲猫科技有限公司 + - 375、北京匠心演绎科技有限公司 + - 376、宝贝走天下 + - 377、厦门众库科技有限公司 + - 378、海通证券数据中心 + - 389、湖南快乐通宝小额贷款有限公司 + - 380、浙江大华技术股份有限公司 + - 381、杭州魔筷科技有限公司 + - 382、青岛掌讯通区块链科技有限公司 + - 383、新大陆金融科技 + - 384、常州玺拓软件科技有限公司 + - 385、北京正保网格教育科技有限公司 + - 386、统一企业(中国)投资有限公司【统一】 + - 387、微革网络科技有限公司 + - 388、杭州融易算科技有限公司 + - 399、青岛上啥班网络科技有限公司 + - 390、京东酒世界 + - 391、杭州爱博仕科技有限公司 + - 392、五星金服控股有限公司 + - 393、福建乐摩物联科技有限公司 + - 394、百炼智能科技有限公司 + - 395、山东能源数智云科技有限公司 + - 396、招商局能源运输股份有限公司 + - 397、三一集团【三一】 + - 398、东巴文(深圳)健康管理有限公司 + - 400、索易软件 + - 401、深圳市宁远科技有限公司 + - 402、熙牛医疗 + - 403、南京智鹤电子科技有限公司 + - 404、嘀嗒出行【嘀嗒出行】 + - 405、广州虎牙信息科技有限公司【虎牙】 + - 406、广州欧莱雅百库网络科技有限公司【欧莱雅】 + - 407、微微科技有限公司 + - 408、我爱我家房地产经纪有限公司【我爱我家】 + - …… + +> 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。 + +欢迎大家的关注和使用,XXL-JOB也将拥抱变化,持续发展。 + + +## Contributing +Contributions are welcome! Open a pull request to fix a bug, or open an [Issue](https://github.com/xuxueli/xxl-job/issues/) to discuss a new feature or change. + +欢迎参与项目贡献!比如提交PR修复一个bug,或者新建 [Issue](https://github.com/xuxueli/xxl-job/issues/) 讨论新特性或者变更。 + + +## Copyright and License +This product is open source and free, and will continue to provide free community technical support. Individual or enterprise users are free to access and use. + +- Licensed under the GNU General Public License (GPL) v3. +- Copyright (c) 2015-present, xuxueli. + +产品开源免费,并且将持续提供免费的社区技术支持。个人或企业内部可自由的接入和使用。 + + +## Donate +No matter how much the donation amount is enough to express your thought, thank you very much :) [To donate](https://www.xuxueli.com/page/donate.html ) + +无论捐赠金额多少都足够表达您这份心意,非常感谢 :) [前往捐赠](https://www.xuxueli.com/page/donate.html ) diff --git a/xxl-job/application.properties b/xxl-job/application.properties new file mode 100644 index 00000000..eae3a677 --- /dev/null +++ b/xxl-job/application.properties @@ -0,0 +1,65 @@ +### web +server.port=9001 +server.servlet.context-path=/xxl-job-admin + +### actuator +management.server.servlet.context-path=/actuator +management.health.mail.enabled=false + +### resources +spring.mvc.servlet.load-on-startup=0 +spring.mvc.static-path-pattern=/static/** +spring.resources.static-locations=classpath:/static/ + +### freemarker +spring.freemarker.templateLoaderPath=classpath:/templates/ +spring.freemarker.suffix=.ftl +spring.freemarker.charset=UTF-8 +spring.freemarker.request-context-attribute=request +spring.freemarker.settings.number_format=0.########## + +### mybatis +mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml +#mybatis.type-aliases-package=com.xxl.job.admin.core.model + +### xxl-job, datasource +spring.datasource.url=jdbc:mysql://192.168.0.106:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai +spring.datasource.username=root +spring.datasource.password=lilishop +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + +### datasource-pool +spring.datasource.type=com.zaxxer.hikari.HikariDataSource +spring.datasource.hikari.minimum-idle=10 +spring.datasource.hikari.maximum-pool-size=30 +spring.datasource.hikari.auto-commit=true +spring.datasource.hikari.idle-timeout=30000 +spring.datasource.hikari.pool-name=HikariCP +spring.datasource.hikari.max-lifetime=900000 +spring.datasource.hikari.connection-timeout=10000 +spring.datasource.hikari.connection-test-query=SELECT 1 +spring.datasource.hikari.validation-timeout=1000 + +### xxl-job, email +spring.mail.host=smtp.qq.com +spring.mail.port=25 +spring.mail.username=xxx@qq.com +spring.mail.from=xxx@qq.com +spring.mail.password=xxx +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true +spring.mail.properties.mail.smtp.starttls.required=true +spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory + +### xxl-job, access token +xxl.job.accessToken= + +### xxl-job, i18n (default is zh_CN, and you can choose "zh_CN", "zh_TC" and "en") +xxl.job.i18n=zh_CN + +## xxl-job, triggerpool max size +xxl.job.triggerpool.fast.max=200 +xxl.job.triggerpool.slow.max=100 + +### xxl-job, log retention days +xxl.job.logretentiondays=30 diff --git a/xxl-job/db/xxl_job.sql b/xxl-job/db/xxl_job.sql new file mode 100644 index 00000000..add0a6f9 --- /dev/null +++ b/xxl-job/db/xxl_job.sql @@ -0,0 +1,236 @@ +/* + Navicat Premium Data Transfer + + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 50711 + Source Host : localhost:3306 + Source Schema : xxl_job + + Target Server Type : MySQL + Target Server Version : 50711 + File Encoding : 65001 + + Date: 24/12/2020 11:17:34 +*/ + +CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci; +use `xxl_job`; +/* + Navicat Premium Data Transfer + + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 50711 + Source Host : localhost:3306 + Source Schema : xxl_job + + Target Server Type : MySQL + Target Server Version : 50711 + File Encoding : 65001 + + Date: 24/12/2020 15:56:01 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for xxl_job_group +-- ---------------------------- +DROP TABLE IF EXISTS `xxl_job_group`; +CREATE TABLE `xxl_job_group` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `app_name` varchar(64) NOT NULL COMMENT '执行器AppName', + `title` varchar(12) NOT NULL COMMENT '执行器名称', + `address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入', + `address_list` text COMMENT '执行器地址列表,多地址逗号分隔', + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; + +-- ---------------------------- +-- Records of xxl_job_group +-- ---------------------------- +BEGIN; +INSERT INTO `xxl_job_group` VALUES (1, 'xxl-job-executor-sample', '示例执行器', 0, NULL, '2020-12-24 15:08:09'); +INSERT INTO `xxl_job_group` VALUES (2, 'xxl-job-executor-lilishop', 'lilishop', 0, 'http://192.168.0.100:8891/', '2020-12-24 15:08:09'); +COMMIT; + +-- ---------------------------- +-- Table structure for xxl_job_info +-- ---------------------------- +DROP TABLE IF EXISTS `xxl_job_info`; +CREATE TABLE `xxl_job_info` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `job_group` int(11) NOT NULL COMMENT '执行器主键ID', + `job_desc` varchar(255) NOT NULL, + `add_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + `author` varchar(64) DEFAULT NULL COMMENT '作者', + `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件', + `schedule_type` varchar(50) NOT NULL DEFAULT 'NONE' COMMENT '调度类型', + `schedule_conf` varchar(128) DEFAULT NULL COMMENT '调度配置,值含义取决于调度类型', + `misfire_strategy` varchar(50) NOT NULL DEFAULT 'DO_NOTHING' COMMENT '调度过期策略', + `executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略', + `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', + `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', + `executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略', + `executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间,单位秒', + `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', + `glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型', + `glue_source` mediumtext COMMENT 'GLUE源代码', + `glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注', + `glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间', + `child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔', + `trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态:0-停止,1-运行', + `trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间', + `trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4; + +-- ---------------------------- +-- Records of xxl_job_info +-- ---------------------------- +BEGIN; +INSERT INTO `xxl_job_info` VALUES (1, 1, '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'CRON', '0 0 0 * * ? *', 'DO_NOTHING', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', '', 0, 0, 0); +INSERT INTO `xxl_job_info` VALUES (2, 2, '每小时执行任务', '2020-12-24 11:01:24', '2020-12-24 15:03:03', 'admin', '', 'CRON', '0 0 0/1 * * ?', 'DO_NOTHING', 'ROUND', 'everyHourExecuteJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2020-12-24 11:01:24', '', 1, 1608793388000, 1608796800000); +INSERT INTO `xxl_job_info` VALUES (3, 2, '每分钟执行', '2020-12-24 11:02:58', '2020-12-24 15:02:49', 'admin', '', 'CRON', '0 0/1 * * * ?', 'DO_NOTHING', 'ROUND', 'everyMinuteExecute', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2020-12-24 11:02:58', '', 1, 1608793680000, 1608793740000); +INSERT INTO `xxl_job_info` VALUES (4, 2, '每天执行', '2020-12-24 11:03:41', '2020-12-24 15:02:28', 'admin', '', 'CRON', '0 5 2 1/1 * ?', 'DO_NOTHING', 'ROUND', 'everyDayExecuteJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2020-12-24 11:03:41', '', 1, 0, 1608833100000); +COMMIT; + +-- ---------------------------- +-- Table structure for xxl_job_lock +-- ---------------------------- +DROP TABLE IF EXISTS `xxl_job_lock`; +CREATE TABLE `xxl_job_lock` ( + `lock_name` varchar(50) NOT NULL COMMENT '锁名称', + PRIMARY KEY (`lock_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ---------------------------- +-- Records of xxl_job_lock +-- ---------------------------- +BEGIN; +INSERT INTO `xxl_job_lock` VALUES ('schedule_lock'); +COMMIT; + +-- ---------------------------- +-- Table structure for xxl_job_log +-- ---------------------------- +DROP TABLE IF EXISTS `xxl_job_log`; +CREATE TABLE `xxl_job_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `job_group` int(11) NOT NULL COMMENT '执行器主键ID', + `job_id` int(11) NOT NULL COMMENT '任务,主键ID', + `executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址', + `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', + `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', + `executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数,格式如 1/2', + `executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', + `trigger_time` datetime DEFAULT NULL COMMENT '调度-时间', + `trigger_code` int(11) NOT NULL COMMENT '调度-结果', + `trigger_msg` text COMMENT '调度-日志', + `handle_time` datetime DEFAULT NULL COMMENT '执行-时间', + `handle_code` int(11) NOT NULL COMMENT '执行-状态', + `handle_msg` text COMMENT '执行-日志', + `alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态:0-默认、1-无需告警、2-告警成功、3-告警失败', + PRIMARY KEY (`id`), + KEY `I_trigger_time` (`trigger_time`), + KEY `I_handle_code` (`handle_code`) +) ENGINE=InnoDB AUTO_INCREMENT=1681 DEFAULT CHARSET=utf8mb4; + +-- ---------------------------- +-- Records of xxl_job_log +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for xxl_job_log_report +-- ---------------------------- +DROP TABLE IF EXISTS `xxl_job_log_report`; +CREATE TABLE `xxl_job_log_report` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `trigger_day` datetime DEFAULT NULL COMMENT '调度-时间', + `running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量', + `suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量', + `fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量', + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; + +-- ---------------------------- +-- Records of xxl_job_log_report +-- ---------------------------- +BEGIN; +INSERT INTO `xxl_job_log_report` VALUES (1, '2020-12-24 00:00:00', 0, 293, 1387, NULL); +INSERT INTO `xxl_job_log_report` VALUES (2, '2020-12-23 00:00:00', 0, 0, 0, NULL); +INSERT INTO `xxl_job_log_report` VALUES (3, '2020-12-22 00:00:00', 0, 0, 0, NULL); +COMMIT; + +-- ---------------------------- +-- Table structure for xxl_job_logglue +-- ---------------------------- +DROP TABLE IF EXISTS `xxl_job_logglue`; +CREATE TABLE `xxl_job_logglue` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `job_id` int(11) NOT NULL COMMENT '任务,主键ID', + `glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型', + `glue_source` mediumtext COMMENT 'GLUE源代码', + `glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注', + `add_time` datetime DEFAULT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ---------------------------- +-- Records of xxl_job_logglue +-- ---------------------------- +BEGIN; +COMMIT; + +-- ---------------------------- +-- Table structure for xxl_job_registry +-- ---------------------------- +DROP TABLE IF EXISTS `xxl_job_registry`; +CREATE TABLE `xxl_job_registry` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `registry_group` varchar(50) NOT NULL, + `registry_key` varchar(255) NOT NULL, + `registry_value` varchar(255) NOT NULL, + `update_time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4; + +-- ---------------------------- +-- Records of xxl_job_registry +-- ---------------------------- +BEGIN; +INSERT INTO `xxl_job_registry` VALUES (4, 'EXECUTOR', 'xxl-job-executor-lilishop', 'http://192.168.0.100:8891/', '2020-12-24 15:08:21'); +COMMIT; + +-- ---------------------------- +-- Table structure for xxl_job_user +-- ---------------------------- +DROP TABLE IF EXISTS `xxl_job_user`; +CREATE TABLE `xxl_job_user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `username` varchar(50) NOT NULL COMMENT '账号', + `password` varchar(50) NOT NULL COMMENT '密码', + `role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员', + `permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割', + PRIMARY KEY (`id`), + UNIQUE KEY `i_username` (`username`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; + +-- ---------------------------- +-- Records of xxl_job_user +-- ---------------------------- +BEGIN; +INSERT INTO `xxl_job_user` VALUES (1, 'admin', '96e79218965eb72c92a549dd5a330112', 1, NULL); +COMMIT; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/xxl-job/xxl-job-admin-2.3.0-SNAPSHOT.jar b/xxl-job/xxl-job-admin-2.3.0-SNAPSHOT.jar new file mode 100644 index 00000000..e6002908 Binary files /dev/null and b/xxl-job/xxl-job-admin-2.3.0-SNAPSHOT.jar differ