74 Commits
v4.4 ... master

Author SHA1 Message Date
pikachu1995@126.com
b5b1c38f71 docs(readme): 更新README文档链接和社区支持信息
- 移除了聚合版相关说明
-修正了部署文档的链接地址
- 更新了社区支持部分,添加在线客服和微信群信息
- 调整了文档中的联系方式展示方式
2025-10-20 14:49:54 +08:00
pikachu1995@126.com
e1f834313a feat(search): 新增ES商品索引管理功能
- 添加删除下架商品索引接口
- 实现清理无效SKU索引逻辑
- 增加商品缓存生成功能
- 扩展商品搜索服务方法
-优化商品详情缓存时间配置
- 新增ES删除参数传输对象EsDeleteDTO
2025-10-20 13:35:00 +08:00
pikachu1995@126.com
dedcc0a556 refactor(payment): 统一使用标准库进行URL编解码
- 将支付宝插件中的Hutool URLEncoder替换为java.net.URLEncoder- 将支付宝插件中的Hutool URLDecoder替换为java.net.URLDecoder
- 在微信支付插件中统一使用标准库进行URL编解码
- 处理UnsupportedEncodingException异常情况- 更新相关注释和日志信息以反映变更
2025-10-11 18:50:44 +08:00
pikachu1995@126.com
33fcce1cc9 refactor(core):优化字符串比较逻辑以提高代码健壮性
- 将多个equals方法调用改为常量在前的写法,避免空指针异常
- 统一了所有字符串比较的格式,增强代码可读性和一致性- 修复潜在的NPE风险,提升系统稳定性
2025-10-11 18:19:52 +08:00
pikachu1995@126.com
85c8c5d021 style(logistics): 格式化顺丰插件代码- 移除了多余的@Component注解- 统一了if语句中的空格格式
- 调整了方法参数间的空格
- 修正了对象创建时的空格问题
- 规范了变量声明时的空格- 优化了代码缩进和换行- 修正了方法调用时的空格问题
- 统一了布尔值比较的格式
- 修正了JSON数据处理时的空格- 优化了代码注释的格式- 修正了sendPost方法中的工具类调用方式
2025-10-11 18:10:14 +08:00
pikachu1995@126.com
2ab2c4ff5d feat(sms): 华为短信插件信任所有HTTPS证书- 在华为短信插件中添加X509TrustManager实现
- 覆盖checkClientTrusted方法以跳过客户端证书验证
- 覆盖checkServerTrusted方法以跳过服务端证书验证- 实现getAcceptedIssuers方法返回null以接受所有颁发者
- 为所有新增方法添加@Override注解确保正确重写

fix(im): 补充IM对话服务实现注解

-为getTalkByUser方法添加@Override注解

fix(member): 补充会员服务实现注解

- 为modifyPass方法添加@Override注解

fix(promotion): 补充促销服务实现注解- 为getGoodsSkuPromotionMap方法添加@Override注解
- 为wrapperPromotionMapList方法添加@Override注解

fix(system): 补充地区服务实现注解- 为updateById方法添加@Override注解
-为save方法添加@Override注解

fix(logistics): 补充顺丰物流插件实现注解

- 为createOrder方法添加@Override注解
2025-10-11 18:07:39 +08:00
OceansDeep
feff49c79b !368 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg
Merge pull request !368 from OceansDeep/pg
2025-10-11 02:51:24 +00:00
misworga831
c813ed0062 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg 2025-10-11 11:49:51 +09:00
misworga831
f658d43a5a fix: 添加空指针检查以优化日志记录逻辑 (pg)
- 在日志切面中为方法返回值和请求对象增加空指针检查,确保稳定性。
- 调整日志记录逻辑以避免潜在异常。
2025-10-11 11:49:36 +09:00
chc
fe0b9c37a4 获取订单货物售后数量调整,以防止申请售后未审核依然取消订单 2025-09-24 15:20:19 +08:00
chc
28cc0617c3 获取订单货物售后数量调整,以防止申请售后未审核依然取消订单 2025-09-23 18:06:40 +08:00
chc
f2cb6b56ea 商品审核swagger 2025-09-23 13:31:11 +08:00
chc
81b7da07f4 sql增加 2025-09-12 16:53:45 +08:00
OceansDeep
db3f775c50 !367 修复商品索引无效促销活动清理逻辑
Merge pull request !367 from 羚羊跳/N/A
2025-09-11 11:47:30 +00:00
羚羊跳
93a45319ac update framework/src/main/java/cn/lili/modules/search/serviceimpl/EsGoodsIndexServiceImpl.java.
fix: 修复商品索引无效促销活动清理逻辑,方法(executeCleanInvalidPromotions)
- 修复分页查询起始索引错误,从1改为0,避免跳过第一页数据
- 修复促销活动清洗后未同步更新到索引的问题,清洗后的promotionMap需重新序列化并设置回goodsIndex

Signed-off-by: 羚羊跳 <zxc2399897@vip.qq.com>
2025-09-11 08:49:49 +00:00
pikachu1995@126.com
7627614284 feat(order): 添加订单数量统计功能
- 新增 OrderNumVO 类用于订单数量统计
- 在 OrderService 接口中添加 getOrderNumVO 方法
- 在 OrderServiceImpl 类中实现 getOrderNumVO 方法
- 在 OrderMapper 接口中添加 getOrderNumVO SQL 查询
- 在 OrderManagerController 和 OrderStoreController 中添加获取订单数量的 API 接口- 优化物流公司的获取方式,
2025-08-29 11:37:41 +08:00
pikachu1995@126.com
ca5887028a feat(order): 添加订单数量统计功能
- 新增 OrderNumVO 类用于订单数量统计
- 在 OrderService 接口中添加 getOrderNumVO 方法
- 在 OrderServiceImpl 类中实现 getOrderNumVO 方法
- 在 OrderMapper 接口中添加 getOrderNumVO SQL 查询
- 在 OrderManagerController 和 OrderStoreController 中添加获取订单数量的 API 接口- 优化物流公司的获取方式,
2025-08-28 11:22:32 +08:00
pikachu1995@126.com
1a3736fd29 refactor(framework): 优化代码中的空值判断和字符串处理- 使用 CollUtil.isNotEmpty() 替代空集合判断
- 使用 CharSequenceUtil.isNotEmpty() 替代空字符串判断
- 使用 Objects.nonNull() 替代对象非空判断- 优化部分代码结构以提高可读性
2025-08-28 09:34:39 +08:00
pikachu1995@126.com
09d00260f6 feat(goods): 添加商品数量统计接口并优化商品查询
- 新增 getGoodsNumVO 接口,用于获取不同状态的商品数量统计
- 在 GoodsSearchParams 中添加 goodsStatus 字段,用于筛选商品状态
- 修改 queryByParams 方法,支持按商品状态进行筛选
- 新增 GoodsNumVO 类,用于返回商品数量统计结果
-优化商品查询性能,通过聚合查询一次性获取所有状态的商品数量
2025-08-27 16:15:56 +08:00
pikachu1995@126.com
2e019ef933 feat(order): 优化订单搜索功能
- 在订单查询中增加用户
2025-08-27 10:12:50 +08:00
pikachu1995@126.com
6acc99c7cf perf(es): 优化品牌聚合查询和网络连接配置
-增加 connectionRequestTimeout 配置,设置为 1000 毫秒
-调整 maxConnPerRoute 为 50,maxConnTotal为 200
-限制 terms 聚合 size,防止内存压力
- 优化品牌聚合查询逻辑,提高查询效率和准确性
2025-08-26 19:33:15 +08:00
pikachu1995@126.com
b57e7813d6 feat(statistics): 添加营业概览统计功能
- 新增营业概览、收款构成、营业构成等统计接口和页面
- 实现订单数、营业额、优惠金额等统计指标
- 添加虚拟发货的物流方式
- 优化订单和充值相关查询方法
2025-08-26 17:32:16 +08:00
OceansDeep
09e6f4a1a1 !366 docs: 为 README.md 中的徽章添加链接 (pg)
Merge pull request !366 from OceansDeep/pg
2025-06-20 02:19:44 +00:00
misworga831
872bb220dc docs: 为 README.md 中的徽章添加链接 (pg) 2025-06-20 10:18:09 +08:00
misworga831
2498e57600 docs: 重构README.md,完善项目介绍与功能说明 (pg)
- 更新项目标题为“Lilishop B2B2C 商城系统”
- 新增GitHub、Gitee、Spring Boot等徽章展示
- 重新组织项目简介,突出技术栈和架构特点
- 详细列出平台管理端与商家端功能模块
- 优化快速开始、数据库初始化及在线演示说明
- 补充技术架构图及前后端技术栈说明
- 增加开源协议与商业授权说明
- 丰富社区支持与贡献内容介绍
- 调整图片大小及格式,提升文档可读性
2025-06-20 10:14:22 +08:00
misworga831
cfbb673387 feat: 添加项目概述、技术架构、开发指南和部署指南文档 2025-06-20 09:57:16 +08:00
chc
05db6501f7 优惠券价格未渲染,但是优惠券却使用问题 2025-06-03 10:52:01 +08:00
chc
a5aa090f4a 使用优惠券时的价格获取方式修改 2025-05-28 10:02:13 +08:00
chc
09aae17a02 渲染优惠券时,增加一次遍历,确保优惠券一定可以正常使用 2025-05-27 19:31:13 +08:00
chc
7f5739f89b 查询bill时间参数错误问题 2025-05-16 14:36:27 +08:00
chc
131d4eb156 config下的application中服务IP地址更换为127.0.0.1 2025-05-08 14:26:03 +08:00
chc
ffa0b0db30 h5支付没有传入sceneInfo问题 2025-04-02 11:00:03 +08:00
chc
7bd0084802 Merge remote-tracking branch 'origin/master' 2025-04-01 15:25:13 +08:00
pikachu1995@126.com
d88762f3ab refactor(connect): 修改微信连接器用户名获取逻辑
- 在 BaseAuthWeChatPCRequest 和 BaseAuthWeChatRequest 中,将用户名获取方式从 nickname 改为 unionid- 更新 Kuaidi100Plugin 中的物流订单请求逻辑,增加多个电子面单相关参数- 在 StoreLogistics 和 StoreLogisticsCustomerDTO 中添加电子面单客户账户名称字段
2025-03-17 15:00:06 +08:00
pikachu1995@126.com
ebdbed6d08 docs(README): 更新交流群信息并优化文档内容
- 移除已满的 QQ 交流群信息
- 添加微信群交流信息- 删除重复的商业授权联系方式- 简化交流群部分的信息
2025-03-04 11:09:06 +08:00
Chopper711
efdf505e63 fix: 修复ServiceException类中异常消息拼接问题 (master)
修改ServiceException类中默认异常消息的设置方式,使用
ResultCode.ERROR.message()代替DEFAULT_MESSAGE。
2025-02-21 11:11:05 +08:00
Chopper711
98f25179d3 fix: 异常消息拼接问题处理 2025-02-20 10:53:40 +08:00
chc
9a0c7dd73d Merge remote-tracking branch 'origin/master' 2025-02-20 10:41:07 +08:00
pikachu1995@126.com
0cf464e549 feat(payment): 微信支付支持公钥和证书两种验证方式- 在 WechatPaymentSetting 中添加 publicType 字段,用于选择验证方式
- 修改 WechatPlugin 中的支付、退款等方法,支持两种验证方式
- 新增 getPublicKeyConfig 和 getCertificateConfig 方法,分别用于获取公钥和证书配置
- 优化退款通知处理逻辑,使用 NotificationParser 进行验证和解析
2025-02-11 15:42:35 +08:00
chc
2e6ddeafdd 优惠券删除时,只有指定商品时,才传递scopeId。 2025-01-22 15:48:10 +08:00
chc
633b94c375 获取用户信息时判断用户是否已禁用 2025-01-20 17:09:24 +08:00
chc
6441018d95 获取用户信息时判断用户是否已禁用 2025-01-20 16:38:10 +08:00
pikachu1995@126.com
e706431df5 refactor(payment): 重构微信支付模块
- 更新微信支付相关工具类和接口实现
- 新增微信支付配置项
- 优化微信支付流程和参数处理
- 引入微信支付官方SDK
2025-01-08 16:13:39 +08:00
pikachu1995@126.com
db1a3566ae 初始化 2024-12-27 14:07:35 +08:00
pikachu1995@126.com
9225b4ff10 refactor(payment): 重构微信支付私钥处理逻辑
- 将方法参数从 keyPath 修改为 key,移除对文件路径的依赖
- 更新方法签名和文档注释,以适应新的参数
- 删除 WechatPaymentSetting 中未使用的证书字段
2024-12-27 11:02:57 +08:00
chc
586a507bb1 订单分包裹发货问题修复 2024-12-25 10:01:47 +08:00
OceansDeep
034f29c734 !362 fix & improve
Merge pull request !362 from OceansDeep/pg
2024-12-12 08:54:57 +00:00
misworga831
478dd1d201 fix: 添加订单和商品存在性检查以改善评价流程 (pg)
- 在MemberEvaluationServiceImpl中引入订单和商品存在性检查。
- 当订单或商品不存在时,抛出异常以避免错误日志的产生。
- 同时,在OrderEveryDayTaskExecute中捕获异常时更新评价状态。
2024-12-12 16:53:20 +08:00
misworga831
554aed024c fix: 添加会员评价时商品存在性检查以修复潜在的空指针异常 (pg)
- 在会员评价时获取商品信息后增加商品存在性检查,避免空指针异常。当商品不存在时,记录错误日志并返回null。
2024-12-12 16:08:13 +08:00
OceansDeep
b2e0fe1de4 !361 fix & improve
Merge pull request !361 from OceansDeep/pg
2024-12-11 08:20:07 +00:00
misworga831
b69a558df0 fix: 优化积分商品,修复促销时间段重叠问题 (pg)
- 添加方法以检查促销时间段是否重叠
- 更新文档注释以提高可读性
- 使用@NotNull注解来增强空值检查
2024-12-11 16:18:58 +08:00
misworga831
2f180a73ed fix: 优化积分商品,移除异步删除索引 (pg)
- 重构deleteByQuery方法,移除异步调用,改为同步处理。
添加了异常处理以记录删除索引时的错误日志。
2024-12-11 16:18:37 +08:00
chc
82f3223a03 订单取消时,将订单货物设置为全部退款 2024-12-11 14:53:47 +08:00
chc
cfe48d539b 拼团问题处理 2024-11-06 18:19:02 +08:00
OceansDeep
8ef00ae285 !360 fix & improve
Merge pull request !360 from pg
2024-10-31 08:47:11 +00:00
misworga831
ed032653ee fix: 优化积分商品活动可以同一时间创建多个 2024-10-31 16:45:38 +08:00
OceansDeep
9108a1585c !359 fix & improve
Merge pull request !359 from pg
2024-10-31 03:50:57 +00:00
misworga831
d4855b702b fix: 增加添加验证码时,检查验证码是否可以正常生成问题 2024-10-31 11:47:41 +08:00
chc
40822ca05b 砍价活动,当随机金额超过剩余砍价金额时,返回剩余金额,防止超过 2024-10-24 14:04:39 +08:00
chc
4dac80f084 砍价活动时间校验问题导致可以创建多个砍价活动 2024-10-23 17:51:31 +08:00
chc
8609216ef5 重复砍价商品导致获取商品信息错误问题 2024-10-23 11:18:26 +08:00
chc
9678c7f506 缺少事务导致发货消息发送失败问题处理 2024-10-18 15:56:23 +08:00
pikachu1995@126.com
63cdebdf1f 微信支付,微信小程序订单,推送发货信息 2024-10-15 10:17:09 +08:00
Chopper711
e89be8eb8c !358 优化 web-二维码登录 优化睡眠时间使用指数退避策略来延长每次轮询的时间间隔,减少CPU占用率
Merge pull request !358 from 铉清/N/A
2024-09-25 01:59:23 +00:00
铉清
3559971a8d 优化 web-二维码登录 优化睡眠时间使用指数退避策略来延长每次轮询的时间间隔,减少CPU占用率
Signed-off-by: 铉清 <13698617+xuanqings@user.noreply.gitee.com>
2024-09-21 16:17:53 +00:00
Chopper711
dc1d1a7e7e Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2024-09-18 15:40:02 +08:00
Chopper711
10ecce1e0d fix:修复代码与注释不匹配问题 2024-09-18 15:39:56 +08:00
chc
2520c27f77 Merge remote-tracking branch 'origin/master' 2024-09-18 15:16:35 +08:00
chc
63d684c972 订单增加部分发货状态。
部分发货无法申请售后
2024-09-18 15:16:28 +08:00
Chopper711
35739b64a3 fix: 部分字段重复问题处理 2024-09-18 14:20:13 +08:00
Chopper711
ddcda57a08 Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop 2024-09-18 12:24:16 +08:00
Chopper711
16ae69ed06 fix: 脚本问题导致限流脚本无效,不确定是否是版本升级带来的问题。 2024-09-18 12:24:11 +08:00
Chopper711
fd067d8abf update DB/version4.3toMASTER.sql.
缺少标点符号导致无法一次性执行 sql

Signed-off-by: Chopper711 <1814994716@qq.com>
2024-09-18 02:09:02 +00:00
pikachu1995@126.com
42b3c72977 微信小程序发货信息录入参数错误,短信发送使用系统后台配置的短信CODE 2024-09-14 16:11:05 +08:00
113 changed files with 3643 additions and 1881 deletions

View File

@@ -0,0 +1,30 @@
---
description:
globs:
alwaysApply: false
---
# Lilishop Project Overview
This is a comprehensive B2B2C e-commerce system built with Spring Boot (backend) and Vue/uniapp (frontend). The project supports multi-tenant merchant access and distributed deployment.
## Key Project Components
### Backend APIs
- [buyer-api/](mdc:buyer-api) - Buyer-facing APIs for shopping functionality
- [seller-api/](mdc:seller-api) - Merchant-facing APIs for store management
- [manager-api/](mdc:manager-api) - Platform admin APIs
- [common-api/](mdc:common-api) - Shared API components
- [im-api/](mdc:im-api) - Instant messaging APIs
### Core Components
- [framework/](mdc:framework) - Core framework and shared utilities
- [consumer/](mdc:consumer) - Message queue consumers
- [config/](mdc:config) - System configuration files
### Documentation
- [docs/](mdc:docs) - Project documentation
- [README.md](mdc:README.md) - Project overview and setup instructions
### Build & Deployment
- [pom.xml](mdc:pom.xml) - Maven project configuration
- [deploy-api.yml](mdc:deploy-api.yml) - Deployment configuration

View File

@@ -0,0 +1,38 @@
---
description:
globs:
alwaysApply: false
---
# Technical Architecture
## Backend Stack
- Spring Boot - Core framework
- Spring MVC - Web framework
- Mybatis-Plus - ORM framework
- Spring Security - Security framework
- JWT - Authentication
- RocketMQ - Message queue
- Redis & MongoDB - Caching
- Elasticsearch - Search engine
- MySQL - Primary database
- Sharding - Database sharding
- XXL-Job - Distributed scheduling
## Frontend Stack
### Admin & Seller Portals ([admin/](mdc:admin))
- Vue.js - Frontend framework
- iView - UI components
- Vuex - State management
- Vue Router - Routing
- Axios - HTTP client
### Mobile Applications
- Uni-app - Cross-platform framework
- uViewui - UI components
- SCSS - Styling
## Infrastructure
- Nginx - Load balancing & reverse proxy
- Aliyun OSS - Object storage
- Docker - Containerization
- Docker Compose - Container orchestration

View File

@@ -0,0 +1,43 @@
---
description:
globs:
alwaysApply: false
---
# Development Guidelines
## Project Setup
1. Ensure you have the following prerequisites:
- JDK 1.8+
- Maven 3.x
- MySQL 5.7+
- Redis
- RocketMQ
- Elasticsearch
- Node.js 12+
## Database Setup
- Initial database scripts can be found in [DB/](mdc:DB)
- For Docker deployment, refer to docker-compose configuration
## API Development Guidelines
1. All new APIs should be placed in appropriate modules:
- Customer-facing APIs in [buyer-api/](mdc:buyer-api)
- Merchant APIs in [seller-api/](mdc:seller-api)
- Admin APIs in [manager-api/](mdc:manager-api)
- Shared components in [common-api/](mdc:common-api)
2. Follow RESTful API conventions:
- Use appropriate HTTP methods (GET, POST, PUT, DELETE)
- Use consistent URL patterns
- Implement proper error handling
## Security Guidelines
1. All sensitive operations must be authenticated
2. Use Spring Security for access control
3. Store sensitive configuration in environment variables
4. Never commit sensitive credentials to version control
## Testing
1. Write unit tests for critical business logic
2. Ensure API endpoints are properly documented
3. Test both success and error scenarios

View File

@@ -0,0 +1,37 @@
---
description:
globs:
alwaysApply: false
---
# Deployment Guide
## Local Development Setup
1. Clone the repository
2. Import as Maven project
3. Configure environment variables in [config/](mdc:config)
4. Start required services (MySQL, Redis, RocketMQ, Elasticsearch)
5. Run individual API modules
## Docker Deployment
- Use [deploy-api.yml](mdc:deploy-api.yml) for container configuration
- Execute [docker-image.sh](mdc:docker-image.sh) to build images
## Production Deployment
1. Configure load balancer (Nginx recommended)
2. Set up database replication/clustering
3. Configure message queue clusters
4. Set up monitoring and logging
5. Configure CDN for static assets
## Configuration Files
- Database: [DB/](mdc:DB)
- API deployment: [deploy-api.yml](mdc:deploy-api.yml)
- Docker scripts: [docker-image.sh](mdc:docker-image.sh)
## Deployment Checklist
1. Database migration and backup
2. Environment variable configuration
3. Service dependencies verification
4. SSL certificate setup
5. Monitoring and alerting setup
6. Backup and recovery procedures

14
DB/index.sql Normal file
View File

@@ -0,0 +1,14 @@
-- 针对WHERE条件、排序和分组的组合索引
CREATE INDEX idx_order_delete_flag_create_time_id_sn ON li_order (delete_flag, create_time DESC, id DESC, sn);
-- 针对WHERE条件和连接条件的组合索引
CREATE INDEX idx_order_status_delete_flag_sn ON li_order (order_status, delete_flag, sn);
-- 针对连接条件的索引
CREATE INDEX idx_order_item_order_sn ON li_order_item (order_sn);
-- 针对过滤条件、排序字段的组合索引
CREATE INDEX idx_li_member_disabled_create_time ON li_member (disabled, create_time DESC);
-- 针对过滤条件、排序字段的组合索引
CREATE INDEX idx_li_goods_delete_flag_create_time ON li_goods (delete_flag, create_time DESC);

View File

@@ -68,8 +68,6 @@ CREATE TABLE `li_order_package_item` (
*/
ALTER TABLE li_order_item ADD `deliver_number` int DEFAULT NULL COMMENT '发货数量';
ALTER TABLE li_goods_sku ADD `alert_quantity` int DEFAULT NULL COMMENT '预警库存';
/*
sku增加预警库存
*/
@@ -105,7 +103,7 @@ WHERE
sf.flow_type = 'REFUND'
AND sf.store_id=b.store_id
AND sf.create_time BETWEEN b.start_time
AND b.end_time),0)
AND b.end_time),0);
UPDATE li_bill b
SET b.kanjia_refund_settlement_price =IFNULL((
@@ -119,20 +117,6 @@ SET b.kanjia_refund_settlement_price =IFNULL((
AND sf.create_time BETWEEN b.start_time
AND b.end_time),0);
/**
*/
ALTER TABLE li_order_item ADD `is_refund` varchar(255) DEFAULT NULL COMMENT '是否退款';
/**
*/
ALTER TABLE li_order_item ADD `refund_price` decimal(10,2) DEFAULT NULL COMMENT '退款金额';
/**
*/
@@ -181,3 +165,9 @@ ALTER TABLE `li_store_flow` ADD `profit_sharing` varchar(255) NULL COMMENT '分
INSERT INTO `lilishop`.`li_setting` (`id`, `create_by`, `create_time`, `delete_flag`, `update_by`, `update_time`, `setting_value`) VALUES ('CONNECT_SETTING', 'admin', '2024-07-07 13:55:38.686000', b'0', NULL, NULL, '{\"callbackUrl\":\"https://buyer-api.pickmall.cn\",\"pc\":\"https://pc-b2b2c.pickmall.cn\",\"wap\":\"https://m-b2b2c.pickmall.cn\"}');
UPDATE `lilishop`.`li_setting` SET `create_by` = 'admin', `create_time` = '2021-01-23 02:18:03.299000', `delete_flag` = b'0', `update_by` = 'admin', `update_time` = '2024-07-07 13:53:44.732000', `setting_value` = '{\"accessKeyId\":\"test\",\"tencentSdkAppId\":\"null\",\"registerTemplateCode\":\"SMS_205755298\",\"huaweiSender\":\"null\",\"signName\":\"lili\",\"tencentSecretId\":\"null\",\"huaweiAppKey\":\"null\",\"isTestModel\":\"true\",\"tencentSecretKey\":\"null\",\"type\":\"ALI\",\"accessSecret\":\"test\",\"tencentSignName\":\"null\",\"huaweiSignature\":\"null\",\"payPasswordTemplateCode\":\"SMS_205755301\",\"walletPasswordTemplateCode\":\"SMS_205755297\",\"findPasswordTemplateCode\":\"SMS_205755301\",\"huaweiAppSecret\":\"null\",\"loginTemplateCode\":\"SMS_205755300\"}' WHERE `id` = 'SMS_SETTING';
ALTER TABLE li_store_logistics ADD `partner_name` varchar(255) DEFAULT NULL COMMENT '电子面单客户账户名称';

286
README.md
View File

@@ -1,205 +1,167 @@
## Lilishop 商城系统
# Lilishop B2B2C 商城系统
[![GitHub Stars](https://img.shields.io/github/stars/hongyehuicheng/lilishop.svg?style=social&logo=github)](https://github.com/hongyehuicheng/lilishop)
[![Gitee Stars](https://gitee.com/beijing_hongye_huicheng/lilishop/badge/star.svg?theme=dark)](https://gitee.com/beijing_hongye_huicheng/lilishop)
[![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0.html)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7.18-brightgreen)](https://spring.io/projects/spring-boot)
[![Vue.js](https://img.shields.io/badge/Vue.js-2.x-green)](https://vuejs.org/)
[![uni-app](https://img.shields.io/badge/uni--app-3.x-green)](https://uniapp.dcloud.io/)
---
### 1. 项目简介
**Lilishop** 是一款功能完善的B2B2C多商户商城系统采用前后端分离架构全端代码开源。后端基于 **SpringBoot** 构建具备高内聚、低耦合的特性支持分布式部署。前端覆盖PC、H5、小程序和APP基于 **Vue****uni-app** 开发。
- **官方网站**: <https://pickmall.cn>
- **官方文档**: <https://docs.pickmall.cn>
- **Gitee 仓库**: <https://gitee.com/beijing_hongye_huicheng/lilishop>
- **GitHub 仓库**: <https://github.com/lilishop/lilishop>
### 商城介绍
**官网**https://pickmall.cn
---
Lilishop商城系统支持商家入驻后端基于SpringBoot 研发,前端使用 Vue、uniapp开发 **系统全端全部代码开源**
### 2. 核心特性
前后端分离支持分布式部署支持Docker各个API独立并且有独立的消费者
- **全端覆盖**: 一套代码库支持PC、H5、小程序、APP降低开发和维护成本
- **商家入驻**: 支持多商家入驻,构建平台化电商生态。
- **分布式架构**: 后端API服务化支持独立部署和弹性伸缩。
- **前后端分离**: 清晰的职责划分,便于团队协作和独立开发。
- **容器化支持**: 提供Docker镜像和docker-compose配置实现一键部署。
- **功能完善**: 涵盖会员、订单、商品、促销、店铺、运营、统计等完整电商业务模块。
### 商城 API/消费者 聚合版
api不需要单独部署只需启动一个jar包就可以正常运转 如有需要,可以点击跳转
https://gitee.com/beijing_hongye_huicheng/lilishop-simplify
---
### 开发/使用/常见问题 帮助文档
### 3. 在线演示
https://docs.pickmall.cn
**注意**: 演示站手机验证码统一为 `111111`。演示环境部署于 `master` 分支。
- **平台管理端**: <https://admin-b2b2c.pickmall.cn>
- 账号: `admin`
- 密码: `123456`
- **店铺管理端**: <https://store-b2b2c.pickmall.cn>
- 账号: `13011111111`
- 密码: `111111`
- **商城PC端**: <https://pc-b2b2c.pickmall.cn>
- **移动端 (H5/小程序/APP)**:
![移动端体验二维码](https://static.pickmall.cn/images/h5-qrcode.png)
#### 欢迎交流需求,交流业务,交流技术(基础问题自行解决,其他问题先看文档后提问)
---
#### 不用削尖脑袋往老群里加,老群活跃度较低,很多潜水党,新群相对而言活跃一些 :tw-1f606: :tw-1f606: :tw-1f606: :tw-1f606: :tw-1f606: :tw-1f606:
### 4. 快速开始
#### 开发新手或者不熟悉的同学在群内提问或新开Issue提问前请先阅读[【提问的智慧】](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md),并确保已查阅过 [【在线文档】](https://docs.pickmall.cn/) ,避免浪费大家的宝贵时间;
#### 环境准备与部署
详细的本地部署指南,请参考官方文档:
[**部署文档 -> 环境准备**](https://docs.pickmall.cn/deply/deply.html)
##### 交流 qq 1群 961316482已满
##### 交流 qq 2群 875294241已满
##### 交流 qq 3群 263785057已满
##### 交流 qq 4群 674617534已满
##### 交流 qq 5群 594675235已满
##### 交流 qq 6群 917026848已满
##### 交流 qq 7群 936344822
#### 数据库初始化
- **推荐方式**: 使用项目提供的 `docker-compose` 配置可自动完成数据库MySQL, Redis, Elasticsearch等的部署与初始化。
- **手动方式**: 如果您选择手动部署SQL脚本位于以下地址。请确保获取与您代码版本一致的SQL文件。
[**数据库脚本 (Gitee)**](https://gitee.com/beijing_hongye_huicheng/docker/tree/master/init/mysql)
##### 体验 公众号/小程序/APP 体验,扫描二维码
---
![image-20210511171611793](https://static.pickmall.cn/images/h5-qrcode.png)
### 5. 技术架构
[![star](https://gitee.com/beijing_hongye_huicheng/lilishop/badge/star.svg?theme=dark)](https://gitee.com/beijing_hongye_huicheng/lilishop/stargazers)
&nbsp;&nbsp;![github](https://img.shields.io/github/stars/hongyehuicheng/lilishop.svg?style=social&logo=#181717)
#### 5.1 架构图
![系统架构图](https://lili-system.oss-cn-beijing.aliyuncs.com/docs/%E6%9E%B6%E6%9E%84.png)
#### 5.2 后端技术栈
#### PS **演示站点所有环境均部署master分支。如果有演示站点问题可以反馈如果演示站点没问题本地运行有问题需自行处理**
| 技术 | 选型 | 备注/用途 |
| :-------------- | :-------------- | :--------- |
| 核心框架 | Spring Boot | 简化应用开发 |
| ORM框架 | Mybatis-Plus | 数据持久化 |
| 数据库 | MySQL | 关系型数据存储 |
| 消息队列 | RocketMQ | 异步任务与解耦 |
| 缓存 | Redis, MongoDB | 数据缓存与存储 |
| 搜索引擎 | Elasticsearch | 商品搜索 |
| 安全框架 | Spring Security | 认证与授权 |
| 分库分表 | ShardingSphere | 数据水平扩展 |
| 定时任务 | XXL-Job | 分布式任务调度 |
| 认证方案 | JWT | Token |
#### 5.3 前端技术栈
### 项目地址
**管理端 (平台/商家)**
gitee : https://gitee.com/beijing_hongye_huicheng
github 镜像: https://github.com/lilishop?tab=repositories
商城UI 项目下3个文件夹
buyer买家PC端seller商家端manager后台管理端
### 演示地址
PS手机验证码为 111111
**平台管理端**https://admin-b2b2c.pickmall.cn 账号admin/123456
**店铺管理端**https://store-b2b2c.pickmall.cn 账号13011111111/111111
**商城PC页面**https://pc-b2b2c.pickmall.cn
**商城 小程序/公众号/APP**:扫描二维码
![image-20210511171611793](https://static.pickmall.cn/images/h5-qrcode.png)
### 快速本地部署
[点击跳转](https://docs.pickmall.cn/deploy/%E8%BF%90%E8%A1%8C%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87.html)
**商城数据库**
使用docker-compose部署数据库自动初始化数据库不需要手动下载等操作
如果手动部署才需要获取sql [点击跳转](https://gitee.com/beijing_hongye_huicheng/docker/tree/master/init/mysql) PS这里有与tag版本一致的sql如果是历史版本则docker项目也切换至历史版本获取sql即可历史版本升级则根据java相聚的根目录DB目录下的升级sql按需执行
| 技术 | 选型 | 备注/用途 |
| :--------- | :--------- | :--------- |
| JS框架 | Vue.js | 核心框架 |
| UI库 | iView | 界面组件 |
| 状态管理 | Vuex | 全局状态管理 |
| 路由 | Vue Router | 页面路由 |
| HTTP客户端 | axios | API请求 |
**移动端 (H5/小程序/APP)**
| 技术 | 选型 | 备注/用途 |
| :-------- | :------ | :------------- |
| 核心框架 | uni-app | 跨平台开发框架 |
| UI库 | uViewUI | 丰富的组件库 |
| CSS预处理 | SCSS | 样式开发 |
### 功能列表
### 6. 功能清单
#### 6.1 平台管理端
#### 平台管理端功能
| 模块 | 主要功能 |
| :--- | :--------------------------------------------------------------------- |
| 首页 | 数据看板、待办事项 |
| 会员 | 会员管理、会员评价、积分管理、资金流水、充值管理 |
| 订单 | 商品订单、虚拟订单、售后处理、订单投诉、收款与退款流水 |
| 商品 | 商品管理、商品审核、分类、品牌、规格、计量单位管理 |
| 促销 | 优惠券、秒杀、砍价、拼团、积分商品等营销活动 |
| 店铺 | 店铺管理、入驻审核、结算管理、店铺对账 |
| 运营 | 页面装修、分销管理、文章管理、意见反馈、站内信、短信配置 |
| 统计 | 会员、订单、流量、商品销量等多维度统计 |
| 设置 | 权限、角色、部门、管理员、系统参数、OSS、支付、物流、敏感词等基础配置 |
#### 6.2 商家端
| 模块 <img width=80/> | 功能 |
|--------------------|-----------------------------------------------------------------|
| 首页 | 平台基础信息统计、待办事项 |
| 会员 | 会员列表、评价列表、积分历史、会员资金、会员充值 |
| 订单 | 商品订单、虚拟订单、订单售后、订单投诉、售后原因维护、收款流水、退款流水 |
| 商品 | 商品列表、商品审核、商品分类、商品品牌、商品规格、商品计量单位 |
| 促销 | 优惠券、券活动(每日&每月&每周&邀新 赠券)、秒杀活动、砍价活动、拼团活动、积分商品 |
| 店铺 | 店铺管理、店铺审核、店铺结算、店铺对账 |
| 运营 | 楼层装修、分销商管理、文章管理、意见反馈、站内信、短信、搜索热词管理 |
| 统计 | 会员统计、订单统计、流量统计、商品销量统计 |
| 设置 | 菜单管理、角色管理、部门管理、管理员管理、系统设置、行政地区管理、OSS管理、联合登陆、支付、物流公司、敏感词、验证码资源 |
| 模块 | 主要功能 |
| :--- | :----------------------------------------------------------- |
| 首页 | 店铺看板、待办事项、公告 |
| 商品 | 商品发布、商品管理、运费模板、店铺内分类 |
| 订单 | 订单处理、评价管理、投诉处理、退款/退货申请 |
| 财务 | 店铺对账、结算管理、发票管理 |
| 促销 | 优惠券、满减、秒杀、拼团、分销商品管理 |
| 统计 | 订单统计、流量分析、商品销量排行 |
| 设置 | 物流配送、自提点、店铺信息、PC/移动端装修、店员与权限管理 |
---
### 7. 界面展示
#### 卖家功能
| 模块 <img width=80/> | 功能 |
|----|-------------------------------|
| 首页 | 店铺基础信息统计、待办事项、店铺公告 |
| 商品 | 商品发布、商品列表、商品模板、店铺分类 |
| 订单 | 商品订单、虚拟订单、订单评价、订单投诉、退款申请、退货申请 |
| 财务 | 店铺对账、店铺结算、发票管理 |
| 促销 | 优惠券、满额优惠、秒杀、拼团 、分销商品、分校订单 |
| 统计 |单统计、流量统计、商品销量统计 |
| 设置 | 配送公司、物流模板、店铺设置、店铺自提设置、PC装修、移动端装修、店员管理、部门管理、角色管理 |
| 消息 | 站内信 |
### 商城前端功能展示
#### 商城移动端
<img src="https://static.pickmall.cn/images/other/app.gif" alt="移动端功能展示" style="zoom:50%;" />
#### 移动端
<img src="https://static.pickmall.cn/images/other/app.gif" alt="移动端功能展示" width="300"/>
#### 平台管理端
![管理端功能展示](https://static.pickmall.cn/images/other/manager.gif)
---
### 技术选型
### 8. 开源与授权
#### 架构图
1. **开源协议**: 本项目遵循 `AGPL-3.0` 开源协议。
2. **使用范围**: 仅允许用于个人学习、研究和非商业用途。
3. **禁止行为**: 禁止将本项目的代码和资源用于任何形式的商业销售。
4. **商业授权**: 如需商业使用,必须获得官方授权。授权为一次性永久授权,并提供持续的版本升级服务。详情请联系官网客服。
5. **软件著作权**: 本软件受国家计算机软件著作权保护登记号2021SR0805085
![技术选型](https://lili-system.oss-cn-beijing.aliyuncs.com/docs/%E6%9E%B6%E6%9E%84.png)
---
##### 后台技术选型
### 9. 社区与支持
| 说明 | 框架 | 说明 | |
| -------------- | --------------- | -------------- | ------------- |
| 基础框架 | Spring Boot | MVC框架 | Spring MVC |
| 持久框架 | Mybatis-Plus | 程序构建 | Maven |
| 关系型数据库 | MySQL | 消息中间件AMQP | RocketMQ |
| 缓存 | Redis +MongoDB | 搜索引擎 | Elasticsearch |
| 安全框架 | Spring Security | 数据库连接池 | Druid |
| 数据库分库分表 | sharding | 定时任务 | xxl-job |
| 负载均衡 | Nginx | 静态资源 | 阿里云OSS |
| 短信 | 阿里云短信 | 认证 | JWT |
| 日志处理 | Log4j | 接口规范 | RESTful |
我们欢迎任何形式的交流与贡献。在提问前,请先查阅 [官方文档](https://docs.pickmall.cn/) ,并参考 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/master/README-zh_CN.md) 以便高效沟通。
##### 前端-运营后台、店铺后台
- **[在线客服](https://work.weixin.qq.com/kfid/kfc4d8dc24a73c15f44)**
- **微信交流1群已满**
- **微信交流2群**:
![微信群](https://lilishop-wechat.oss-cn-beijing.aliyuncs.com/wechat.jpg)
| 说明 | 框架 | 说明 | 框架 |
| ---------- | ---------- | ---------- | ------- |
| 构建工具 | webpack | JS版本 | ES6 |
| 基础JS框架 | Vue.js | 视频播放器 | Dplayer |
| 路由管理 | Vue Router | 状态管理 | Vuex |
| 基础UI库 | iView | UI界面基于 | iView |
| 网络请求 | axios | | |
##### 前端-移动端
| 说明 | 架构 | 说明 | 架构 |
| --------- | ------- | -------- | ------- |
| 基础UI库 | uViewui | 基础框架 | uni-app |
| CSS预处理 | scss | 地图引擎 | amap |
### 版本升级
```
系统后续会提供多场景解决方案。
更多架构微服务、Saas、中台等都会支持。 支持差价升级商业授权
```
### 商业授权
商业版本与开源版本代码一致,没有区分
商业使用需要授权授权方式可选择联系官网客服或者qq群联系群主。
商业授权模式为永久授权,支持永久升级。
商业案例由于涉及部分多层二开关系,如需了解可以咨询销售。
### 开源须知
1.仅允许用于个人学习研究使用.
2.禁止将本开源的代码和资源进行任何形式任何名义的出售.
3.软件受国家计算机软件著作权保护登记号2021SR0805085
4.限制商用如果需要商业使用请联系我们。QQ3409056806.或者加入qq群联系群主。
### 交流群
##### 交流 qq 1群 961316482已满
##### 交流 qq 2群 875294241已满
##### 交流 qq 3群 263785057已满
##### 交流 qq 4群 674617534已满
##### 交流 qq 5群 594675235已满
### 附录
有人有自己的学习视频、学习记录文档、希望宣传关联开源项目等均可以私聊仓库所有者。
类似:
清晨敲代码同学的分析: https://blog.csdn.net/vaevaevae233/category_12103567.html
- **社区贡献内容**:
- 清晨敲代码的分析: <https://blog.csdn.net/vaevaevae233/category_12103567.html>
- DeepWiki: <https://deepwiki.com/lilishop/lilishop>

View File

@@ -59,7 +59,7 @@ public class MemberAddressBuyerController {
public ResultMessage<MemberAddress> addShippingAddress(@Valid MemberAddress shippingAddress) {
//添加会员地址
shippingAddress.setMemberId(Objects.requireNonNull(UserContext.getCurrentUser()).getId());
if(shippingAddress.getIsDefault()==null){
if(Objects.isNull(shippingAddress.getIsDefault())){
shippingAddress.setIsDefault(false);
}
return ResultUtil.data(memberAddressService.saveMemberAddress(shippingAddress));

View File

@@ -29,7 +29,7 @@ import org.springframework.web.context.request.async.DeferredResult;
import javax.validation.constraints.NotNull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 买家端,会员接口
@@ -73,6 +73,8 @@ public class MemberBuyerController {
new ResponseEntity<>(ResultUtil.error(ResultCode.ERROR), HttpStatus.OK);
int timeoutSecond = 20;
DeferredResult<ResponseEntity<Object>> deferredResult = new DeferredResult<>(timeoutSecond * 1000L, timeoutResponseEntity);
// 用于记录重试次数
AtomicInteger retryCount = new AtomicInteger(0);
CompletableFuture.runAsync(() -> {
try {
int i = 0;
@@ -83,7 +85,16 @@ public class MemberBuyerController {
&& (QRCodeLoginSessionStatusEnum.WAIT_SCANNING.getCode() == status
|| QRCodeLoginSessionStatusEnum.SCANNING.getCode() == status)) {
//睡眠一秒种,继续等待结果
TimeUnit.SECONDS.sleep(1);
//TimeUnit.SECONDS.sleep(1);
// 应用指数退避策略
int baseSleepTime = 1000; // 基础退避时间(毫秒)
int maxSleepTime = 10000; // 最大退避时间(毫秒)
int sleepTime = Math.min(maxSleepTime, baseSleepTime * (1 + retryCount.getAndIncrement()));
int randomFactor = (int) (Math.random() * (sleepTime / 2)); // 随机化因子
TimeUnit.MILLISECONDS.sleep(sleepTime + randomFactor);
} else {
deferredResult.setResult(new ResponseEntity<>(ResultUtil.data(queryResult), HttpStatus.OK));
break;

View File

@@ -91,15 +91,16 @@ public class BuyerAuthenticationFilter extends BasicAuthenticationFilter {
try {
Claims claims
= Jwts.parser()
= Jwts.parserBuilder()
.setSigningKey(SecretKeyUtil.generalKeyByDecoders())
.build()
.parseClaimsJws(jwt).getBody();
//获取存储在claims中的用户信息
// 获取存储在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,authUser.getId()) + jwt)) {
if (cache.hasKey(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.MEMBER, authUser.getId()) + jwt)) {
//构造返回信息
List<GrantedAuthority> auths = new ArrayList<>();
auths.add(new SimpleGrantedAuthority("ROLE_" + authUser.getRole().name()));

View File

@@ -20,6 +20,19 @@ management:
exposure:
include: '*'
spring:
mail:
host: smtp.qq.com
port: 465
username: lifenlong@foxmail.com
password: dirpxpqgfvysbefh
protocol: smtps
properties:
mail:
smtp:
auth: true
starttls:
enable: true
# 要在其中注册的Spring Boot Admin Server的URL。
boot:
admin:
@@ -29,8 +42,8 @@ spring:
type: redis
# Redis
redis:
host: 192.168.31.100
port: 30379
host: 127.0.0.1
port: 6379
password: lilishop
lettuce:
pool:
@@ -60,7 +73,7 @@ spring:
default-datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.31.100:30306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: lilishop
maxActive: 50
@@ -211,10 +224,10 @@ lili:
faultTolerant: 3
system:
isDemoSite: false
# 脱敏级别:
# 0不做脱敏处理
# 1管理端用户手机号等信息脱敏
# 2商家端信息脱敏为2时表示管理端商家端同时脱敏
# 脱敏级别:
# 0不做脱敏处理
# 1管理端用户手机号等信息脱敏
# 2商家端信息脱敏为2时表示管理端商家端同时脱敏
sensitiveLevel: 1
statistics:
@@ -256,7 +269,7 @@ lili:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.31.100:30920
cluster-nodes: 127.0.0.1:9200
index:
number-of-replicas: 0
number-of-shards: 3
@@ -267,7 +280,7 @@ lili:
# password: LiLiShopES
logstash:
server: 192.168.31.100:30560
server: 127.0.0.1:4560
rocketmq:
promotion-topic: shop_lili_promotion_topic
promotion-group: shop_lili_promotion_group
@@ -288,7 +301,7 @@ lili:
after-sale-topic: shop_lili_after_sale_topic
after-sale-group: shop_lili_after_sale_group
rocketmq:
name-server: 192.168.31.100:30876
name-server: 127.0.0.1:9876
isVIPChannel: false
producer:
group: lili_group
@@ -297,7 +310,7 @@ rocketmq:
xxl:
job:
admin:
addresses: http://192.168.31.100:30001/xxl-job-admin
addresses: http://127.0.0.1:9001/xxl-job-admin
executor:
appname: xxl-job-executor-lilishop
address:

View File

@@ -4,6 +4,8 @@ import cn.lili.common.utils.CurrencyUtil;
import cn.lili.event.AfterSaleStatusChangeEvent;
import cn.lili.event.TradeEvent;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleSearchParams;
import cn.lili.modules.order.aftersale.service.AfterSaleService;
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.dos.OrderItem;
@@ -34,6 +36,8 @@ public class OrderStatusHandlerExecute implements TradeEvent, AfterSaleStatusCha
private OrderItemService orderItemService;
@Autowired
private OrderService orderService;
@Autowired
private AfterSaleService afterSaleService;
@Override
public void orderCreate(TradeDTO tradeDTO) {
@@ -64,10 +68,16 @@ public class OrderStatusHandlerExecute implements TradeEvent, AfterSaleStatusCha
int returnCount = 0;
// 总购买数量
int deliverCount = 0;
for (OrderItem item : orderItems) {
returnCount += item.getReturnGoodsNumber();
deliverCount += item.getNum();
//获取订单货物已完成售后的数量
AfterSaleSearchParams saleSearchParams = new AfterSaleSearchParams();
saleSearchParams.setOrderSn(afterSale.getOrderSn());
saleSearchParams.setServiceStatus(AfterSaleStatusEnum.COMPLETE.name());
List<AfterSale> afterSales = afterSaleService.exportAfterSaleOrder(saleSearchParams);
for (AfterSale sale : afterSales) {
returnCount += sale.getNum();
}
//订单货物购买总数
deliverCount = order.getGoodsNum();
if (returnCount == deliverCount) {
orderService.systemCancel(afterSale.getOrderSn(),"订单货物全部退款",false);
}

View File

@@ -43,7 +43,7 @@ public class VerificationOrderExecute implements OrderStatusChangeEvent {
//修改虚拟订单货物可以进行售后、投诉
orderItemService.update(new LambdaUpdateWrapper<OrderItem>().eq(OrderItem::getOrderSn, orderMessage.getOrderSn())
.set(OrderItem::getAfterSaleStatus, OrderItemAfterSaleStatusEnum.NOT_APPLIED)
.set(OrderItem::getComplainStatus, OrderComplaintStatusEnum.COMPLETE));
.set(OrderItem::getComplainStatus, OrderComplaintStatusEnum.NO_APPLY));
}
}

View File

@@ -29,14 +29,14 @@ 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 java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author paulG
* @since 2021/3/11
@@ -198,8 +198,9 @@ public class OrderEveryDayTaskExecute implements EveryDayExecute {
try {
memberEvaluationService.addMemberEvaluation(memberEvaluationDTO, false);
} catch (Exception e) {
// 修改订单货物评价状态为已评价避免无限调用评价异常
orderItemService.updateCommentStatus(orderItem.getSn(), CommentStatusEnum.FINISHED);
log.error(e.getMessage(), e);
}
}

View File

@@ -15,15 +15,6 @@
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
@@ -468,6 +459,11 @@
<scope>runtime</scope>
<classifier>osx-aarch_64</classifier>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.15</version>
</dependency>
</dependencies>

View File

@@ -131,4 +131,13 @@ public class ResultUtil<T> {
return this.resultMessage;
}
/**
* 返回失败
*
* @return 消息
*/
public static <T> ResultMessage<T> error() {
return new ResultUtil<T>().setErrorMsg(ResultCode.ERROR);
}
}

View File

@@ -1,5 +1,6 @@
package cn.lili.common.exception;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil;
import cn.lili.common.vo.ResultMessage;
@@ -42,26 +43,21 @@ public class GlobalControllerExceptionHandler {
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public ResultMessage<Object> handleServiceException(HttpServletRequest request, final Exception e, HttpServletResponse response) {
//如果是自定义异常,则获取异常,返回自定义错误消息
if (e instanceof ServiceException) {
ServiceException serviceException = ((ServiceException) e);
ResultCode resultCode = serviceException.getResultCode();
Integer code = null;
String message = null;
Integer code = resultCode.code();
String message = resultCode.message();
if (resultCode != null) {
code = resultCode.code();
message = resultCode.message();
}
//如果有扩展消息,则输出异常中,跟随补充异常
if (!serviceException.getMsg().equals(ServiceException.DEFAULT_MESSAGE)) {
message += ":" + serviceException.getMsg();
if (message != null) {
message = appendErrorMessage(message, serviceException.getMsg());
}
// 对一些特殊异常处理不再打印error级别的日志
assert serviceException.getResultCode() != null;
if (serviceException.getResultCode().equals(ResultCode.DEMO_SITE_EXCEPTION)) {
log.debug("[DEMO_SITE_EXCEPTION]:{}", serviceException.getResultCode().message(), e);
return ResultUtil.error(code, message);
@@ -103,7 +99,30 @@ public class GlobalControllerExceptionHandler {
log.error("全局异常[RuntimeException]:", e);
return ResultUtil.error(ResultCode.ERROR);
// 检查异常链是否包含 ServiceException
ServiceException serviceException = findServiceException(e);
if (serviceException != null) {
ResultCode resultCode = serviceException.getResultCode();
Integer code = resultCode.code();
String message = resultCode.message();
if (message != null) {
message = appendErrorMessage(message, serviceException.getMsg());
}
return ResultUtil.error(code, message);
}
return ResultUtil.error();
}
// 遍历异常链,查找 ServiceException
private ServiceException findServiceException(Throwable ex) {
while (ex != null) {
if (ex instanceof ServiceException) {
return (ServiceException) ex;
}
ex = ex.getCause();
}
return null;
}
// /**
@@ -145,7 +164,8 @@ public class GlobalControllerExceptionHandler {
if (!fieldErrors.isEmpty()) {
return ResultUtil.error(ResultCode.PARAMS_ERROR.code(),
fieldErrors.stream()
.map(FieldError::getDefaultMessage) // 获取每个对象的名称字段
// 获取每个对象的名称字段
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", ")));
}
return ResultUtil.error(ResultCode.PARAMS_ERROR);
@@ -168,4 +188,42 @@ public class GlobalControllerExceptionHandler {
ConstraintViolationException exception = (ConstraintViolationException) e;
return ResultUtil.error(ResultCode.PARAMS_ERROR.code(), exception.getMessage());
}
/**
* 拼接错误消息
*
* @param message 原始消息
* @param appendMessage 需要拼接的消息
* @return 拼接后的消息
*/
private String appendErrorMessage(String message, String appendMessage) {
//这里的代码看起来有点乱,简单解释一下
//场景1服务A服务B=》
// 服务A调用服务B=》
// 服务B抛出异常{扩展消息},拼接后成为{默认消息}{扩展消息}
// 异常被服务A捕获=》
// 最终消息拼接过程中当前方法体参数message是{默认消息}参数appendMessage是服务A给的{默认消息}+{扩展消息},最终会形成{默认消息}+{默认消息}+{扩展消息}
//场景2只有服务A=》
// 服务A抛出异常{扩展消息}=》
// 当前方法体拼接{默认消息}{扩展消息} 并输出返回。
//
//总的来说由于消息拼接是一个流式传递由服务间传递所以这里的消息可能存在A包含B也可能出现B包含A
// 所以这里需要双重判定A包含B=》返回AB包含A=》返回B否则返回拼接后的消息
if (message.contains(appendMessage)) {
return message;
}
if (appendMessage.contains(message)) {
return appendMessage;
}
//忽略默认错误信息,如果有其他错误消息体就不再返回默认的错误消息
if (message.equals(ResultCode.ERROR.message())) {
return appendMessage;
}
if (appendMessage.equals(ResultCode.ERROR.message())) {
return message;
}
return CharSequenceUtil.format("{}-{}", message, appendMessage);
}
}

View File

@@ -15,12 +15,11 @@ public class ServiceException extends RuntimeException {
private static final long serialVersionUID = 3447728300174142127L;
public static final String DEFAULT_MESSAGE = "网络错误,请稍后重试!";
/**
* 异常消息
*/
private String msg = DEFAULT_MESSAGE;
private String msg = ResultCode.ERROR.message();
/**
* 错误码

View File

@@ -92,8 +92,9 @@ public class UserContext {
try {
//获取token的信息
Claims claims
= Jwts.parser()
= Jwts.parserBuilder()
.setSigningKey(SecretKeyUtil.generalKeyByDecoders())
.build()
.parseClaimsJws(accessToken).getBody();
//获取存储在claims中的用户信息
String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString();

View File

@@ -144,7 +144,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public ServletInputStream getInputStream() throws IOException {
BufferedReader bufferedReader = null;
InputStreamReader reader = null;
//获取输入流
@@ -163,47 +162,55 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
//继续读取下一行流直到line为空
line = bufferedReader.readLine();
}
if (CharSequenceUtil.isNotEmpty(body) && Boolean.TRUE.equals(JSONUtil.isJsonObj(body.toString()))) {
//将body转换为map
Map<String, Object> map = JSONUtil.parseObj(body.toString());
//创建空的map用于存储结果
Map<String, Object> resultMap = new HashMap<>(map.size());
//遍历数组
for (Map.Entry<String, Object> entry : map.entrySet()) {
//如果map.get(key)获取到的是字符串就需要进行处理如果不是直接存储resultMap
if (map.get(entry.getKey()) instanceof String) {
resultMap.put(entry.getKey(), filterXss(entry.getKey(), entry.getValue().toString()));
} else {
resultMap.put(entry.getKey(), entry.getValue());
}
// 兼容替换:不再使用过时的 JSONUtil.isJsonObj(String),改为尝试解析并捕获异常
if (CharSequenceUtil.isNotEmpty(body)) {
Map<String, Object> map = null;
try {
map = JSONUtil.parseObj(body.toString());
} catch (Exception ignore) {
map = null;
}
//将resultMap转换为json字符串
String resultStr = JSONUtil.toJsonStr(resultMap);
//将json字符串转换为字节
final ByteArrayInputStream resultBIS = new ByteArrayInputStream(resultStr.getBytes(StandardCharsets.UTF_8));
//实现接口
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
if (map != null) {
//创建空的map用于存储结果
Map<String, Object> resultMap = new HashMap<>(map.size());
//遍历数组
for (Map.Entry<String, Object> entry : map.entrySet()) {
//如果map.get(key)获取到的是字符串就需要进行处理如果不是直接存储resultMap
if (map.get(entry.getKey()) instanceof String) {
resultMap.put(entry.getKey(), filterXss(entry.getKey(), entry.getValue().toString()));
} else {
resultMap.put(entry.getKey(), entry.getValue());
}
}
@Override
public boolean isReady() {
return false;
}
//将resultMap转换为json字符串
String resultStr = JSONUtil.toJsonStr(resultMap);
//将json字符串转换为字节
final ByteArrayInputStream resultBIS = new ByteArrayInputStream(resultStr.getBytes(StandardCharsets.UTF_8));
@Override
public void setReadListener(ReadListener readListener) {
}
//实现接口
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public int read() {
return resultBIS.read();
}
};
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return resultBIS.read();
}
};
}
}
//将json字符串转换为字节

View File

@@ -65,8 +65,9 @@ public class TokenUtil {
Claims claims;
try {
claims = Jwts.parser()
claims = Jwts.parserBuilder()
.setSigningKey(SecretKeyUtil.generalKeyByDecoders())
.build()
.parseClaimsJws(oldRefreshToken).getBody();
} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException |
IllegalArgumentException e) {

View File

@@ -125,13 +125,21 @@ public final class CurrencyUtil {
return (int) price;
}
public static Long getFenLong(Double money) {
BigDecimal bigDecimalValue = BigDecimal.valueOf(money);
// 乘以 100 并四舍五入到最接近的整数
BigDecimal fenValue = bigDecimalValue.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP);
return fenValue.longValue();
}
/**
* 金额转分
*
* @param money 金额
* @return double类型分
*/
public static double reversalFen(Double money) {
public static double reversalFen(Integer money) {
return div(money, 100);
}
}

View File

@@ -22,7 +22,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = {PhoneValidator.class})
public @interface Phone {
String regexp() default "1[3|4|5|7|8]\\d{9}";
String regexp() default "1[3|4|5|7|8|9]\\d{9}";
String message() default "手机号码格式不正确";

View File

@@ -90,7 +90,8 @@ public abstract class BaseElasticsearchService {
request.settings(Settings.builder()
.put("index.number_of_shards", elasticsearchProperties.getIndex().getNumberOfShards())
.put("index.number_of_replicas", elasticsearchProperties.getIndex().getNumberOfReplicas())
.put("index.max_result_window", 100000) //最大查询结果数
//最大查询结果数
.put("index.max_result_window", 100000)
.put("index.mapping.total_fields.limit", 2000));
//创建索引

View File

@@ -66,14 +66,16 @@ public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
.setRequestConfigCallback(requestConfigBuilder ->
requestConfigBuilder
.setConnectTimeout(1000)
.setSocketTimeout(12 * 1000));
.setSocketTimeout(12 * 1000)
.setConnectionRequestTimeout(1000));
}
private HttpAsyncClientBuilder configureHttpClientBuilder(HttpAsyncClientBuilder httpClientBuilder,
CredentialsProvider credentialsProvider) {
httpClientBuilder
.setKeepAliveStrategy(getConnectionKeepAliveStrategy())
.setMaxConnPerRoute(10)
.setMaxConnPerRoute(50)
.setMaxConnTotal(200)
.setDefaultIOReactorConfig(
IOReactorConfig
.custom()

View File

@@ -55,7 +55,7 @@ public class BaseAuthWeChatPCRequest extends BaseAuthRequest {
return ConnectAuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.username(object.getString("unionid"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)

View File

@@ -57,7 +57,7 @@ public class BaseAuthWeChatRequest extends BaseAuthRequest {
return ConnectAuthUser.builder()
.rawUserInfo(object)
.username(object.getString("nickname"))
.username(object.getString("unionid"))
.nickname(object.getString("nickname"))
.avatar(object.getString("headimgurl"))
.location(location)

View File

@@ -11,6 +11,7 @@ import lombok.*;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 商品查询条件
@@ -70,6 +71,9 @@ public class GoodsSearchParams extends PageVO {
@ApiModelProperty(value = "审核状态")
private String authFlag;
@ApiModelProperty(value = "商品状态")
private String goodsStatus;
@ApiModelProperty(value = "库存数量")
private Integer leQuantity;
@@ -96,6 +100,8 @@ public class GoodsSearchParams extends PageVO {
public <T> QueryWrapper<T> queryWrapper() {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
// 统一使用 CharSequenceUtil.isNotEmpty() 处理字符串
if (CharSequenceUtil.isNotEmpty(goodsId)) {
queryWrapper.eq("goods_id", goodsId);
}
@@ -105,9 +111,12 @@ public class GoodsSearchParams extends PageVO {
if (CharSequenceUtil.isNotEmpty(id)) {
queryWrapper.in("id", Arrays.asList(id.split(",")));
}
// 统一使用 CollUtil.isNotEmpty() 处理集合
if (CollUtil.isNotEmpty(ids)) {
queryWrapper.in("id", ids);
}
if (CharSequenceUtil.isNotEmpty(storeId)) {
queryWrapper.eq("store_id", storeId);
}
@@ -120,36 +129,55 @@ public class GoodsSearchParams extends PageVO {
if (CharSequenceUtil.isNotEmpty(storeCategoryPath)) {
queryWrapper.like("store_category_path", storeCategoryPath);
}
if (selfOperated != null) {
if (CharSequenceUtil.isNotEmpty(goodsStatus)) {
if(goodsStatus.equals(GoodsStatusEnum.UPPER.name())){
queryWrapper.eq("auth_flag", GoodsAuthEnum.PASS.name());
queryWrapper.eq("market_enable", GoodsStatusEnum.UPPER.name());
}else if(goodsStatus.equals(GoodsStatusEnum.DOWN.name())){
queryWrapper.eq("auth_flag", GoodsAuthEnum.PASS.name());
queryWrapper.eq("market_enable", GoodsStatusEnum.DOWN.name());
}else if(goodsStatus.equals(GoodsAuthEnum.TOBEAUDITED.name())){
queryWrapper.eq("auth_flag", GoodsAuthEnum.TOBEAUDITED.name());
}else if(goodsStatus.equals(GoodsAuthEnum.REFUSE.name())){
queryWrapper.eq("auth_flag", GoodsAuthEnum.REFUSE.name());
}
}
// 统一使用 Objects.nonNull() 处理对象非空判断
if (Objects.nonNull(selfOperated)) {
queryWrapper.eq("self_operated", selfOperated);
}
if (CharSequenceUtil.isNotEmpty(marketEnable)) {
queryWrapper.eq("market_enable", marketEnable);
}
if (CharSequenceUtil.isNotEmpty(authFlag)) {
queryWrapper.eq("auth_flag", authFlag);
}
if (leQuantity != null) {
if (Objects.nonNull(leQuantity)) {
queryWrapper.le("quantity", leQuantity);
}
if (geQuantity != null) {
if (Objects.nonNull(geQuantity)) {
queryWrapper.gt("quantity", geQuantity);
}
if (recommend != null) {
if (Objects.nonNull(recommend)) {
queryWrapper.le("recommend", recommend);
}
if (CharSequenceUtil.isNotEmpty(goodsType)) {
queryWrapper.eq("goods_type", goodsType);
}
if (CharSequenceUtil.isNotEmpty(salesModel)) {
queryWrapper.eq("sales_model", salesModel);
}
if(alertQuantity != null && alertQuantity){
if(Objects.nonNull(alertQuantity) && alertQuantity){
queryWrapper.apply("quantity <= alert_quantity");
queryWrapper.ge("alert_quantity", 0);
}
queryWrapper.in(CollUtil.isNotEmpty(ids), "id", ids);
queryWrapper.in(CollUtil.isNotEmpty(ids), "id", ids);
queryWrapper.eq("delete_flag", false);
this.betweenWrapper(queryWrapper);
return queryWrapper;

View File

@@ -0,0 +1,17 @@
package cn.lili.modules.goods.entity.vos;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class GoodsNumVO {
@ApiModelProperty(value = "出售中的商品数量")
private Integer upperGoodsNum;
@ApiModelProperty(value = "仓库中的商品数量")
private Integer downGoodsNum;
@ApiModelProperty(value = "待审核商品数量")
private Integer auditGoodsNum;
@ApiModelProperty(value = "审核未通过数量")
private Integer refuseGoodsNum;
}

View File

@@ -5,6 +5,7 @@ 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.GoodsNumVO;
import cn.lili.modules.goods.entity.vos.GoodsVO;
import cn.lili.modules.store.entity.dos.Store;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -83,6 +84,14 @@ public interface GoodsService extends IService<Goods> {
*/
IPage<Goods> queryByParams(GoodsSearchParams goodsSearchParams);
/**
* 获取商品数量
*
* @param goodsSearchParams 查询参数
* @return 商品数量
*/
GoodsNumVO getGoodsNumVO(GoodsSearchParams goodsSearchParams);
/**
* 商品查询
@@ -191,6 +200,11 @@ public interface GoodsService extends IService<Goods> {
*/
void categoryGoodsName(String categoryId);
/**
* 添加商品评价数量
*
* @param commentNum 评价数量
* @param goodsId 商品ID
*/
void addGoodsCommentNum(Integer commentNum, String goodsId);
}

View File

@@ -205,7 +205,7 @@ public class GoodsImportServiceImpl implements GoodsImportService {
goodsImportDTO.setCategory(category);
goodsImportDTO.setTemplate(templateId);
goodsImportDTO.setGoodsUnit(objects.get(4).toString().substring(objects.get(4).toString().indexOf("-") + 1));
goodsImportDTO.setRelease(objects.get(5).toString().equals("上架"));
goodsImportDTO.setRelease("上架".equals(objects.get(5).toString()));
List<String> goodsGalleryList = new ArrayList<>();
goodsGalleryList.add(objects.get(6).toString());

View File

@@ -20,6 +20,7 @@ import cn.lili.modules.goods.entity.dto.GoodsParamsDTO;
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.GoodsNumVO;
import cn.lili.modules.goods.entity.vos.GoodsSkuVO;
import cn.lili.modules.goods.entity.vos.GoodsVO;
import cn.lili.modules.goods.mapper.GoodsMapper;
@@ -272,7 +273,7 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
goodsVO.setWholesaleList(wholesaleList);
}
cache.put(CachePrefix.GOODS.getPrefix() + goodsId, goodsVO);
cache.put(CachePrefix.GOODS.getPrefix() + goodsId, goodsVO,7200L);
return goodsVO;
}
@@ -281,6 +282,40 @@ public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements
return this.page(PageUtil.initPage(goodsSearchParams), goodsSearchParams.queryWrapper());
}
@Override
public GoodsNumVO getGoodsNumVO(GoodsSearchParams goodsSearchParams) {
GoodsNumVO goodsNumVO = new GoodsNumVO();
// 获取基础查询条件
QueryWrapper<Goods> baseWrapper = goodsSearchParams.queryWrapper();
// 使用聚合查询一次性获取所有状态的商品数量
List<Map<String, Object>> results = this.baseMapper.selectMaps(
baseWrapper.select(
"COUNT(CASE WHEN auth_flag = 'PASS' AND market_enable = 'UPPER' THEN 1 END) as upperGoodsNum",
"COUNT(CASE WHEN auth_flag = 'PASS' AND market_enable = 'DOWN' THEN 1 END) as downGoodsNum",
"COUNT(CASE WHEN auth_flag = 'TOBEAUDITED' THEN 1 END) as auditGoodsNum",
"COUNT(CASE WHEN auth_flag = 'REFUSE' THEN 1 END) as refuseGoodsNum"
)
);
if (!results.isEmpty()) {
Map<String, Object> result = results.get(0);
goodsNumVO.setUpperGoodsNum(((Number) result.get("upperGoodsNum")).intValue());
goodsNumVO.setDownGoodsNum(((Number) result.get("downGoodsNum")).intValue());
goodsNumVO.setAuditGoodsNum(((Number) result.get("auditGoodsNum")).intValue());
goodsNumVO.setRefuseGoodsNum(((Number) result.get("refuseGoodsNum")).intValue());
} else {
// 如果没有结果设置默认值为0
goodsNumVO.setUpperGoodsNum(0);
goodsNumVO.setDownGoodsNum(0);
goodsNumVO.setAuditGoodsNum(0);
goodsNumVO.setRefuseGoodsNum(0);
}
return goodsNumVO;
}
/**
* 商品查询
*

View File

@@ -535,7 +535,8 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
try (InputStream inputStream = file.getInputStream()) {
// 使用 WorkbookFactory.create 方法读取 Excel 文件
Workbook workbook = WorkbookFactory.create(inputStream);
Sheet sheet = workbook.getSheetAt(0); // 我们只读取第一个sheet
// 我们只读取第一个sheet
Sheet sheet = workbook.getSheetAt(0);
// 检查第一个sheet的行数是否超过10002行
if (sheet.getPhysicalNumberOfRows() > 10002) {
@@ -1039,7 +1040,8 @@ public class GoodsSkuServiceImpl extends ServiceImpl<GoodsSkuMapper, GoodsSku> i
// 设置下拉列表数据验证
DataValidationHelper validationHelper = templateSheet.getDataValidationHelper();
DataValidationConstraint constraint = validationHelper.createExplicitListConstraint(new String[]{"", ""});
CellRangeAddressList addressList = new CellRangeAddressList(2, 10001, 2, 2); // 从第3行到第10002行第3列
// 从第3行到第10002行第3列
CellRangeAddressList addressList = new CellRangeAddressList(2, 10001, 2, 2);
DataValidation validation = validationHelper.createValidation(constraint, addressList);
validation.setSuppressDropDownArrow(true);
validation.setShowErrorBox(true);

View File

@@ -46,7 +46,7 @@ public class ImTalkServiceImpl extends ServiceImpl<ImTalkMapper, ImTalk> impleme
@Autowired
private ImMessageService imMessageService;
@Override
public ImTalk getTalkByUser(String userId) {
LambdaQueryWrapper<ImTalk> queryWrapper = new LambdaQueryWrapper<>();
AuthUser currentUser = Objects.requireNonNull(UserContext.getCurrentUser());

View File

@@ -131,50 +131,67 @@ public class KdniaoPlugin implements LogisticsPlugin {
StoreLogistics storeLogistics = labelOrderDTO.getStoreLogistics();
//组装快递鸟应用级参数
String resultDate = "{" +
"'OrderCode': '" + order.getSn() + "'," + //订单编码
"'ShipperCode': '" + logistics.getCode() + "'," + //快递公司编码
"'CustomerName': '" + storeLogistics.getCustomerName() + "'," +//客户编码
"'CustomerPwd': '" + storeLogistics.getCustomerPwd() + "'," + //客户密码
"'MonthCode': '" + storeLogistics.getMonthCode() + "'," + //密钥
"'SendSite': '" + storeLogistics.getSendSite() + "'," + //归属网点
"'SendStaff': '" + storeLogistics.getSendStaff() + "'," + //收件快递员
"'PayType': " + storeLogistics.getPayType() + "," +
"'ExpType': " + storeLogistics.getExpType() + "," +
//发件人信息
"'Sender': {" +
"'Name': '" + storeDeliverGoodsAddressDTO.getSalesConsignorName() + "'," +
"'Mobile': '" + storeDeliverGoodsAddressDTO.getSalesConsignorMobile() + "'," +
"'ProvinceName': '" + consignorAddress[0] + "'," + //省
"'CityName': '" + consignorAddress[1] + "'," + //市
"'ExpAreaName': '" + consignorAddress[2] + "'," + //区
"'Address': '" + storeDeliverGoodsAddressDTO.getSalesConsignorDetail() + "'" + //发件人详细地址
"}," +
//收件人信息
"'Receiver': {" +
"'Name': '" + order.getConsigneeName() + "'," +
"'Mobile': '" + order.getConsigneeMobile() + "'," +
"'ProvinceName': '" + ConsigneeAddress[0] + "'," + //
"'CityName': '" + ConsigneeAddress[1] + "'," + //市
"'ExpAreaName': '" + ConsigneeAddress[2] + "'," + //区
"'Address': '" + order.getConsigneeDetail() + "'" + //收件人详细地址
"}," +
//商品信息
"'Commodity': [";
String resultDate = "{"
// 订单编码
+ "'OrderCode': '" + order.getSn() + "',"
// 快递公司编码
+ "'ShipperCode': '" + logistics.getCode() + "',"
// 客户编码
+ "'CustomerName': '" + storeLogistics.getCustomerName() + "',"
// 客户密码
+ "'CustomerPwd': '" + storeLogistics.getCustomerPwd() + "',"
// 密钥
+ "'MonthCode': '" + storeLogistics.getMonthCode() + "',"
// 归属网点
+ "'SendSite': '" + storeLogistics.getSendSite() + "',"
// 收件快递员
+ "'SendStaff': '" + storeLogistics.getSendStaff() + "',"
+ "'PayType': " + storeLogistics.getPayType() + ","
+ "'ExpType': " + storeLogistics.getExpType() + ","
// 发件人信息
+ "'Sender': {"
+ "'Name': '" + storeDeliverGoodsAddressDTO.getSalesConsignorName() + "',"
+ "'Mobile': '" + storeDeliverGoodsAddressDTO.getSalesConsignorMobile() + "',"
// 省
+ "'ProvinceName': '" + consignorAddress[0] + "',"
//
+ "'CityName': '" + consignorAddress[1] + "',"
//
+ "'ExpAreaName': '" + consignorAddress[2] + "',"
// 发件人详细地址
+ "'Address': '" + storeDeliverGoodsAddressDTO.getSalesConsignorDetail() + "'"
+ "},"
// 收件人信息
+ "'Receiver': {"
+ "'Name': '" + order.getConsigneeName() + "',"
+ "'Mobile': '" + order.getConsigneeMobile() + "',"
// 省
+ "'ProvinceName': '" + ConsigneeAddress[0] + "',"
// 市
+ "'CityName': '" + ConsigneeAddress[1] + "',"
// 区
+ "'ExpAreaName': '" + ConsigneeAddress[2] + "',"
// 收件人详细地址
+ "'Address': '" + order.getConsigneeDetail() + "'"
+ "},"
// 商品信息
+ "'Commodity': [";
//子订单信息
for (OrderItem orderItem : orderItems) {
resultDate = resultDate + "{" +
"'GoodsName': '" + orderItem.getGoodsName() + "'," +
"'Goodsquantity': '" + orderItem.getNum() + "'" +
"},";
resultDate = resultDate + "{"
+ "'GoodsName': '" + orderItem.getGoodsName() + "',"
+ "'Goodsquantity': '" + orderItem.getNum() + "'"
+ "},";
}
resultDate = resultDate + "]," +
"'Quantity': " + orderItems.size() + "," + //包裹数
"'IsReturnPrintTemplate':1," + //生成电子面单模板
"'Remark': '" + order.getRemark() + "'" +//商家备注
"}";
resultDate = resultDate + "],"
// 包裹数
+ "'Quantity': " + orderItems.size() + ","
// 生成电子面单模板
+ "'IsReturnPrintTemplate':1,"
// 商家备注
+ "'Remark': '" + order.getRemark() + "'"
+ "}";
//组织系统级参数
Map<String, String> params = new HashMap<>();
@@ -200,9 +217,9 @@ public class KdniaoPlugin implements LogisticsPlugin {
JSONObject obj = JSONObject.parseObject(result);
log.info("电子面单响应:{}", result);
if (!"100".equals(obj.getString("ResultCode"))) {
// resultMap.put("Reason",obj.getString("Reason"));
// resultMap.put("Reason",obj.getString("Reason"));
throw new ServiceException(obj.getString("Reason"));
// return resultMap;
// return resultMap;
}
JSONObject orderJson = JSONObject.parseObject(obj.getString("Order"));

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.logistics.plugin.kuaidi100;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.modules.logistics.LogisticsPlugin;
import cn.lili.modules.logistics.entity.dto.LabelOrderDTO;
import cn.lili.modules.logistics.entity.enums.LogisticsEnum;
@@ -28,6 +29,7 @@ import com.kuaidi100.sdk.request.labelV2.OrderReq;
import com.kuaidi100.sdk.response.QueryTrackData;
import com.kuaidi100.sdk.response.QueryTrackMapResp;
import com.kuaidi100.sdk.response.QueryTrackResp;
import com.kuaidi100.sdk.response.labelV2.OrderResult;
import com.kuaidi100.sdk.response.samecity.OrderResp;
import com.kuaidi100.sdk.utils.SignUtils;
import lombok.extern.slf4j.Slf4j;
@@ -140,25 +142,74 @@ public class Kuaidi100Plugin implements LogisticsPlugin {
StoreLogistics storeLogistics = labelOrderDTO.getStoreLogistics();
ManInfo recManInfo = new ManInfo();
recManInfo.setName(order.getConsigneeName());
recManInfo.setMobile(order.getConsigneeMobile());
recManInfo.setPrintAddr(consigneeAddress[0] + consigneeAddress[1] + consigneeAddress[2] + consigneeAddress[3] + order.getConsigneeDetail());
ManInfo sendManInfo = new ManInfo();
sendManInfo.setName(storeDeliverGoodsAddressDTO.getSalesConsignorName());
sendManInfo.setMobile(storeDeliverGoodsAddressDTO.getSalesConsignorMobile());
sendManInfo.setPrintAddr(consignorAddress[0] + consignorAddress[1] + consignorAddress[2] + consignorAddress[3] + storeDeliverGoodsAddressDTO.getSalesConsignorDetail());
OrderReq orderReq = new OrderReq();
//打印类型NON只下单不打印默认 IMAGE:生成图片短链HTML:生成html短链 CLOUD:使用快递100云打印机打印使用CLOUD时siid必填
orderReq.setPrintType(PrintType.HTML);
//电子面单客户账户或月结账号,需贵司向当地快递公司网点申请(参考电子面单申请指南); 是否必填该属性,请查看参数字典
orderReq.setPartnerId(storeLogistics.getCustomerName());
//电子面单密码,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getCustomerPwd())){
orderReq.setPartnerKey(storeLogistics.getCustomerPwd());
}
//电子面单密钥,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getMonthCode())) {
orderReq.setPartnerSecret(storeLogistics.getMonthCode());
}
//电子面单客户账户名称,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getPartnerName())) {
orderReq.setPartnerName(storeLogistics.getPartnerName());
}
// orderReq.setNet();
// 电子面单承载编号,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getSendSite())) {
orderReq.setCode(storeLogistics.getSendSite());
}
//电子面单承载快递员名,需贵司向当地快递公司网点申请; 是否必填该属性,请查看参数字典
if(CharSequenceUtil.isNotEmpty(storeLogistics.getSendStaff())) {
orderReq.setCheckMan(storeLogistics.getSendStaff());
}
//快递公司的编码,一律用小写字母,请查看参数字典
orderReq.setKuaidicom(logistics.getCode());
orderReq.setCount(1);
//收件人信息
ManInfo manInfo=new ManInfo();
//收件人姓名
manInfo.setName(order.getConsigneeName());
//收件人的手机号,手机号和电话号二者其一必填
manInfo.setMobile(order.getConsigneeMobile());
//收件人的电话号,手机号和电话号二者其一必填
// manInfo.setTel("");
//收件人所在完整地址如广东深圳市南山区科技南十二路金蝶软件园B10
manInfo.setPrintAddr(consigneeAddress[0]+consigneeAddress[1]+consigneeAddress[2]+consigneeAddress[3]+order.getConsigneeDetail());
orderReq.setRecMan(manInfo);
ManInfo sendMan=new ManInfo();
// 寄件人信息
sendMan.setName(storeDeliverGoodsAddressDTO.getSalesConsignorName());
// 寄件人的手机号,手机号和电话号二者其一必填
sendMan.setMobile(storeDeliverGoodsAddressDTO.getSalesConsignorMobile());
//寄件人的电话号,手机号和电话号二者其一必填
// sendMan.setTel("");
//寄件人所在的完整地址如广东深圳市南山区科技南十二路金蝶软件园B10
sendMan.setPrintAddr(consignorAddress[0]+consignorAddress[1]+consignorAddress[2]+consignorAddress[3]+storeDeliverGoodsAddressDTO.getSalesConsignorDetail());
//寄件人所在公司名称
// sendMan.setCompany("");
orderReq.setSendMan(sendMan);
//物品名称,例:文件
String goodsName="";
for (OrderItem orderItem : orderItems) {
goodsName+=orderItem.getGoodsName() + "',";
}
orderReq.setCargo(goodsName);
// 包裹总数量。
orderReq.setCount(orderItems.size());
//打印设备通过打印机输出的设备码进行获取printType为CLOUD时必填
// orderReq.setSiid("");
// orderReq.setSiid(siid);
//orderReq.setTempId("60f6c17c7c223700131d8bc3");
orderReq.setSendMan(sendManInfo);
orderReq.setRecMan(recManInfo);
orderReq.setPrintType(PrintType.CLOUD);
String param = new Gson().toJson(orderReq);
String t = System.currentTimeMillis() + "";
@@ -167,14 +218,19 @@ public class Kuaidi100Plugin implements LogisticsPlugin {
printReq.setT(t);
printReq.setKey(logisticsSetting.getKuaidi100Key());
printReq.setSign(SignUtils.printSign(param, t, logisticsSetting.getKuaidi100Key(), logisticsSetting.getKuaidi100Customer()));
printReq.setMethod(ApiInfoConstant.ORDER);
printReq.setMethod(ApiInfoConstant.NEW_TEMPLATE_URL);
printReq.setParam(param);
IBaseClient baseClient = new LabelV2();
HttpResult result = baseClient.execute(printReq);
System.out.println(result.getBody());
QueryTrackMapResp queryTrackMapResp = new Gson().fromJson(result.getBody(), QueryTrackMapResp.class);
OrderResp orderResp = new Gson().fromJson(result.getBody(), OrderResp.class);
OrderResult orderResult = new Gson().fromJson(result.getBody(), OrderResult.class);
log.info("电子面单响应:{}", orderResult);
System.out.println("快递单号:"+orderResult.getKdComOrderNum());
System.out.println("面单短链:"+orderResult.getLabel());
} catch (Exception e) {
e.printStackTrace();

View File

@@ -22,13 +22,13 @@ import com.sf.csim.express.service.HttpClientUtil;
import com.sf.csim.express.service.IServiceCodeStandard;
import com.sf.csim.express.service.code.ExpressServiceCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.util.*;
/**
* 顺丰插件
*
* @author admin
*/
@Slf4j
@@ -43,7 +43,8 @@ public class ShunfengPlugin implements LogisticsPlugin {
**/
private LogisticsSetting logisticsSetting;
public ShunfengPlugin(){}
public ShunfengPlugin() {
}
public ShunfengPlugin(LogisticsSetting logisticsSetting) {
this.logisticsSetting = logisticsSetting;
@@ -59,10 +60,11 @@ public class ShunfengPlugin implements LogisticsPlugin {
*
* @param orderDetailVO
*/
@Override
public String createOrder(OrderDetailVO orderDetailVO) {
StoreDetailService storeService = SpringContextUtil.getBean(StoreDetailService.class);
StoreDeliverGoodsAddressDTO storeDeliverGoodsAddressDTO = storeService.getStoreDeliverGoodsAddressDto(orderDetailVO.getOrder().getStoreId());
if(storeDeliverGoodsAddressDTO == null){
if (storeDeliverGoodsAddressDTO == null) {
throw new ServiceException(ResultCode.STORE_DELIVER_ADDRESS_EXIST);
}
try {
@@ -108,7 +110,7 @@ public class ShunfengPlugin implements LogisticsPlugin {
String result = sendPost(ExpressServiceCodeEnum.EXP_RECE_CREATE_ORDER, msgDataMap);
JSONObject resultData = JSONUtil.parseObj(result).getJSONObject("apiResultData");
if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){
if (Boolean.TRUE.toString().equals(resultData.get("success").toString())) {
return resultData.getJSONObject("msgData").getJSONArray("waybillNoInfoList").getJSONObject(0).get("waybillNo").toString();
}
throw new ServiceException(resultData.get("errorMsg").toString());
@@ -141,10 +143,10 @@ public class ShunfengPlugin implements LogisticsPlugin {
msgDataMap.put("trackingNumber", trackingNumber);
JSONObject result = JSONUtil.parseObj(sendPost(ExpressServiceCodeEnum.EXP_RECE_SEARCH_ROUTES, msgDataMap));
JSONObject resultData = result.getJSONObject("apiResultData");
if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){
if (Boolean.TRUE.toString().equals(resultData.get("success").toString())) {
JSONArray routesJson = resultData.getJSONObject("msgData").getJSONArray("routeResps").getJSONObject(0).getJSONArray("routes");
List<Map> routes = routesJson.toList(Map.class);
return new Traces(logistics.getName(),expNo,routes);
return new Traces(logistics.getName(), expNo, routes);
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
@@ -164,7 +166,7 @@ public class ShunfengPlugin implements LogisticsPlugin {
* @return
*/
@Override
public Map<String,Object> labelOrder(LabelOrderDTO labelOrderDTO) {
public Map<String, Object> labelOrder(LabelOrderDTO labelOrderDTO) {
try {
Map<String, Object> msgDataMap = new HashMap<>();
//模板编码
@@ -173,15 +175,15 @@ public class ShunfengPlugin implements LogisticsPlugin {
//业务数据
Map<String, Object> documents = new HashMap<>();
documents.put("masterWaybillNo", labelOrderDTO.getOrder().getLogisticsNo());
msgDataMap.put("documents",documents);
msgDataMap.put("sync",true);
msgDataMap.put("documents", documents);
msgDataMap.put("sync", true);
/**
* 版本号,传固定值:2.0
*/
msgDataMap.put("version", "2.0");
JSONObject result = JSONUtil.parseObj(sendPost(ExpressServiceCodeEnum.COM_RECE_CLOUD_PRINT_WAYBILLS, msgDataMap));
JSONObject resultData = result.getJSONObject("apiResultData");
if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){
if (Boolean.TRUE.toString().equals(resultData.get("success").toString())) {
return resultData.getJSONObject("obj").getJSONArray("files").toList(Map.class).get(0);
}
throw new ServiceException(resultData.getJSONArray("errorMessage").get(0).toString());
@@ -204,13 +206,13 @@ public class ShunfengPlugin implements LogisticsPlugin {
//校验类型 1电话号码校验 2月结卡号校验
msgDataMap.put("checkType", 1);
//校验值 当校验类型为1时传电话号码 当校验类型为2时传月结卡号
List<String> mobileList= new ArrayList<>();
List<String> mobileList = new ArrayList<>();
mobileList.add(checkNos);
msgDataMap.put("checkNos", mobileList);
JSONObject result = JSONUtil.parseObj(sendPost(ExpressServiceCodeEnum.EXP_RECE_SEARCH_PROMITM, msgDataMap));
JSONObject resultData = result.getJSONObject("apiResultData");
if(Boolean.TRUE.toString().equals(resultData.get("success").toString())){
return resultData.getJSONObject("msgData").get("promiseTm").toString();
if (Boolean.TRUE.toString().equals(resultData.get("success").toString())) {
return resultData.getJSONObject("msgData").get("promiseTm").toString();
}
throw new ServiceException(resultData.get("errorMsg").toString());
} catch (UnsupportedEncodingException e) {
@@ -219,7 +221,6 @@ public class ShunfengPlugin implements LogisticsPlugin {
}
private String sendPost(IServiceCodeStandard standardService, Map<String, Object> msgDataMap) throws UnsupportedEncodingException {
CallExpressServiceTools tools = CallExpressServiceTools.getInstance();
Map<String, String> params = new HashMap<String, String>();
String timeStamp = String.valueOf(System.currentTimeMillis());
// 顾客编码
@@ -230,7 +231,7 @@ public class ShunfengPlugin implements LogisticsPlugin {
params.put("timestamp", timeStamp);
params.put("msgData", JSONUtil.toJsonStr(msgDataMap));
params.put("msgDigest", tools.getMsgDigest(params.get("msgData"), timeStamp, logisticsSetting.getCheckWord()));
params.put("msgDigest", CallExpressServiceTools.getMsgDigest(params.get("msgData"), timeStamp, logisticsSetting.getCheckWord()));
String result = HttpClientUtil.post(logisticsSetting.getCallUrl(), params);
log.info("===调用地址 ===" + logisticsSetting.getCallUrl());

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.member.entity.dos;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.modules.member.entity.dto.ClerkAddDTO;
import cn.lili.modules.store.entity.dos.Store;
@@ -59,7 +60,7 @@ public class Clerk extends BaseEntity {
* @param clerkAddDTO
*/
public Clerk(ClerkAddDTO clerkAddDTO) {
if (clerkAddDTO.getRoles()!=null && !clerkAddDTO.getRoles().isEmpty()) {
if (CollUtil.isNotEmpty(clerkAddDTO.getRoles()) && !clerkAddDTO.getRoles().isEmpty()) {
this.roleIds = CharSequenceUtil.join(",", clerkAddDTO.getRoles());
}
this.memberId = clerkAddDTO.getMemberId();

View File

@@ -102,8 +102,18 @@ public class MemberEvaluationServiceImpl extends ServiceImpl<MemberEvaluationMap
public MemberEvaluationDTO addMemberEvaluation(MemberEvaluationDTO memberEvaluationDTO, Boolean isSelf) {
//获取子订单信息
OrderItem orderItem = orderItemService.getBySn(memberEvaluationDTO.getOrderItemSn());
if (orderItem == null) {
throw new ServiceException(ResultCode.ORDER_ITEM_NOT_EXIST);
}
//获取订单信息
Order order = orderService.getBySn(orderItem.getOrderSn());
if (order == null) {
throw new ServiceException(ResultCode.ORDER_NOT_EXIST);
}
//检测是否可以添加会员评价
Member member;
@@ -119,8 +129,14 @@ public class MemberEvaluationServiceImpl extends ServiceImpl<MemberEvaluationMap
throw new ServiceException(ResultCode.USER_NOT_EXIST);
}
}
//获取商品信息
GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(memberEvaluationDTO.getSkuId());
if (goodsSku == null) {
throw new ServiceException(ResultCode.GOODS_NOT_EXIST);
}
//新增用户评价
MemberEvaluation memberEvaluation = new MemberEvaluation(memberEvaluationDTO, goodsSku, member, order);
//过滤商品咨询敏感词

View File

@@ -118,7 +118,11 @@ public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> impleme
public Member getUserInfo() {
AuthUser tokenUser = UserContext.getCurrentUser();
if (tokenUser != null) {
return this.findByUsername(tokenUser.getUsername());
Member member = this.findByUsername(tokenUser.getUsername());
if(member != null && !member.getDisabled()){
throw new ServiceException(ResultCode.USER_STATUS_ERROR);
}
return member;
}
throw new ServiceException(ResultCode.USER_NOT_LOGIN);
}
@@ -331,6 +335,7 @@ public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> impleme
}
@DemoSite
@Override
public Member modifyPass(String oldPassword, String newPassword) {
AuthUser tokenUser = UserContext.getCurrentUser();
if (tokenUser == null) {

View File

@@ -0,0 +1,33 @@
package cn.lili.modules.order.aftersale.entity.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 售后数量VO
*
* @author Bulbasaur
* @since 2021/3/12 10:32 上午
*/
@Data
public class AfterSaleNumVO {
@ApiModelProperty(value = "申请中售后数量")
private Integer applyNum;
@ApiModelProperty(value = "已通过售后数量")
private Integer passNum;
@ApiModelProperty(value = "已拒绝售后数量")
private Integer refuseNum;
@ApiModelProperty(value = "待卖家收货售后数量")
private Integer buyerReturnNum;
@ApiModelProperty(value = "卖家确认收货售后数量")
private Integer sellerConfirmNum;
@ApiModelProperty(value = "卖家终止售后售后数量")
private Integer sellerTerminationNum;
@ApiModelProperty(value = "买家取消售后售后数量")
private Integer buyerCancelNum;
@ApiModelProperty(value = "等待平台退款售后数量")
private Integer waitRefundNum;
@ApiModelProperty(value = "已完成售后数量")
private Integer completeNum;
}

View File

@@ -86,20 +86,23 @@ public class AfterSaleSearchParams extends PageVO {
if (CharSequenceUtil.isNotEmpty(orderSn)) {
queryWrapper.like("order_sn", orderSn);
}
//按买家查询
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MEMBER.name())) {
queryWrapper.eq("member_id", UserContext.getCurrentUser().getId());
}
//按卖家查询
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name())) {
queryWrapper.eq("store_id", UserContext.getCurrentUser().getStoreId());
if(UserContext.getCurrentUser() != null){
//按买家查询
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MEMBER.name())) {
queryWrapper.eq("member_id", UserContext.getCurrentUser().getId());
}
//按卖家查询
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name())) {
queryWrapper.eq("store_id", UserContext.getCurrentUser().getStoreId());
}
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MANAGER.name())
&& CharSequenceUtil.isNotEmpty(storeId)
) {
queryWrapper.eq("store_id", storeId);
}
}
if (CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.MANAGER.name())
&& CharSequenceUtil.isNotEmpty(storeId)
) {
queryWrapper.eq("store_id", storeId);
}
if (CharSequenceUtil.isNotEmpty(memberName)) {
queryWrapper.like("member_name", memberName);
}

View File

@@ -4,6 +4,7 @@ package cn.lili.modules.order.aftersale.service;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.aftersale.entity.dto.AfterSaleDTO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleApplyVO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleNumVO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleSearchParams;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleVO;
import cn.lili.modules.store.entity.dto.StoreAfterSaleAddressDTO;
@@ -30,6 +31,9 @@ public interface AfterSaleService extends IService<AfterSale> {
*/
IPage<AfterSaleVO> getAfterSalePages(AfterSaleSearchParams saleSearchParams);
AfterSaleNumVO getAfterSaleNumVO(AfterSaleSearchParams saleSearchParams);
/**
* 查询导出售后信息
*

View File

@@ -2,7 +2,6 @@ package cn.lili.modules.order.aftersale.serviceimpl;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.event.TransactionCommitSendMQEvent;
@@ -18,6 +17,7 @@ import cn.lili.modules.order.aftersale.aop.AfterSaleLogPoint;
import cn.lili.modules.order.aftersale.entity.dos.AfterSale;
import cn.lili.modules.order.aftersale.entity.dto.AfterSaleDTO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleApplyVO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleNumVO;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleSearchParams;
import cn.lili.modules.order.aftersale.entity.vo.AfterSaleVO;
import cn.lili.modules.order.aftersale.mapper.AfterSaleMapper;
@@ -39,16 +39,13 @@ import cn.lili.modules.system.entity.dos.Logistics;
import cn.lili.modules.system.entity.vo.Traces;
import cn.lili.modules.system.service.LogisticsService;
import cn.lili.mybatis.util.PageUtil;
import cn.lili.rocketmq.RocketmqSendCallbackBuilder;
import cn.lili.rocketmq.tags.AfterSaleTagsEnum;
import cn.lili.rocketmq.tags.OrderTagsEnum;
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 org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@@ -56,6 +53,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -98,11 +96,6 @@ public class AfterSaleServiceImpl extends ServiceImpl<AfterSaleMapper, AfterSale
*/
@Autowired
private RocketmqCustomProperties rocketmqCustomProperties;
/**
* RocketMQ
*/
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@@ -112,6 +105,55 @@ public class AfterSaleServiceImpl extends ServiceImpl<AfterSaleMapper, AfterSale
return baseMapper.queryByParams(PageUtil.initPage(saleSearchParams), saleSearchParams.queryWrapper());
}
@Override
public AfterSaleNumVO getAfterSaleNumVO(AfterSaleSearchParams saleSearchParams) {
AfterSaleNumVO afterSaleNumVO = new AfterSaleNumVO();
// 获取基础查询条件
QueryWrapper<AfterSale> baseWrapper = saleSearchParams.queryWrapper();
// 使用聚合查询一次性获取所有状态的售后数量
List<Map<String, Object>> results = this.baseMapper.selectMaps(
baseWrapper.select(
"COUNT(CASE WHEN service_status = 'APPLY' THEN 1 END) as applyNum",
"COUNT(CASE WHEN service_status = 'PASS' THEN 1 END) as passNum",
"COUNT(CASE WHEN service_status = 'REFUSE' THEN 1 END) as refuseNum",
"COUNT(CASE WHEN service_status = 'BUYER_RETURN' THEN 1 END) as buyerReturnNum",
"COUNT(CASE WHEN service_status = 'SELLER_CONFIRM' THEN 1 END) as sellerConfirmNum",
"COUNT(CASE WHEN service_status = 'SELLER_TERMINATION' THEN 1 END) as sellerTerminationNum",
"COUNT(CASE WHEN service_status = 'BUYER_CANCEL' THEN 1 END) as buyerCancelNum",
"COUNT(CASE WHEN service_status = 'WAIT_REFUND' THEN 1 END) as waitRefundNum",
"COUNT(CASE WHEN service_status = 'COMPLETE' THEN 1 END) as completeNum"
)
);
if (!results.isEmpty()) {
Map<String, Object> result = results.get(0);
afterSaleNumVO.setApplyNum(((Number) result.get("applyNum")).intValue());
afterSaleNumVO.setPassNum(((Number) result.get("passNum")).intValue());
afterSaleNumVO.setRefuseNum(((Number) result.get("refuseNum")).intValue());
afterSaleNumVO.setBuyerReturnNum(((Number) result.get("buyerReturnNum")).intValue());
afterSaleNumVO.setSellerConfirmNum(((Number) result.get("sellerConfirmNum")).intValue());
afterSaleNumVO.setSellerTerminationNum(((Number) result.get("sellerTerminationNum")).intValue());
afterSaleNumVO.setBuyerCancelNum(((Number) result.get("buyerCancelNum")).intValue());
afterSaleNumVO.setWaitRefundNum(((Number) result.get("waitRefundNum")).intValue());
afterSaleNumVO.setCompleteNum(((Number) result.get("completeNum")).intValue());
} else {
// 如果没有结果设置默认值为0
afterSaleNumVO.setApplyNum(0);
afterSaleNumVO.setPassNum(0);
afterSaleNumVO.setRefuseNum(0);
afterSaleNumVO.setBuyerReturnNum(0);
afterSaleNumVO.setSellerConfirmNum(0);
afterSaleNumVO.setSellerTerminationNum(0);
afterSaleNumVO.setBuyerCancelNum(0);
afterSaleNumVO.setWaitRefundNum(0);
afterSaleNumVO.setCompleteNum(0);
}
return afterSaleNumVO;
}
@Override
public List<AfterSale> exportAfterSaleOrder(AfterSaleSearchParams saleSearchParams) {
return this.list(saleSearchParams.queryWrapper());

View File

@@ -19,7 +19,11 @@ public enum DeliveryMethodEnum {
/**
* "物流"
*/
LOGISTICS("物流");
LOGISTICS("物流"),
/**
* 虚拟发货
*/
VIRTUAL("虚拟发货");
private final String description;

View File

@@ -212,14 +212,27 @@ public class CouponRender implements CartRenderStep {
MemberCouponDTO platformCoupon = tradeDTO.getPlatformCoupon();
//如果有勾选平台优惠券
if (platformCoupon != null) {
renderSku(tradeDTO, platformCoupon);
//判断该优惠券是否可以使用,如果可以进行价格渲染,如果不可以使用,去掉该优惠券的使用
boolean checkFlag = tradeDTO.getCanUseCoupons().stream().anyMatch(item -> item.getCouponId().equals(platformCoupon.getMemberCoupon().getCouponId()));
if(checkFlag){
renderSku(tradeDTO, platformCoupon);
}else{
tradeDTO.setPlatformCoupon(null);
}
}
//计算商家优惠券
Map<String, MemberCouponDTO> map = tradeDTO.getStoreCoupons();
if (map != null && map.size() > 0) {
for (MemberCouponDTO memberCouponDTO : map.values()) {
renderSku(tradeDTO, memberCouponDTO);
//判断该优惠券是否可以使用,如果可以进行价格渲染,如果不可以使用,去掉该优惠券的使用
boolean storeCouponCheck = tradeDTO.getCanUseCoupons().stream().anyMatch(item -> item.getCouponId().equals(memberCouponDTO.getMemberCoupon().getCouponId()));
if(storeCouponCheck){
renderSku(tradeDTO, memberCouponDTO);
}else{
map.values().remove(memberCouponDTO);
}
}
tradeDTO.setStoreCoupons(map);
}
}

View File

@@ -141,6 +141,7 @@ public class SkuPromotionRender implements CartRenderStep {
KanjiaActivitySearchParams kanjiaActivitySearchParams = new KanjiaActivitySearchParams();
kanjiaActivitySearchParams.setGoodsSkuId(cartSkuVO.getGoodsSku().getId());
kanjiaActivitySearchParams.setMemberId(Objects.requireNonNull(UserContext.getCurrentUser()).getId());
kanjiaActivitySearchParams.setKanjiaActivityId(cartSkuVO.getKanjiaId());
kanjiaActivitySearchParams.setStatus(KanJiaStatusEnum.SUCCESS.name());
KanjiaActivityVO kanjiaActivityVO = kanjiaActivityService.getKanjiaActivityVO(kanjiaActivitySearchParams);
//可以砍价金额购买,则处理信息
@@ -266,8 +267,8 @@ public class SkuPromotionRender implements CartRenderStep {
}
}
if (quantity != null && cartSkuVO.getNum() > (Integer) quantity) {//设置购物车未选中
//设置购物车未选中
if (quantity != null && cartSkuVO.getNum() > (Integer) quantity) {
cartSkuVO.setChecked(false);
//设置失效消息
cartSkuVO.setErrorMessage("促销商品库存不足,现有库存数量[" + quantity + "]");

View File

@@ -638,18 +638,20 @@ public class CartServiceImpl implements CartService {
if (Boolean.FALSE.equals(cartSkuVO.getChecked())) {
continue;
}
//使用优惠券时判断最新的sku价格。与后面渲染一致
GoodsSku goodsSku = goodsSkuService.getGoodsSkuByIdFromCache(cartSkuVO.getGoodsSku().getId());
//有促销金额则用促销金额,否则用商品原价
if (cartSkuVO.getPromotionMap() != null && !cartSkuVO.getPromotionMap().isEmpty()) {
if (cartSkuVO.getPromotionMap().keySet().stream().anyMatch(i -> i.contains(PromotionTypeEnum.PINTUAN.name()) || i.contains(PromotionTypeEnum.SECKILL.name()))) {
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(cartSkuVO.getPurchasePrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(cartSkuVO.getPurchasePrice(), cartSkuVO.getNum()));
} else {
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(cartSkuVO.getGoodsSku().getPrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(cartSkuVO.getGoodsSku().getPrice(), cartSkuVO.getNum()));
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(goodsSku.getPrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(goodsSku.getPrice(), cartSkuVO.getNum()));
}
} else {
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(cartSkuVO.getGoodsSku().getPrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(cartSkuVO.getGoodsSku().getPrice(), cartSkuVO.getNum()));
cartPrice = CurrencyUtil.add(cartPrice, CurrencyUtil.mul(goodsSku.getPrice(), cartSkuVO.getNum()));
skuPrice.put(cartSkuVO.getGoodsSku().getId(), CurrencyUtil.mul(goodsSku.getPrice(), cartSkuVO.getNum()));
}
}

View File

@@ -63,9 +63,6 @@ public class OrderSearchParams extends PageVO {
@ApiModelProperty(value = "关键字 商品名称/买家名称/店铺名称")
private String keywords;
@ApiModelProperty(value = "付款方式")
private String paymentType;
/**
* @see OrderTypeEnum
* @see cn.lili.modules.order.order.entity.enums.OrderPromotionTypeEnum
@@ -121,7 +118,11 @@ public class OrderSearchParams extends PageVO {
//关键字查询
if (CharSequenceUtil.isNotEmpty(keywords)) {
wrapper.and(keyWrapper -> keyWrapper.like("o.sn", keywords).or().like("oi.goods_name", keywords));
wrapper.and(keyWrapper -> keyWrapper.like("o.sn", keywords).or()
.like("oi.goods_name", keywords).or()
.like("o.consignee_name", keywords).or()
.like("o.consignee_mobile", keywords).or()
.like("o.store_name", keywords));
}
if (currentUser != null) {
//按卖家查询
@@ -157,9 +158,6 @@ public class OrderSearchParams extends PageVO {
//按商品名称查询
wrapper.like(CharSequenceUtil.isNotEmpty(goodsName), "oi.goods_name", goodsName);
//付款方式
wrapper.like(CharSequenceUtil.isNotEmpty(paymentType), "o.payment_type", paymentType);
//按支付方式
wrapper.eq(CharSequenceUtil.isNotEmpty(paymentMethod), "o.payment_method", paymentMethod);

View File

@@ -12,6 +12,7 @@ public enum DeliverStatusEnum {
* 发货状态
*/
UNDELIVERED("未发货"),
PARTS_DELIVERED("部分发货"),
DELIVERED("已发货"),
RECEIVED("已收货");

View File

@@ -14,6 +14,7 @@ public enum OrderStatusEnum {
UNPAID("未付款"),
PAID("已付款"),
UNDELIVERED("待发货"),
PARTS_DELIVERED("部分发货"),
DELIVERED("已发货"),
COMPLETED("已完成"),
STAY_PICKED_UP("待自提"),

View File

@@ -73,18 +73,18 @@ public class AllowOperation implements Serializable {
//可编辑订单收件人信息=实物订单 && 订单未发货 && 订单未取消 && 订单不是自提
this.editConsignee = order.getOrderType().equals(OrderTypeEnum.NORMAL.name())
&& order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name())
&& (order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()) || order.getDeliverStatus().equals(DeliverStatusEnum.PARTS_DELIVERED.name()))
&& !status.equals(OrderStatusEnum.CANCELLED.name())
&& !order.getDeliveryMethod().equals(DeliveryMethodEnum.SELF_PICK_UP.name());
//是否允许被发货
this.ship = editConsignee && status.equals(OrderStatusEnum.UNDELIVERED.name());
this.ship = editConsignee && (status.equals(OrderStatusEnum.UNDELIVERED.name()) || order.getDeliverStatus().equals(DeliverStatusEnum.PARTS_DELIVERED.name()));
//是否允许被收货
this.rog = status.equals(OrderStatusEnum.DELIVERED.name());
//是否允许查看物流信息
this.showLogistics = order.getDeliverStatus().equals(DeliverStatusEnum.DELIVERED.name()) && status.equals(OrderStatusEnum.DELIVERED.name());
this.showLogistics = (order.getDeliverStatus().equals(DeliverStatusEnum.DELIVERED.name()) || order.getDeliverStatus().equals(DeliverStatusEnum.PARTS_DELIVERED.name())) && (status.equals(OrderStatusEnum.DELIVERED.name()) || status.equals(OrderStatusEnum.PARTS_DELIVERED.name()));
//虚拟订单 或 自提订单可以核销
this.take =

View File

@@ -0,0 +1,27 @@
package cn.lili.modules.order.order.entity.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class OrderNumVO {
@ApiModelProperty(value = "未付款订单数量")
private Integer waitPayNum;
@ApiModelProperty(value = "已付款订单数量")
private Integer waitDeliveryNum;
@ApiModelProperty(value = "待发货订单数量")
private Integer waitShipNum;
@ApiModelProperty(value = "部分发货订单数量")
private Integer partsDeliveredNumNum;
@ApiModelProperty(value = "待收货订单数量")
private Integer deliveredNum;
@ApiModelProperty(value = "待核验订单数量")
private Integer waitCheckNum;
@ApiModelProperty(value = "待自提订单数量")
private Integer waitSelfPickNum;
@ApiModelProperty(value = "已完成订单数量")
private Integer finishNum;
@ApiModelProperty(value = "已关闭订单数量")
private Integer closeNum;
}

View File

@@ -56,6 +56,9 @@ public class OrderSimpleVO {
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date paymentTime;
@ApiModelProperty(value = "用户ID")
private String memberId;
@ApiModelProperty(value = "用户名")
@Sensitive(strategy = SensitiveStrategy.PHONE)
private String memberName;

View File

@@ -2,6 +2,7 @@ package cn.lili.modules.order.order.mapper;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.dto.OrderExportDTO;
import cn.lili.modules.order.order.entity.vo.OrderNumVO;
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;
@@ -80,7 +81,7 @@ public interface OrderMapper extends BaseMapper<Order> {
* @param queryWrapper 查询条件
* @return 简短订单分页
*/
@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 " +
@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.member_id,o.store_name as " +
"store_name,o.store_id as store_id,o.client_type,o.order_type,o.deliver_status,o.order_promotion_type,o.seller_remark " +
",GROUP_CONCAT(oi.goods_id) as group_goods_id," +
" GROUP_CONCAT(oi.sku_id) as group_sku_id," +
@@ -97,6 +98,17 @@ public interface OrderMapper extends BaseMapper<Order> {
" FROM li_order o LEFT JOIN li_order_item AS oi on o.sn = oi.order_sn ${ew.customSqlSegment} ")
IPage<OrderSimpleVO> queryByParams(IPage<OrderSimpleVO> page, @Param(Constants.WRAPPER) Wrapper<OrderSimpleVO> queryWrapper);
@Select("select COUNT(CASE WHEN order_status = 'UNPAID' THEN 1 END) as waitPayNum,"+
"COUNT(CASE WHEN order_status = 'PAID' THEN 1 END) as waitDeliveryNum,"+
"COUNT(CASE WHEN order_status = 'UNDELIVERED' THEN 1 END) as waitShipNum,"+
"COUNT(CASE WHEN order_status = 'PARTS_DELIVERED' THEN 1 END) as partsDeliveredNumNum,"+
"COUNT(CASE WHEN order_status = 'DELIVERED' THEN 1 END) as deliveredNum,"+
"COUNT(CASE WHEN order_status = 'TAKE' THEN 1 END) as waitCheckNum,"+
"COUNT(CASE WHEN order_status = 'STAY_PICKED_UP' THEN 1 END) as waitSelfPickNum,"+
"COUNT(CASE WHEN order_status = 'COMPLETED' THEN 1 END) as finishNum,"+
"COUNT(CASE WHEN order_status = 'CANCELLED' THEN 1 END) as closeNum "+
" FROM li_order o LEFT JOIN li_order_item AS oi on o.sn = oi.order_sn ${ew.customSqlSegment} ")
OrderNumVO getOrderNumVO(@Param(Constants.WRAPPER) Wrapper<OrderSimpleVO> queryWrapper);
/**
* 查询订单信息
*

View File

@@ -3,11 +3,11 @@ package cn.lili.modules.order.order.service;
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.OrderExportDTO;
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.PartDeliveryParamsDTO;
import cn.lili.modules.order.order.entity.vo.OrderDetailVO;
import cn.lili.modules.order.order.entity.vo.OrderNumVO;
import cn.lili.modules.order.order.entity.vo.OrderSimpleVO;
import cn.lili.modules.order.order.entity.vo.PaymentLog;
import cn.lili.modules.system.entity.vo.Traces;
@@ -54,6 +54,14 @@ public interface OrderService extends IService<Order> {
*/
IPage<OrderSimpleVO> queryByParams(OrderSearchParams orderSearchParams);
/**
* 获取订单数量
*
* @param orderSearchParams 查询参数
* @return 订单数量
*/
OrderNumVO getOrderNumVO(OrderSearchParams orderSearchParams);
/**
* 订单信息
*

View File

@@ -54,8 +54,8 @@ public class OrderPackageServiceImpl extends ServiceImpl<OrderPackageMapper, Ord
orderPackageVO.setOrderPackageItemList(orderPackageItemList);
String str = orderPackage.getConsigneeMobile();
str = str.substring(str.length() - 4);
// Traces traces = logisticsService.getLogisticTrack(orderPackage.getLogisticsCode(), orderPackage.getLogisticsNo(), str);
// orderPackageVO.setTraces(traces);
Traces traces = logisticsService.getLogisticTrack(orderPackage.getLogisticsCode(), orderPackage.getLogisticsNo(), str);
orderPackageVO.setTraces(traces);
orderPackageVOS.add(orderPackageVO);
});

View File

@@ -28,10 +28,7 @@ import cn.lili.modules.order.order.aop.OrderLogPoint;
import cn.lili.modules.order.order.entity.dos.*;
import cn.lili.modules.order.order.entity.dto.*;
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.entity.vo.PaymentLog;
import cn.lili.modules.order.order.entity.vo.*;
import cn.lili.modules.order.order.mapper.OrderMapper;
import cn.lili.modules.order.order.service.*;
import cn.lili.modules.order.trade.entity.dos.OrderLog;
@@ -224,6 +221,11 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
return this.baseMapper.queryByParams(PageUtil.initPage(orderSearchParams), queryWrapper);
}
@Override
public OrderNumVO getOrderNumVO(OrderSearchParams orderSearchParams) {
return this.baseMapper.getOrderNumVO(orderSearchParams.queryWrapper());
}
/**
* 订单信息
*
@@ -351,6 +353,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
order.setCancelReason(reason);
//修改订单
this.updateById(order);
//订单货物设置全部退款
orderItemService.update(new LambdaUpdateWrapper<OrderItem>().eq(OrderItem::getOrderSn,orderSn).set(OrderItem::getIsRefund,RefundStatusEnum.ALL_REFUND.name()));
//生成店铺退款流水
storeFlowService.orderCancel(orderSn);
//发送消息
@@ -370,6 +374,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
order.setOrderStatus(OrderStatusEnum.CANCELLED.name());
order.setCancelReason(reason);
this.updateById(order);
//订单货物设置全部退款
orderItemService.update(new LambdaUpdateWrapper<OrderItem>().eq(OrderItem::getOrderSn,orderSn).set(OrderItem::getIsRefund,RefundStatusEnum.ALL_REFUND.name()));
if (refundMoney) {
//生成店铺退款流水
storeFlowService.orderCancel(orderSn);
@@ -470,7 +476,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
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())) {
if ((order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()) || order.getDeliverStatus().equals(DeliverStatusEnum.PARTS_DELIVERED.name())) &&
(order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name()) || order.getOrderStatus().equals(OrderStatusEnum.PARTS_DELIVERED.name()))) {
//获取对应物流
Logistics logistics = logisticsService.getById(logisticsId);
if (logistics == null) {
@@ -779,7 +786,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
*/
private void checkBatchDeliver(List<OrderBatchDeliverDTO> list) {
List<Logistics> logistics = logisticsService.list();
Map<String, String> logisticsMap = logisticsService.list().stream()
.collect(Collectors.toMap(Logistics::getName, Logistics::getId));
for (OrderBatchDeliverDTO orderBatchDeliverDTO : list) {
//查看订单号是否存在-是否是当前店铺的订单
Order order = this.getOne(new LambdaQueryWrapper<Order>()
@@ -791,11 +799,10 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
throw new ServiceException("订单编号:'" + orderBatchDeliverDTO.getOrderSn() + " '不能发货");
}
//获取物流公司
logistics.forEach(item -> {
if (item.getName().equals(orderBatchDeliverDTO.getLogisticsName())) {
orderBatchDeliverDTO.setLogisticsId(item.getId());
}
});
String logisticsId = logisticsMap.get(orderBatchDeliverDTO.getLogisticsName());
if (logisticsId != null) {
orderBatchDeliverDTO.setLogisticsId(logisticsId);
}
if (CharSequenceUtil.isEmpty(orderBatchDeliverDTO.getLogisticsId())) {
throw new ServiceException("物流公司:'" + orderBatchDeliverDTO.getLogisticsName() + " '不存在");
}
@@ -841,6 +848,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
}
@Override
@Transactional(rollbackFor = Exception.class)
public Order partDelivery(PartDeliveryParamsDTO partDeliveryParamsDTO) {
String logisticsId = partDeliveryParamsDTO.getLogisticsId();
String orderSn = partDeliveryParamsDTO.getOrderSn();
@@ -858,7 +866,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
orderPackage.setPackageNo(SnowFlake.createStr("OP"));
orderPackage.setOrderSn(orderSn);
orderPackage.setLogisticsNo(invoiceNumber);
orderPackage.setLogisticsCode(logistics.getCode());
orderPackage.setLogisticsCode(logistics.getId());
orderPackage.setLogisticsName(logistics.getName());
orderPackage.setStatus("1");
orderPackage.setConsigneeMobile(order.getConsigneeMobile());
@@ -872,7 +880,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
throw new ServiceException("发货数量不正确!");
}
orderItem.setDeliverNumber((partDeliveryDTO.getDeliveryNum() + orderItem.getDeliverNumber()));
// 记录分包裹中每个item子单的具体发货信息
OrderPackageItem orderPackageItem = new OrderPackageItem();
orderPackageItem.setOrderSn(orderSn);
@@ -906,6 +913,11 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
//是否全部发货
if (delivery) {
return delivery(orderSn, invoiceNumber, logisticsId);
}else if(order.getDeliverStatus().equals(DeliverStatusEnum.UNDELIVERED.name()) || order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name())){
//更改订单状态为部分发货
order.setDeliverStatus(DeliverStatusEnum.PARTS_DELIVERED.name());
order.setOrderStatus(OrderStatusEnum.PARTS_DELIVERED.name());
this.updateById(order);
}
return order;
}
@@ -1278,13 +1290,13 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
row.createCell(2).setCellValue(dto.getGoodsName());
row.createCell(3).setCellValue(dto.getNum());
row.createCell(4).setCellValue(dto.getGoodsId());
row.createCell(5).setCellValue(dto.getUnitPrice()!=null?dto.getUnitPrice():0);
row.createCell(6).setCellValue(dto.getFlowPrice()!=null?dto.getFlowPrice():0);
row.createCell(7).setCellValue(dto.getFreightPrice()!=null?dto.getFreightPrice():0);
row.createCell(8).setCellValue(dto.getDiscountPrice()!=null?dto.getDiscountPrice():0);
row.createCell(9).setCellValue(dto.getSiteMarketingCost()!=null?dto.getSiteMarketingCost():0);
row.createCell(10).setCellValue(dto.getStoreMarketingCost()!=null?dto.getStoreMarketingCost():0);
row.createCell(11).setCellValue(dto.getUpdatePrice()!=null?dto.getUpdatePrice():0);
row.createCell(5).setCellValue(Objects.nonNull(dto.getUnitPrice())?dto.getUnitPrice():0);
row.createCell(6).setCellValue(Objects.nonNull(dto.getFlowPrice())?dto.getFlowPrice():0);
row.createCell(7).setCellValue(Objects.nonNull(dto.getFreightPrice())?dto.getFreightPrice():0);
row.createCell(8).setCellValue(Objects.nonNull(dto.getDiscountPrice())?dto.getDiscountPrice():0);
row.createCell(9).setCellValue(Objects.nonNull(dto.getSiteMarketingCost())?dto.getSiteMarketingCost():0);
row.createCell(10).setCellValue(Objects.nonNull(dto.getStoreMarketingCost())?dto.getStoreMarketingCost():0);
row.createCell(11).setCellValue(Objects.nonNull(dto.getUpdatePrice())?dto.getUpdatePrice():0);
row.createCell(12).setCellValue(dto.getPaymentMethod());
row.createCell(13).setCellValue(dto.getConsigneeName());
row.createCell(14).setCellValue(dto.getConsigneeMobile());

View File

@@ -313,16 +313,34 @@ public class PayKit {
* v3 接口创建签名
*
* @param signMessage 待签名的参数
* @param keyPath key.pem 证书路径
* @param key key.pem 证书
* @return 生成 v3 签名
* @throws Exception 异常信息
*/
public static String createSign(String signMessage, String keyPath) throws Exception {
public static String createSign(String signMessage, String key) throws Exception {
if (StrUtil.isEmpty(signMessage)) {
return null;
}
//获取商户私钥
PrivateKey privateKey = PayKit.getPrivateKey(keyPath);
PrivateKey privateKey = PayKit.getPrivateKey(key);
//生成签名
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
}
/**
* v3 接口创建签名
*
* @param signMessage 待签名的参数
* @param key key.pem 证书
* @return 生成 v3 签名
* @throws Exception 异常信息
*/
public static String createPublicSign(String signMessage, String key) throws Exception {
if (StrUtil.isEmpty(signMessage)) {
return null;
}
//获取商户私钥
PrivateKey privateKey = PayKit.getPublicKey(key);
//生成签名
return RsaKit.encryptByPrivateKey(signMessage, privateKey);
}
@@ -378,13 +396,13 @@ public class PayKit {
/**
* 获取商户私钥
*
* @param keyPath 商户私钥证书路径
* @param key 商户私钥证书
* @return {@link PrivateKey} 商户私钥
* @throws Exception 异常信息
*/
public static PrivateKey getPrivateKey(String keyPath) throws Exception {
String originalKey = FileUtil.readUtf8String(keyPath);
String privateKey = originalKey
public static PrivateKey getPrivateKey(String key) throws Exception {
// String originalKey = FileUtil.readUtf8String(keyPath);
String privateKey = key
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
@@ -392,6 +410,23 @@ public class PayKit {
return RsaKit.loadPrivateKey(privateKey);
}
/**
* 获取商户公钥
*
* @param key 商户私钥证书
* @return {@link PrivateKey} 商户私钥
* @throws Exception 异常信息
*/
public static PrivateKey getPublicKey(String key) throws Exception {
// String originalKey = FileUtil.readUtf8String(keyPath);
String privateKey = key
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s+", "");
return RsaKit.loadPrivateKey(privateKey);
}
/**
* 获取证书
*

View File

@@ -312,13 +312,18 @@ public class RsaKit {
*/
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) {
e.printStackTrace();
throw new Exception("私钥非法");
} catch (NullPointerException e) {
throw new Exception("私钥数据为空");

View File

@@ -449,7 +449,8 @@ public class WxPayKit {
* @param urlSuffix 可通过 WxApiType 来获取URL挂载参数需要自行拼接
* @param mchId 商户Id
* @param serialNo 商户 API 证书序列号
* @param keyPath key.pem 证书路径
* @param key key.pem 证书
* @param publicKey 公钥证书
* @param body 接口请求参数
* @param nonceStr 随机字符库
* @param timestamp 时间戳
@@ -458,11 +459,12 @@ public class WxPayKit {
* @throws Exception 异常信息
*/
public static String buildAuthorization(RequestMethodEnums method, String urlSuffix, String mchId,
String serialNo, String keyPath, String body, String nonceStr,
String serialNo, String key, String publicKey ,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);
String publicKeySignature = PayKit.createPublicSign(buildSignMessage, publicKey);
String signature = PayKit.createSign(publicKeySignature, key);
//根据平台规则生成请求头 authorization
return PayKit.getAuthorization(mchId, serialNo, nonceStr, String.valueOf(timestamp), signature, authType);
}
@@ -492,27 +494,27 @@ public class WxPayKit {
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 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

View File

@@ -1,13 +1,10 @@
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.context.ThreadContextHolder;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.ApiProperties;
import cn.lili.common.properties.DomainProperties;
import cn.lili.common.utils.BeanUtil;
import cn.lili.common.utils.SnowFlake;
@@ -40,7 +37,6 @@ import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
@@ -90,13 +86,14 @@ public class AliPayPlugin implements Payment {
payModel.setBody(cashierParam.getTitle());
payModel.setSubject(cashierParam.getDetail());
payModel.setTotalAmount(cashierParam.getPrice() + "");
//回传数据
payModel.setPassbackParams(URLEncoder.createAll().encode(BeanUtil.formatKeyValuePair(payParam), StandardCharsets.UTF_8));
//3分钟超时
payModel.setTimeoutExpress("3m");
payModel.setOutTradeNo(outTradeNo);
payModel.setProductCode("QUICK_WAP_PAY");
try {
// Passback params moved into try to handle checked exception
payModel.setPassbackParams(java.net.URLEncoder.encode(BeanUtil.formatKeyValuePair(payParam), "UTF-8"));
log.info("支付宝H5支付{}", JSONUtil.toJsonStr(payModel));
AliPayRequest.wapPay(response, payModel, callbackUrl(alipayPaymentSetting.getCallbackUrl(), PaymentMethodEnum.ALIPAY),
notifyUrl(alipayPaymentSetting.getCallbackUrl(), PaymentMethodEnum.ALIPAY));
@@ -130,8 +127,8 @@ public class AliPayPlugin implements Payment {
//3分钟超时
payModel.setTimeoutExpress("3m");
//回传数据
payModel.setPassbackParams(URLEncoder.createAll().encode(BeanUtil.formatKeyValuePair(payParam), StandardCharsets.UTF_8));
//回传数据(替换 Hutool 的 URLEncoder 为 JDK 的 java.net.URLEncoder
payModel.setPassbackParams(java.net.URLEncoder.encode(BeanUtil.formatKeyValuePair(payParam), "UTF-8"));
payModel.setOutTradeNo(outTradeNo);
payModel.setProductCode("QUICK_MSECURITY_PAY");
@@ -164,8 +161,8 @@ public class AliPayPlugin implements Payment {
payModel.setSubject(cashierParam.getDetail());
payModel.setTotalAmount(cashierParam.getPrice() + "");
//回传数据
payModel.setPassbackParams(URLEncoder.createAll().encode(BeanUtil.formatKeyValuePair(payParam), StandardCharsets.UTF_8));
//回传数据(替换 Hutool 的 URLEncoder 为 JDK 的 java.net.URLEncoder
payModel.setPassbackParams(java.net.URLEncoder.encode(BeanUtil.formatKeyValuePair(payParam), "UTF-8"));
payModel.setTimeoutExpress("3m");
payModel.setOutTradeNo(outTradeNo);
log.info("支付宝扫码:{}", payModel);
@@ -314,7 +311,8 @@ public class AliPayPlugin implements Payment {
return;
}
String payParamStr = map.get("passback_params");
String payParamJson = URLDecoder.decode(payParamStr, StandardCharsets.UTF_8);
// java.net.URLDecoder.decode throws UnsupportedEncodingException, add catch below
String payParamJson = java.net.URLDecoder.decode(payParamStr, "UTF-8");
PayParam payParam = BeanUtil.formatKeyValuePair(payParamJson, new PayParam());
@@ -331,6 +329,8 @@ public class AliPayPlugin implements Payment {
}
} catch (AlipayApiException e) {
log.error("支付回调通知异常", e);
} catch (java.io.UnsupportedEncodingException e) {
log.error("URL 解码异常", e);
}
}

View File

@@ -1,6 +1,5 @@
package cn.lili.modules.payment.kit.plugin.unionpay;
import cn.hutool.core.net.URLEncoder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.common.enums.ResultCode;
@@ -67,7 +66,7 @@ public class UnionPayPlugin implements Payment {
String ip = IpKit.getRealIp(request);
//第三方付款订单
String outOrderNo = SnowFlake.getIdStr();
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
String attach = java.net.URLEncoder.encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8.name());
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.NATIVE.toString())
.mch_id(unionPaymentSetting.getUnionPayMachId())
@@ -94,12 +93,10 @@ public class UnionPayPlugin implements Payment {
@Override
public ResultMessage<Object> appPay(HttpServletRequest request, PayParam payParam) {
try {
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting();
String notifyUrl = unionPaymentSetting.getUnionPayDomain().concat("/unionPay/payNotify");
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
String attach = java.net.URLEncoder.encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8.name());
//用户ip
String ip = IpKit.getRealIp(request);
Map<String, String> params = UnifiedOrderModel.builder()
@@ -135,9 +132,7 @@ public class UnionPayPlugin implements Payment {
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
return null;
}
}
@@ -147,10 +142,12 @@ public class UnionPayPlugin implements Payment {
String buyerId="";
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting();
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
// 将 attach 的编码移动到 try 内,避免方法外部的受检异常
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
//用户ip
String ip = IpKit.getRealIp(request);
try {
String attach = java.net.URLEncoder.encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8.name());
if (StrUtil.isEmpty(buyerLogonId) && StrUtil.isEmpty(buyerId)) {
log.error("buyer_logon_id buyer_id 不能同时为空");
return null;

View File

@@ -1,16 +1,10 @@
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.cache.Cache;
import cn.lili.cache.CachePrefix;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.enums.ResultUtil;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.ApiProperties;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.common.utils.SnowFlake;
@@ -26,17 +20,14 @@ import cn.lili.modules.payment.entity.RefundLog;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
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.kit.HttpKit;
import cn.lili.modules.payment.kit.core.kit.IpKit;
import cn.lili.modules.payment.kit.core.kit.WxPayKit;
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.params.dto.CashierParam;
import cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApiEnum;
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;
@@ -51,6 +42,31 @@ import cn.lili.modules.wallet.entity.dto.TransferResultDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.google.gson.Gson;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.h5.H5Service;
import com.wechat.pay.java.service.payments.h5.model.H5Info;
import com.wechat.pay.java.service.payments.h5.model.SceneInfo;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -58,12 +74,9 @@ 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.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 微信支付
@@ -120,10 +133,10 @@ public class WechatPlugin implements Payment {
//支付参数准备
SceneInfo sceneInfo = new SceneInfo();
sceneInfo.setPayer_client_ip(IpKit.getRealIp(request));
sceneInfo.setPayerClientIp(IpKit.getRealIp(request));
H5Info h5Info = new H5Info();
h5Info.setType("WAP");
sceneInfo.setH5_info(h5Info);
sceneInfo.setH5Info(h5Info);
//支付金额
Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
@@ -133,7 +146,7 @@ public class WechatPlugin implements Payment {
String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
//回传数据
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
String attach = java.net.URLEncoder.encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8.name());
WechatPaymentSetting setting = wechatPaymentSetting();
@@ -141,31 +154,33 @@ public class WechatPlugin implements Payment {
if (appid == null) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
.setAttach(attach)
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(),PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen)).setScene_info(sceneInfo);
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.H5_PAY.toString(),
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
JSONUtil.toJsonStr(unifiedOrderModel)
);
Config config = null;
if ("CERT".equals(setting.getPublicType())) {
config = this.getCertificateConfig(setting);
} else {
config = this.getPublicKeyConfig(setting);
}
// 构建service
H5Service service = new H5Service.Builder().config(config).build();
updateOrderPayNo(payParam,outOrderNo);
com.wechat.pay.java.service.payments.h5.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.h5.model.PrepayRequest();
com.wechat.pay.java.service.payments.h5.model.Amount amount = new com.wechat.pay.java.service.payments.h5.model.Amount();
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
prepayRequest.setSceneInfo(sceneInfo);
// 调用下单方法,得到应答
com.wechat.pay.java.service.payments.h5.model.PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam, outOrderNo);
return ResultUtil.data(JSONUtil.toJsonStr(response.getBody()));
return ResultUtil.data(response.getH5Url());
} catch (Exception e) {
log.error("微信H5支付错误", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@@ -183,8 +198,6 @@ public class WechatPlugin implements Payment {
return null;
}
Payer payer = new Payer();
payer.setOpenid(connect.getUnionId());
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
@@ -195,53 +208,46 @@ public class WechatPlugin implements Payment {
//过期时间
String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
// 将 Hutool URLEncoder 替换为标准库
String attach = java.net.URLEncoder.encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8.name());
WechatPaymentSetting setting = wechatPaymentSetting();
String appid = setting.getJsapiAppId();
if (appid == null) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
.setAttach(attach)
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen))
.setPayer(payer);
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.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<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
log.info("唤起支付参数:{}", map);
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(map);
Config config = null;
if ("CERT".equals(setting.getPublicType())) {
config = this.getCertificateConfig(setting);
} else {
config = this.getPublicKeyConfig(setting);
}
log.error("微信支付参数验证错误,请及时处理");
throw new ServiceException(ResultCode.PAY_ERROR);
// 构建service
JsapiService service = new JsapiService.Builder().config(config).build();
com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
com.wechat.pay.java.service.payments.jsapi.model.Payer payer = new com.wechat.pay.java.service.payments.jsapi.model.Payer();
payer.setOpenid(connect.getUnionId());
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
prepayRequest.setPayer(payer);
// 调用下单方法,得到应答
com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam, outOrderNo);
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, response.getPrepayId(), setting.getApiclientKey());
log.info("唤起支付参数:{}", map);
return ResultUtil.data(map);
} catch (Exception e) {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@@ -262,55 +268,47 @@ public class WechatPlugin implements Payment {
//过期时间
String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
// 将 Hutool URLEncoder 替换为标准库
String attach = java.net.URLEncoder.encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8.name());
WechatPaymentSetting setting = wechatPaymentSetting();
String appid = setting.getJsapiAppId();
if (appid == null) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
.setAttach(attach)
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen));
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.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<String, String> map = WxPayKit.appPrepayIdCreateSign(appid,
setting.getMchId(),
prepayId,
setting.getApiclient_key(), SignType.MD5);
log.info("唤起支付参数:{}", map);
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(map);
Config config = null;
if ("CERT".equals(setting.getPublicType())) {
config = this.getCertificateConfig(setting);
} else {
config = this.getPublicKeyConfig(setting);
}
log.error("微信支付参数验证错误,请及时处理");
throw new ServiceException(ResultCode.PAY_ERROR);
// 构建service
AppService service = new AppService.Builder().config(config).build();
com.wechat.pay.java.service.payments.app.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
// 调用下单方法,得到应答
com.wechat.pay.java.service.payments.app.model.PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam, outOrderNo);
Map<String, String> map = WxPayKit.appPrepayIdCreateSign(appid,
setting.getMchId(),
response.getPrepayId(),
setting.getApiclientKey(), SignType.MD5);
log.info("唤起支付参数:{}", map);
//修改付款单号
updateOrderPayNo(payParam, outOrderNo);
return ResultUtil.data(map);
} catch (Exception e) {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@@ -331,7 +329,8 @@ public class WechatPlugin implements Payment {
//过期时间
String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
// 将 Hutool URLEncoder 替换为标准库
String attach = java.net.URLEncoder.encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8.name());
WechatPaymentSetting setting = wechatPaymentSetting();
@@ -339,41 +338,33 @@ public class WechatPlugin implements Payment {
if (appid == null) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
//回传参数
.setAttach(attach)
.setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen));
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.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) {
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(new JSONObject(response.getBody()).getStr("code_url"));
Config config = null;
if ("CERT".equals(setting.getPublicType())) {
config = this.getCertificateConfig(setting);
} else {
log.error("微信支付参数验证错误,请及时处理");
throw new ServiceException(ResultCode.PAY_ERROR);
config = this.getPublicKeyConfig(setting);
}
// 构建service
NativePayService service = new NativePayService.Builder().config(config).build();
PrepayRequest prepayRequest = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
// 调用下单方法,得到应答
PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam, outOrderNo);
return ResultUtil.data(response.getCodeUrl());
} catch (ServiceException e) {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@@ -381,6 +372,7 @@ public class WechatPlugin implements Payment {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
}
}
@Override
@@ -394,7 +386,7 @@ public class WechatPlugin implements Payment {
return null;
}
Payer payer = new Payer();
com.wechat.pay.java.service.payments.jsapi.model.Payer payer = new com.wechat.pay.java.service.payments.jsapi.model.Payer();
payer.setOpenid(connect.getUnionId());
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
@@ -412,47 +404,41 @@ public class WechatPlugin implements Payment {
if (StringUtils.isEmpty(appid)) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
// 将 Hutool URLEncoder 替换为标准库
String attach = java.net.URLEncoder.encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8.name());
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(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen))
.setPayer(payer);
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.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<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key());
log.info("唤起支付参数:{}", map);
updateOrderPayNo(payParam,outOrderNo);
return ResultUtil.data(map);
Config config = null;
if ("CERT".equals(setting.getPublicType())) {
config = this.getCertificateConfig(setting);
} else {
config = this.getPublicKeyConfig(setting);
}
log.error("微信支付参数验证错误,请及时处理");
throw new ServiceException(ResultCode.PAY_ERROR);
// 构建service
JsapiService service = new JsapiService.Builder().config(config).build();
com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest prepayRequest = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
amount.setTotal(fen);
prepayRequest.setAmount(amount);
prepayRequest.setAppid(appid);
prepayRequest.setMchid(setting.getMchId());
prepayRequest.setDescription(cashierParam.getDetail());
prepayRequest.setNotifyUrl(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
prepayRequest.setAttach(attach);
prepayRequest.setTimeExpire(timeExpire);
prepayRequest.setOutTradeNo(outOrderNo);
prepayRequest.setPayer(payer);
// 调用下单方法,得到应答
com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse response = service.prepay(prepayRequest);
updateOrderPayNo(payParam, outOrderNo);
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, response.getPrepayId(), setting.getApiclientKey());
log.info("唤起支付参数:{}", map);
return ResultUtil.data(map);
} catch (Exception e) {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
@@ -514,48 +500,50 @@ public class WechatPlugin implements Payment {
}
}
//获取微信设置
WechatPaymentSetting wechatPaymentSetting = wechatPaymentSetting();
//获取用户openId
Connect connect = connectService.queryConnect(
ConnectQueryDTO.builder().userId(memberWithdrawApply.getMemberId())
.unionType(source).build()
);
//构建提现,发起申请
TransferModel transferModel = new TransferModel()
.setAppid(withdrawalSetting.getWechatAppId())
.setOut_batch_no(SnowFlake.createStr("T"))
.setBatch_name("用户提现")
.setBatch_remark("用户提现")
.setTotal_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney()))
.setTotal_num(1)
.setTransfer_scene_id("1000");
//获取微信设置
WechatPaymentSetting setting = wechatPaymentSetting();
Config config = null;
if ("CERT".equals(setting.getPublicType())) {
config = this.getCertificateConfig(setting);
} else {
config = this.getPublicKeyConfig(setting);
}
// 构建service
TransferBatchService service = new TransferBatchService.Builder().config(config).build();
InitiateBatchTransferRequest request = new InitiateBatchTransferRequest();
request.setAppid(withdrawalSetting.getWechatAppId());
request.setOutBatchNo(SnowFlake.createStr("T"));
request.setBatchName("用户提现");
request.setBatchRemark("用户提现");
request.setTotalAmount(CurrencyUtil.getFenLong(memberWithdrawApply.getApplyMoney()));
request.setTotalNum(1);
request.setTransferSceneId("1000");
List<TransferDetailInput> transferDetailListList = new ArrayList<>();
{
TransferDetailInput transferDetailInput = new TransferDetailInput();
transferDetailInput.setOut_detail_no(SnowFlake.createStr("TD"));
transferDetailInput.setTransfer_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney()));
transferDetailInput.setTransfer_remark("用户提现");
transferDetailInput.setOutDetailNo(SnowFlake.createStr("TD"));
transferDetailInput.setTransferAmount(CurrencyUtil.getFenLong(memberWithdrawApply.getApplyMoney()));
transferDetailInput.setTransferRemark("用户提现");
transferDetailInput.setOpenid(connect.getUnionId());
transferDetailListList.add(transferDetailInput);
}
transferModel.setTransfer_detail_list(transferDetailListList);
request.setTransferDetailList(transferDetailListList);
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.TRANSFER_BATCHES.toString(),
wechatPaymentSetting.getMchId(),
wechatPaymentSetting.getSerialNumber(),
null,
wechatPaymentSetting.getApiclient_key(),
JSONUtil.toJsonStr(transferModel)
);
// 调用下单方法,得到应答
InitiateBatchTransferResponse response = service.initiateBatchTransfer(request);
log.info("微信提现响应 {}", response);
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
return TransferResultDTO.builder().result(jsonObject.getStr("batch_id") != null).response(body).build();
return TransferResultDTO.builder().result(response.getBatchId() != null).build();
//根据自身业务进行接下来的任务处理
} catch (Exception e) {
e.printStackTrace();
@@ -572,41 +560,63 @@ public class WechatPlugin implements Payment {
*/
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);
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(request.getHeader("Wechatpay-Serial"))
.nonce(request.getHeader("Wechatpay-Nonce"))
.signature(request.getHeader("Wechatpay-Signature"))
.timestamp(request.getHeader("Wechatpay-Timestamp"))
.body(HttpKit.readData(request))
.build();
WechatPaymentSetting setting = wechatPaymentSetting();
//校验服务器端响应¬
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
setting.getApiKey3(), Objects.requireNonNull(getPlatformCert()));
NotificationConfig config = null;
if ("CERT".equals(setting.getPublicType())) {
config = new RSAAutoCertificateConfig.Builder()
.merchantId(setting.getMchId())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.apiV3Key(setting.getApiKey3())
.build();
} else {
config = new RSAPublicKeyConfig.Builder()
.merchantId(setting.getMchId())
.apiV3Key(setting.getApiKey3())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.publicKeyId(setting.getPublicId())
.publicKey(setting.getPublicKey())
.build();
}
log.info("微信支付通知明文 {}", plainText);
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
JSONObject jsonObject = JSONUtil.parseObj(plainText);
try {
// 以支付通知回调为例,验签、解密并转换成 Transaction
Transaction transaction = parser.parse(requestParam, Transaction.class);
String payParamStr = jsonObject.getStr("attach");
String payParamJson = URLDecoder.decode(payParamStr, StandardCharsets.UTF_8);
PayParam payParam = JSONUtil.toBean(payParamJson, PayParam.class);
// 将 Hutool URLDecoder 替换为标准库
String payParamJson = java.net.URLDecoder.decode(transaction.getAttach(), StandardCharsets.UTF_8.name());
PayParam payParam = new Gson().fromJson(payParamJson, PayParam.class);
String tradeNo = jsonObject.getStr("transaction_id");
Double totalAmount = CurrencyUtil.reversalFen(jsonObject.getJSONObject("amount").getDouble("total"));
Double totalAmount = CurrencyUtil.reversalFen(transaction.getAmount().getTotal());
PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
PaymentMethodEnum.WECHAT.name(),
tradeNo,
totalAmount,
payParam
);
PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams(
PaymentMethodEnum.WECHAT.name(),
transaction.getTransactionId(),
totalAmount,
payParam
);
paymentService.success(paymentSuccessParams);
} catch (ValidationException e) {
// 签名验证失败,返回 401 UNAUTHORIZED 状态码
log.error("sign verification failed", e);
}
paymentService.success(paymentSuccessParams);
log.info("微信支付回调:支付成功{}", plainText);
}
@Override
@@ -614,39 +624,33 @@ public class WechatPlugin implements Payment {
try {
Amount amount = new Amount().setRefund(CurrencyUtil.fen(refundLog.getTotalAmount()))
.setTotal(CurrencyUtil.fen(orderService.getPaymentTotal(refundLog.getOrderSn())));
//退款参数准备
RefundModel refundModel = new RefundModel()
.setTransaction_id(refundLog.getPaymentReceivableNo())
.setOut_refund_no(refundLog.getOutOrderNo())
.setReason(refundLog.getRefundReason())
.setAmount(amount)
.setNotify_url(refundNotifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
AmountReq amount = new AmountReq();
amount.setRefund(CurrencyUtil.getFenLong(refundLog.getTotalAmount()));
amount.setTotal(CurrencyUtil.getFenLong(orderService.getPaymentTotal(refundLog.getOrderSn())));
amount.setCurrency("CNY");
//获取微信设置
WechatPaymentSetting setting = wechatPaymentSetting();
log.info("微信退款参数 {}", JSONUtil.toJsonStr(refundModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.DOMESTIC_REFUNDS.toString(),
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
JSONUtil.toJsonStr(refundModel)
);
log.info("微信退款响应 {}", response);
//退款申请成功
if (response.getStatus() == 200) {
refundLogService.save(refundLog);
Config config = null;
if ("CERT".equals(setting.getPublicType())) {
config = this.getCertificateConfig(setting);
} else {
//退款申请失败
refundLog.setErrorMessage(response.getBody());
refundLogService.save(refundLog);
config = this.getPublicKeyConfig(setting);
}
// 构建service
RefundService refundService = new RefundService.Builder().config(config).build();
CreateRequest request = new CreateRequest();
request.setTransactionId(refundLog.getPaymentReceivableNo());
request.setAmount(amount);
request.setOutRefundNo(refundLog.getOutOrderNo());
request.setReason(refundLog.getRefundReason());
request.setNotifyUrl(refundNotifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT));
Refund refund = refundService.create(request);
log.info("微信退款响应 {}", refund);
refundLogService.save(refundLog);
} catch (Exception e) {
log.error("微信退款申请失败", e);
}
@@ -655,49 +659,45 @@ public class WechatPlugin implements Payment {
@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");
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(request.getHeader("Wechatpay-Serial"))
.nonce(request.getHeader("Wechatpay-Nonce"))
.signature(request.getHeader("Wechatpay-Signature"))
.timestamp(request.getHeader("Wechatpay-Timestamp"))
.body(HttpKit.readData(request))
.build();
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
String result = HttpKit.readData(request);
log.info("微信退款通知密文 {}", result);
JSONObject ciphertext = JSONUtil.parseObj(result);
WechatPaymentSetting setting = wechatPaymentSetting();
NotificationConfig config = null;
if ("CERT".equals(setting.getPublicType())) {
config = new RSAAutoCertificateConfig.Builder()
.merchantId(setting.getMchId())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.apiV3Key(setting.getApiKey3())
.build();
} else {
config = new RSAPublicKeyConfig.Builder()
.merchantId(setting.getMchId())
.apiV3Key(setting.getApiKey3())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.publicKeyId(setting.getPublicId())
.publicKey(setting.getPublicKey())
.build();
}
try { //校验服务器端响应¬
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
wechatPaymentSetting().getApiKey3(), Objects.requireNonNull(getPlatformCert()));
log.info("微信退款通知明文 {}", plainText);
if (("REFUND.SUCCESS").equals(ciphertext.getStr("event_type"))) {
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<RefundLog>().eq(RefundLog::getPaymentReceivableNo,
transactionId));
if (refundLog != null) {
refundLog.setIsRefund(true);
refundLog.setReceivableNo(refundId);
refundLogService.saveOrUpdate(refundLog);
}
} else {
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<RefundLog>().eq(RefundLog::getPaymentReceivableNo,
transactionId));
if (refundLog != null) {
refundLog.setReceivableNo(refundId);
refundLog.setErrorMessage(ciphertext.getStr("summary"));
refundLogService.saveOrUpdate(refundLog);
}
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
try {
Refund refund = parser.parse(requestParam, Refund.class);
RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper<RefundLog>().eq(RefundLog::getPaymentReceivableNo,
refund.getTransactionId()));
if (refundLog != null) {
refundLog.setIsRefund(true);
refundLog.setReceivableNo(refund.getRefundId());
refundLogService.saveOrUpdate(refundLog);
}
} catch (Exception e) {
log.error("微信退款失败", e);
@@ -721,93 +721,53 @@ public class WechatPlugin implements Payment {
}
/**
* 获取平台公钥
* 获取微信公钥配置
*
* @return 平台公钥
* @param setting
* @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(),
WechatApiEnum.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() - System.currentTimeMillis()) / 1000;
cache.put(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix(), publicCert, second);
} else {
log.error("证书获取失败:{}" + body);
throw new ServiceException(ResultCode.WECHAT_CERT_ERROR);
}
return PayKit.getCertificate(publicCert);
} catch (Exception e) {
log.error("证书获取失败", e);
}
return null;
private RSAPublicKeyConfig getPublicKeyConfig(WechatPaymentSetting setting) {
return
new RSAPublicKeyConfig.Builder()
.merchantId(setting.getMchId())
.privateKey(setting.getApiclientKey())
.publicKey(setting.getPublicKey())
.publicKeyId(setting.getPublicId())
.merchantSerialNumber(setting.getSerialNumber())
.apiV3Key(setting.getApiKey3())
.build();
}
/**
* 获取平台证书缓存的字符串
* 下列各个密钥参数
* 获取微信证书配置
*
* @param associatedData 密钥参数
* @param nonce 密钥参数
* @param cipherText 密钥参数
* @return platform key
* @throws GeneralSecurityException 密钥获取异常
* @param setting
* @return
*/
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
);
private RSAAutoCertificateConfig getCertificateConfig(WechatPaymentSetting setting) {
return new RSAAutoCertificateConfig.Builder()
.merchantId(setting.getMchId())
.privateKey(setting.getApiclientKey())
.merchantSerialNumber(setting.getSerialNumber())
.apiV3Key(setting.getApiKey3())
.build();
}
/**
* 修改订单支付单号
* @param payParam 支付参数
*
* @param payParam 支付参数
* @param outOrderNo 订单号
*/
private void updateOrderPayNo(PayParam payParam,String outOrderNo ){
if(payParam.getOrderType().equals("ORDER")){
private void updateOrderPayNo(PayParam payParam, String outOrderNo) {
if ("ORDER".equals(payParam.getOrderType())) {
orderService.update(new LambdaUpdateWrapper<Order>()
.eq(Order::getSn,payParam.getSn())
.set(Order::getPayOrderNo,outOrderNo));
}else if(payParam.getOrderType().equals("TRADE")){
.eq(Order::getSn, payParam.getSn())
.set(Order::getPayOrderNo, outOrderNo));
} else if ("TRADE".equals(payParam.getOrderType())) {
orderService.update(new LambdaUpdateWrapper<Order>()
.eq(Order::getTradeSn,payParam.getSn())
.set(Order::getPayOrderNo,outOrderNo));
.eq(Order::getTradeSn, payParam.getSn())
.set(Order::getPayOrderNo, outOrderNo));
}
}
}

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.promotion.serviceimpl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
@@ -12,6 +13,7 @@ import cn.lili.modules.promotion.entity.dos.BasePromotions;
import cn.lili.modules.promotion.entity.dos.PromotionGoods;
import cn.lili.modules.promotion.entity.dto.search.BasePromotionsSearchParams;
import cn.lili.modules.promotion.entity.enums.PromotionsScopeTypeEnum;
import cn.lili.modules.promotion.entity.enums.PromotionsStatusEnum;
import cn.lili.modules.promotion.service.AbstractPromotionsService;
import cn.lili.modules.promotion.service.PromotionGoodsService;
import cn.lili.modules.promotion.tools.PromotionTools;
@@ -26,8 +28,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotNull;
import java.util.*;
import static cn.lili.modules.promotion.tools.PromotionTools.queryPromotionStatus;
/**
* @author paulG
* @since 2021/11/30
@@ -215,7 +220,7 @@ public abstract class AbstractPromotionsServiceImpl<M extends BaseMapper<T>, T e
* 更新促销商品信息
*
* @param promotions 促销实体
* @return
* @return 是否更新成功
*/
@Override
@Transactional(rollbackFor = {Exception.class})
@@ -283,12 +288,53 @@ public abstract class AbstractPromotionsServiceImpl<M extends BaseMapper<T>, T e
if (promotions.getStartTime() == null || promotions.getEndTime() == null) {
return;
}
QueryWrapper<T> queryWrapper = PromotionTools.checkActiveTime(promotions.getStartTime(), promotions.getEndTime(), this.getPromotionType(), promotions.getStoreId(), promotions.getId());
long sameNum = this.count(queryWrapper);
//当前时间段是否存在同类活动
if (sameNum > 0) {
throw new ServiceException(ResultCode.PROMOTION_SAME_ACTIVE_EXIST);
}
// 遍历编辑活动开始时间的当天开始时间到结束的当天结束时间内的活动
QueryWrapper<T> querySameWrapper = getSameWrapper(promotions);
this.list(querySameWrapper).forEach(promotion -> {
if (promotion.getStartTime() == null || promotion.getEndTime() == null) {
return;
}
if (isOverlapping(promotions.getStartTime(), promotions.getEndTime(), promotion.getStartTime(), promotion.getEndTime())) {
throw new ServiceException(ResultCode.PROMOTION_SAME_ACTIVE_EXIST);
}
});
}
/**
* 获取相同时间段的促销活动
*
* @param promotions 促销活动
* @return 相同时间段的促销活动
* @param <T> 促销活动类型
*/
private static <T extends BasePromotions> @NotNull QueryWrapper<T> getSameWrapper(T promotions) {
QueryWrapper<T> querySameWrapper = new QueryWrapper<>();
querySameWrapper.eq("store_id", promotions.getStoreId());
querySameWrapper.eq("delete_flag", false);
querySameWrapper.ne("id", promotions.getId());
querySameWrapper.and(i -> i.or(queryPromotionStatus(PromotionsStatusEnum.NEW)).or(queryPromotionStatus(PromotionsStatusEnum.START)));
querySameWrapper.and(i -> i.or(j -> j.between("start_time", DateUtil.beginOfDay(promotions.getStartTime()), DateUtil.endOfDay(promotions.getEndTime()))
.or().between("end_time", DateUtil.beginOfDay(promotions.getStartTime()), DateUtil.endOfDay(promotions.getEndTime()))));
return querySameWrapper;
}
/**
* 判断两个时间段是否有交集
*
* @param a1 原时间段开始时间
* @param a2 原时间段结束时间
* @param b1 新时间段开始时间
* @param b2 新时间段结束时间
* @return 是否有交集
*/
public static boolean isOverlapping(Date a1, Date a2, Date b1, Date b2) {
// 两个时间段有交集的条件是b1在a1和a2之间或者b2在a1和a2之间或者a1在b1和b2之间
return (b1.before(a2) && b2.after(a1)) || (b1.before(a1) && b2.after(a2)) || (a1.before(b1) && a2.after(b2));
}
}

View File

@@ -411,9 +411,12 @@ public class CouponActivityServiceImpl extends AbstractPromotionsServiceImpl<Cou
//判断优惠券的发送范围,获取会员列表
List<String> ids = new ArrayList<>();
if (JSONUtil.isJsonArray(couponActivity.getActivityScopeInfo())) {
JSONArray array = JSONUtil.parseArray(couponActivity.getActivityScopeInfo());
String scopeInfo = couponActivity.getActivityScopeInfo();
try {
JSONArray array = JSONUtil.parseArray(scopeInfo);
ids = array.toList(Map.class).stream().map(i -> i.get("id").toString()).collect(Collectors.toList());
} catch (Exception ignore) {
// 非数组或格式错误时忽略,保持 ids 为空列表
}
return memberService.listFieldsByMemberIds("id,nick_name", ids);
}

View File

@@ -286,7 +286,10 @@ public class CouponServiceImpl extends AbstractPromotionsServiceImpl<CouponMappe
public void updateEsGoodsIndex(Coupon promotions) {
Coupon coupon = JSONUtil.parse(promotions).toBean(Coupon.class);
if (!CouponRangeDayEnum.DYNAMICTIME.name().equals(coupon.getRangeDayType()) && promotions.getStartTime() == null && promotions.getEndTime() == null) {
Map<Object, Object> build = MapBuilder.create().put("promotionKey", this.getPromotionType() + "-" + promotions.getId()).put("scopeId", promotions.getScopeId()).build();
Map<Object, Object> build = MapBuilder.create().put("promotionKey", this.getPromotionType() + "-" + promotions.getId()).build();
if(promotions.getScopeType().equals(PromotionsScopeTypeEnum.PORTION_GOODS.name())){
build.put("scopeId", promotions.getScopeId());
}
//删除商品促销消息
applicationEventPublisher.publishEvent(new TransactionCommitSendMQEvent("删除商品促销事件", rocketmqCustomProperties.getGoodsTopic(), GoodsTagsEnum.DELETE_GOODS_INDEX_PROMOTIONS.name(), JSONUtil.toJsonStr(build)));
} else {

View File

@@ -185,13 +185,13 @@ public class KanjiaActivityGoodsServiceImpl extends AbstractPromotionsServiceImp
.or(PromotionTools.queryPromotionStatus(PromotionsStatusEnum.START))
.or(PromotionTools.queryPromotionStatus(PromotionsStatusEnum.NEW)));
if (kanJiaActivityGoodsDTO != null && kanJiaActivityGoodsDTO.getStartTime() != null) {
queryWrapper.ge("start_time", kanJiaActivityGoodsDTO.getStartTime());
}
if (kanJiaActivityGoodsDTO != null && kanJiaActivityGoodsDTO.getEndTime() != null) {
queryWrapper.le("end_time", kanJiaActivityGoodsDTO.getEndTime());
}
// if (kanJiaActivityGoodsDTO != null && kanJiaActivityGoodsDTO.getStartTime() != null) {
// queryWrapper.ge("start_time", kanJiaActivityGoodsDTO.getStartTime());
// }
//
// if (kanJiaActivityGoodsDTO != null && kanJiaActivityGoodsDTO.getEndTime() != null) {
// queryWrapper.le("end_time", kanJiaActivityGoodsDTO.getEndTime());
// }
return this.getOne(queryWrapper);

View File

@@ -211,6 +211,10 @@ public class KanjiaActivityServiceImpl extends ServiceImpl<KanJiaActivityMapper,
//获取随机砍价金额
BigDecimal bigDecimal = RandomUtil.randomBigDecimal(Convert.toBigDecimal(kanjiaActivityGoods.getLowestPrice()),
Convert.toBigDecimal(kanjiaActivityGoods.getHighestPrice()));
if(bigDecimal.setScale(2, RoundingMode.UP).doubleValue() > surplusPrice){
return surplusPrice;
}
return bigDecimal.setScale(2, RoundingMode.UP).doubleValue();
}

View File

@@ -241,8 +241,12 @@ public class PintuanServiceImpl extends AbstractPromotionsServiceImpl<PintuanMap
private void setMemberVONum(PintuanMemberVO memberVO, Integer requiredNum, String orderSn) {
long count = this.orderService.queryCountByPromotion(PromotionTypeEnum.PINTUAN.name(), PayStatusEnum.PAID.name(), orderSn, orderSn);
//获取待参团人数
Order order = orderService.getBySn(orderSn);
long toBoGrouped = requiredNum - count;
if(order.getOrderStatus().equals(OrderStatusEnum.UNDELIVERED.name())){
toBoGrouped = 0L;
}
//获取待参团人数
memberVO.setGroupNum(requiredNum);
memberVO.setGroupedNum(count);
memberVO.setToBeGroupedNum(toBoGrouped);

View File

@@ -247,12 +247,17 @@ public class PointsGoodsServiceImpl extends AbstractPromotionsServiceImpl<Points
private PointsGoods checkExist(String id) {
PointsGoods pointsGoods = this.getById(id);
if (pointsGoods == null) {
log.error("id为" + id + "的积分商品不存在!");
log.error("id为{}的积分商品不存在!", id);
throw new ServiceException();
}
return pointsGoods;
}
@Override
public boolean allowExistSame() {
return true;
}
/**
* 检查积分商品是否重复存在
*

View File

@@ -87,6 +87,7 @@ public class PromotionServiceImpl implements PromotionService {
* @param goodsSkuId 商品skuId
* @return 当前促销活动集合
*/
@Override
public Map<String, Object> getGoodsSkuPromotionMap(String storeId, String goodsSkuId) {
String storeIds = storeId + "," + PromotionTools.PLATFORM_ID;
List<PromotionGoods> promotionGoodsList = promotionGoodsService.findSkuValidPromotion(goodsSkuId, storeIds);
@@ -99,7 +100,7 @@ public class PromotionServiceImpl implements PromotionService {
promotionGoodsService.deletePromotionGoodsByGoods(goodsIds);
kanjiaActivityGoodsService.deleteByGoodsIds(goodsIds);
}
@Override
public Map<String, Object> wrapperPromotionMapList(List<PromotionGoods> promotionGoodsList) {
Map<String, Object> promotionMap = new HashMap<>();
for (PromotionGoods promotionGoods : promotionGoodsList) {

View File

@@ -0,0 +1,34 @@
package cn.lili.modules.search.entity.dto;
import lombok.*;
import java.util.List;
import java.util.Map;
/**
* ES删除DTO
* 用于封装删除ES的参数
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EsDeleteDTO {
/**
* 删除的索引
*/
private List<String> ids;
/**
* 查询字段
*/
private Map<String, Object> queryFields;
/**
* 删除的索引
*/
@NonNull
private Class<?> clazz;
}

View File

@@ -19,6 +19,18 @@ import java.util.Map;
**/
public interface EsGoodsIndexService {
/**
* 删除下架商品索引
*/
Boolean deleteGoodsDown();
/**
* 删除不存在的索引
* @return
*/
Boolean delSkuIndex();
Boolean goodsCache();
/**
* 全局索引数据初始化
*/

View File

@@ -6,6 +6,8 @@ import cn.lili.modules.search.entity.dos.EsGoodsRelatedInfo;
import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.data.elasticsearch.core.SearchPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import java.util.List;
@@ -26,6 +28,16 @@ public interface EsGoodsSearchService {
*/
SearchPage<EsGoodsIndex> searchGoods(EsGoodsSearchDTO searchDTO, PageVO pageVo);
/**
* 搜索商品
*
* @param searchQuery 搜索条件
* @param clazz 搜索结果类
* @param <T> 泛型
* @return 搜索结果
*/
<T> SearchPage<T> searchGoods(Query searchQuery, Class<T> clazz);
/**
* 商品搜索
*
@@ -59,4 +71,13 @@ public interface EsGoodsSearchService {
* @return 商品索引
*/
EsGoodsIndex getEsGoodsById(String id);
/**
* 创建搜索条件
*
* @param searchDTO 搜索参数
* @param pageVo 分页参数
* @return 搜索条件
*/
NativeSearchQueryBuilder createSearchQueryBuilder(EsGoodsSearchDTO searchDTO, PageVO pageVo);
}

View File

@@ -2,6 +2,7 @@ package cn.lili.modules.search.serviceimpl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ReflectUtil;
@@ -18,16 +19,15 @@ import cn.lili.common.vo.PageVO;
import cn.lili.elasticsearch.BaseElasticsearchService;
import cn.lili.elasticsearch.EsSuffix;
import cn.lili.elasticsearch.config.ElasticsearchProperties;
import cn.lili.modules.goods.entity.dos.Goods;
import cn.lili.modules.goods.entity.dos.GoodsSku;
import cn.lili.modules.goods.entity.dto.GoodsParamsDTO;
import cn.lili.modules.goods.entity.dto.GoodsSearchParams;
import cn.lili.modules.goods.entity.dto.GoodsSkuDTO;
import cn.lili.modules.goods.entity.enums.GoodsAuthEnum;
import cn.lili.modules.goods.entity.enums.GoodsSalesModeEnum;
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.goods.service.GoodsSkuService;
import cn.lili.modules.goods.service.StoreGoodsLabelService;
import cn.lili.modules.goods.service.*;
import cn.lili.modules.promotion.entity.dos.BasePromotions;
import cn.lili.modules.promotion.entity.dos.PromotionGoods;
import cn.lili.modules.promotion.entity.enums.PromotionsScopeTypeEnum;
@@ -38,13 +38,16 @@ import cn.lili.modules.promotion.tools.PromotionTools;
import cn.lili.modules.search.entity.dos.CustomWords;
import cn.lili.modules.search.entity.dos.EsGoodsAttribute;
import cn.lili.modules.search.entity.dos.EsGoodsIndex;
import cn.lili.modules.search.entity.dto.EsDeleteDTO;
import cn.lili.modules.search.entity.dto.EsGoodsSearchDTO;
import cn.lili.modules.search.repository.EsGoodsIndexRepository;
import cn.lili.modules.search.service.CustomWordsService;
import cn.lili.modules.search.service.EsGoodsIndexService;
import cn.lili.modules.search.service.EsGoodsSearchService;
import cn.lili.mybatis.util.PageUtil;
import cn.lili.rocketmq.RocketmqSendCallbackBuilder;
import cn.lili.rocketmq.tags.GoodsTagsEnum;
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;
@@ -65,13 +68,17 @@ import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;
@@ -110,6 +117,8 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
@Autowired
private GoodsSkuService goodsSkuService;
@Autowired
private GoodsService goodsService;
@Autowired
private BrandService brandService;
@Autowired
@@ -118,6 +127,8 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
@Autowired
private StoreGoodsLabelService storeGoodsLabelService;
@Autowired
private EsGoodsSearchService esGoodsSearchService;
@Autowired
private Cache<Object> cache;
/**
* rocketMq
@@ -144,6 +155,126 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
list.addAll(h);
}
@Override
public Boolean deleteGoodsDown() {
List<Goods> goodsList = goodsService.list(new LambdaQueryWrapper<Goods>().eq(Goods::getMarketEnable, GoodsStatusEnum.DOWN.name()));
for (Goods goods : goodsList) {
this.deleteIndex(
EsDeleteDTO.builder()
.queryFields(MapUtil.builder(new HashMap<String, Object>()).put("goodsId", goods.getId()).build())
.clazz(EsGoodsIndex.class)
.build());
}
return true;
}
@Override
public Boolean delSkuIndex() {
PageVO pageVO = new PageVO();
EsGoodsSearchDTO goodsSearchParams = new EsGoodsSearchDTO();
log.error("开始");
try {
List<Object> searchFilters = new ArrayList<>();
for (int i = 1; ; i++) {
log.error("" + i + "");
pageVO.setPageSize(1000);
pageVO.setPageNumber(i);
pageVO.setNotConvert(true);
pageVO.setSort("_id");
pageVO.setOrder("asc");
NativeSearchQueryBuilder searchQueryBuilder = esGoodsSearchService.createSearchQueryBuilder(goodsSearchParams, pageVO);
searchQueryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"_id"}, null));
Pageable pageable = PageRequest.of(0, 1000);
//分页
searchQueryBuilder.withPageable(pageable);
NativeSearchQuery query = searchQueryBuilder.build();
EsGoodsSearchDTO searchDTO = new EsGoodsSearchDTO();
SearchPage<EsGoodsIndex> searchHits = goodsSearchService.searchGoods(query, EsGoodsIndex.class);
if (searchHits == null || searchHits.isEmpty()) {
break;
}
List<String> idList = searchHits.getSearchHits().stream().map(SearchHit::getContent).map(EsGoodsIndex::getId).collect(Collectors.toList());
LambdaQueryWrapper<GoodsSku> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.select(GoodsSku::getId);
queryWrapper.in(GoodsSku::getId, idList);
List<GoodsSku> goodsSkus = goodsSkuService.list(queryWrapper);
idList.forEach(id -> {
if (goodsSkus.stream().noneMatch(goodsSku -> goodsSku.getId().equals(id))) {
log.error("[{}]不存在,进行删除", id);
this.deleteIndexById(id);
}
});
if (!searchHits.getSearchHits().getSearchHit(idList.size() -1).getSortValues().isEmpty()) {
searchFilters = searchHits.getSearchHits().getSearchHit(idList.size() -1).getSortValues();
}
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("结束了");
return true;
}
@Override
public Boolean goodsCache() {
GoodsSearchParams searchParams = new GoodsSearchParams();
searchParams.setAuthFlag(GoodsAuthEnum.PASS.name());
searchParams.setMarketEnable(GoodsStatusEnum.UPPER.name());
for (int i = 1; ; i++) {
try {
IPage<Goods> pagePage = new Page<>();
searchParams.setPageSize(1000);
searchParams.setPageNumber(i);
pagePage = goodsService.queryByParams(searchParams);
if (pagePage == null || CollUtil.isEmpty(pagePage.getRecords())) {
break;
}
for (Goods goods : pagePage.getRecords()) {
cache.remove(CachePrefix.GOODS.getPrefix() + goods.getId());
goodsService.getGoodsVO(goods.getId());
}
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i = 1; ; i++) {
try {
IPage<GoodsSkuDTO> skuIPage = new Page<>();
searchParams.setPageSize(1000);
searchParams.setPageNumber(i);
skuIPage = goodsSkuService.getGoodsSkuDTOByPage(PageUtil.initPage(searchParams),
searchParams.queryWrapper());
if (skuIPage == null || CollUtil.isEmpty(skuIPage.getRecords())) {
break;
}
for (GoodsSkuDTO goodsSkuDTO : skuIPage.getRecords()) {
GoodsSku goodsSku = goodsSkuService.getById(goodsSkuDTO.getId());
cache.put(GoodsSkuService.getCacheKeys(goodsSkuDTO.getId()), goodsSku,600L);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return true;
}
@Override
public void init() {
//获取索引任务标识
@@ -247,9 +378,9 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
List<PromotionGoods> promotionGoods = skuValidPromotions.stream()
.filter(j ->
(CharSequenceUtil.isNotEmpty(j.getSkuId()) && j.getSkuId().equals(goodsSku.getId())) ||
(j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals("0")) ||
(j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && "0".equals(j.getStoreId())) ||
(j.getScopeType().equals(PromotionsScopeTypeEnum.ALL.name()) && j.getStoreId().equals(esGoodsIndex.getStoreId())) ||
(j.getScopeType().equals(PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name()) && j.getStoreId().equals("0") && j.getScopeId().contains(goodsSku.getCategoryPath()))||
(j.getScopeType().equals(PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name()) && "0".equals(j.getStoreId()) && j.getScopeId().contains(goodsSku.getCategoryPath()))||
(j.getScopeType().equals(PromotionsScopeTypeEnum.PORTION_GOODS_CATEGORY.name()) && j.getStoreId().equals(goodsSku.getStoreId()) && j.getScopeId().contains(goodsSku.getCategoryPath()))
)
.collect(Collectors.toList());
@@ -484,21 +615,27 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
deleteByQueryRequest.setQuery(boolQueryBuilder);
deleteByQueryRequest.indices(getIndexName());
deleteByQueryRequest.setConflicts("proceed");
this.client.deleteByQueryAsync(deleteByQueryRequest, RequestOptions.DEFAULT, new ActionListener<BulkByScrollResponse>() {
@Override
public void onResponse(BulkByScrollResponse bulkByScrollResponse) {
if (bulkByScrollResponse.getVersionConflicts() > 0) {
throw new RetryException("删除索引失败es内容版本冲突");
}
}
try {
this.client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("删除索引出现异常", e);
}
@Override
public void onFailure(Exception e) {
throw new RetryException("删除索引失败," + e.getMessage());
}
});
}
/**
* 删除索引
*
* @param esDeleteDTO 删除索引参数
*/
private void deleteIndex(EsDeleteDTO esDeleteDTO) {
if (esDeleteDTO.getIds() != null && !esDeleteDTO.getIds().isEmpty()) {
this.deleteIndexByIds(esDeleteDTO.getIds());
}
if (esDeleteDTO.getQueryFields() != null && esDeleteDTO.getQueryFields().size() > 0) {
this.deleteIndex(esDeleteDTO.getQueryFields());
}
}
/**
@@ -750,7 +887,7 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
}
private void executeCleanInvalidPromotions() {
for (int i = 1; ; i++) {
for (int i = 0; ; i++) {
org.springframework.data.domain.Page<EsGoodsIndex> all = goodsIndexRepository.findAll(PageRequest.of(i, 1000));
if (all.isEmpty()) {
break;
@@ -765,6 +902,9 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
BasePromotions promotion = promotionJson.toBean(BasePromotions.class);
return promotion.getEndTime() != null && promotion.getEndTime().getTime() < DateUtil.date().getTime();
});
// 更新回 goodsIndex 对象
goodsIndex.setPromotionMapJson(JSONUtil.toJsonStr(promotionMap));
}
}
goodsIndexRepository.saveAll(all);
@@ -867,7 +1007,8 @@ public class EsGoodsIndexServiceImpl extends BaseElasticsearchService implements
// log.info("ES修改商品活动索引-原商品索引信息:{}", goodsIndex);
// log.info("ES修改商品活动索引-原商品索引活动信息:{}", promotionMap);
//如果活动已结束
if (promotion.getPromotionStatus().equals(PromotionsStatusEnum.END.name()) || promotion.getPromotionStatus().equals(PromotionsStatusEnum.CLOSE.name())) {//如果存在活动
//如果存在活动
if (promotion.getPromotionStatus().equals(PromotionsStatusEnum.END.name()) || promotion.getPromotionStatus().equals(PromotionsStatusEnum.CLOSE.name())) {
//删除活动
promotionMap.remove(key);
} else {

View File

@@ -41,6 +41,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
import java.util.*;
@@ -68,6 +69,8 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
private static final String ATTR_BRAND_URL = "brandUrlAgg";
private static final String ATTR_NAME_KEY = "nameList";
private static final String ATTR_VALUE_KEY = "valueList";
// 限制 terms 聚合 size防止内存压力
private static final int MAX_AGG_SIZE = 200;
/**
* ES
*/
@@ -96,6 +99,12 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
return SearchHitSupport.searchPageFor(search, searchQuery.getPageable());
}
@Override
public <T> SearchPage<T> searchGoods(Query searchQuery, Class<T> clazz) {
SearchHits<T> search = restTemplate.search(searchQuery, clazz);
return SearchHitSupport.searchPageFor(search, searchQuery.getPageable());
}
@Override
public Page<EsGoodsIndex> searchGoodsByPage(EsGoodsSearchDTO searchDTO, PageVO pageVo) {
// 判断商品索引是否存在
@@ -122,23 +131,24 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
if (!restTemplate.indexOps(EsGoodsIndex.class).exists()) {
return null;
}
NativeSearchQueryBuilder builder = createSearchQueryBuilder(goodsSearch, null);
//分类
AggregationBuilder categoryNameBuilder = AggregationBuilders.terms("categoryNameAgg").field("categoryNamePath.keyword");
builder.addAggregation(AggregationBuilders.terms("categoryAgg").field("categoryPath").subAggregation(categoryNameBuilder));
AggregationBuilder categoryNameBuilder = AggregationBuilders.terms("categoryNameAgg").field("categoryNamePath.keyword").size(MAX_AGG_SIZE);
builder.addAggregation(AggregationBuilders.terms("categoryAgg").field("categoryPath").size(MAX_AGG_SIZE).subAggregation(categoryNameBuilder));
//品牌
AggregationBuilder brandNameBuilder = AggregationBuilders.terms(ATTR_BRAND_NAME).field("brandName.keyword");
builder.addAggregation(AggregationBuilders.terms("brandIdNameAgg").field(ATTR_BRAND_ID).size(Integer.MAX_VALUE).subAggregation(brandNameBuilder));
AggregationBuilder brandNameBuilder = AggregationBuilders.terms(ATTR_BRAND_NAME).field("brandName.keyword").size(1);
builder.addAggregation(AggregationBuilders.terms("brandIdNameAgg").field(ATTR_BRAND_ID).size(MAX_AGG_SIZE).subAggregation(brandNameBuilder));
AggregationBuilder brandUrlBuilder = AggregationBuilders.terms(ATTR_BRAND_URL).field("brandUrl.keyword");
builder.addAggregation(AggregationBuilders.terms("brandIdUrlAgg").field(ATTR_BRAND_ID).size(Integer.MAX_VALUE).subAggregation(brandUrlBuilder));
AggregationBuilder brandUrlBuilder = AggregationBuilders.terms(ATTR_BRAND_URL).field("brandUrl.keyword").size(1);
builder.addAggregation(AggregationBuilders.terms("brandIdUrlAgg").field(ATTR_BRAND_ID).size(MAX_AGG_SIZE).subAggregation(brandUrlBuilder));
//参数
AggregationBuilder valuesBuilder = AggregationBuilders.terms("valueAgg").field(ATTR_VALUE);
AggregationBuilder valuesBuilder = AggregationBuilders.terms("valueAgg").field(ATTR_VALUE).size(MAX_AGG_SIZE);
AggregationBuilder sortBuilder = AggregationBuilders.sum("sortAgg").field(ATTR_SORT);
AggregationBuilder paramsNameBuilder = AggregationBuilders.terms("nameAgg").field(ATTR_NAME).subAggregation(sortBuilder).order(BucketOrder.aggregation("sortAgg", false)).subAggregation(valuesBuilder);
AggregationBuilder paramsNameBuilder = AggregationBuilders.terms("nameAgg").field(ATTR_NAME).size(MAX_AGG_SIZE)
.subAggregation(sortBuilder).order(BucketOrder.aggregation("sortAgg", false)).subAggregation(valuesBuilder);
builder.addAggregation(AggregationBuilders.nested("attrAgg", ATTR_PATH).subAggregation(paramsNameBuilder));
NativeSearchQuery searchQuery = builder.build();
searchQuery.setMaxResults(0);
SearchHits<EsGoodsIndex> search = restTemplate.search(searchQuery, EsGoodsIndex.class);
@@ -227,35 +237,57 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
*/
private List<SelectorOptions> convertBrandOptions(EsGoodsSearchDTO goodsSearch, List<? extends Terms.Bucket> brandBuckets, List<? extends Terms.Bucket> brandUrlBuckets) {
List<SelectorOptions> brandOptions = new ArrayList<>();
for (int i = 0; i < brandBuckets.size(); i++) {
String brandId = brandBuckets.get(i).getKey().toString();
//当商品品牌id为0时代表商品没有选择品牌所以过滤掉品牌选择器
//当品牌id为空并且
if (brandId.equals("0") ||
(CharSequenceUtil.isNotEmpty(goodsSearch.getBrandId())
&& Arrays.asList(goodsSearch.getBrandId().split("@")).contains(brandId))) {
// 以 brandId 为 key 构建 URL 桶索引,避免按下标对齐产生越界/错位
Map<String, Terms.Bucket> brandUrlBucketMap = new HashMap<>();
if (brandUrlBuckets != null) {
for (Terms.Bucket urlBucket : brandUrlBuckets) {
if (urlBucket != null && urlBucket.getKey() != null) {
brandUrlBucketMap.put(urlBucket.getKey().toString(), urlBucket);
}
}
}
Set<String> selectedBrandIds = new HashSet<>();
if (CharSequenceUtil.isNotEmpty(goodsSearch.getBrandId())) {
selectedBrandIds.addAll(Arrays.asList(goodsSearch.getBrandId().split("@")));
}
for (Terms.Bucket bucket : brandBuckets) {
if (bucket == null || bucket.getKey() == null) {
continue;
}
String brandId = bucket.getKey().toString();
// 排除无品牌或已选择品牌
if ("0".equals(brandId) || (!selectedBrandIds.isEmpty() && selectedBrandIds.contains(brandId))) {
continue;
}
// 品牌名
String brandName = "";
if (brandBuckets.get(i).getAggregations() != null && brandBuckets.get(i).getAggregations().get(ATTR_BRAND_NAME) != null) {
ParsedStringTerms aggregation = brandBuckets.get(i).getAggregations().get(ATTR_BRAND_NAME);
brandName = this.getAggregationsBrandOptions(aggregation);
if (StringUtils.isEmpty(brandName)) {
continue;
Aggregations nameAggs = bucket.getAggregations();
if (nameAggs != null) {
Aggregation nameAgg = nameAggs.get(ATTR_BRAND_NAME);
if (nameAgg instanceof ParsedStringTerms) {
brandName = this.getAggregationsBrandOptions((ParsedStringTerms) nameAgg);
}
}
if (StringUtils.isEmpty(brandName)) {
continue;
}
// 品牌 URL
String brandUrl = "";
if (brandUrlBuckets != null && !brandUrlBuckets.isEmpty() &&
brandUrlBuckets.get(i).getAggregations() != null &&
brandUrlBuckets.get(i).getAggregations().get(ATTR_BRAND_URL) != null) {
ParsedStringTerms aggregation = brandUrlBuckets.get(i).getAggregations().get(ATTR_BRAND_URL);
brandUrl = this.getAggregationsBrandOptions(aggregation);
if (StringUtils.isEmpty(brandUrl)) {
continue;
Terms.Bucket urlBucket = brandUrlBucketMap.get(brandId);
if (urlBucket != null && urlBucket.getAggregations() != null) {
Aggregation urlAgg = urlBucket.getAggregations().get(ATTR_BRAND_URL);
if (urlAgg instanceof ParsedStringTerms) {
brandUrl = this.getAggregationsBrandOptions((ParsedStringTerms) urlAgg);
}
}
if (StringUtils.isEmpty(brandUrl)) {
continue;
}
SelectorOptions so = new SelectorOptions();
so.setName(brandName);
so.setValue(brandId);
@@ -377,7 +409,8 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
* @param pageVo 分页参数
* @return es搜索builder
*/
private NativeSearchQueryBuilder createSearchQueryBuilder(EsGoodsSearchDTO searchDTO, PageVO pageVo) {
@Override
public NativeSearchQueryBuilder createSearchQueryBuilder(EsGoodsSearchDTO searchDTO, PageVO pageVo) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
if (pageVo != null) {
int pageNumber = pageVo.getPageNumber() - 1;
@@ -475,7 +508,7 @@ public class EsGoodsSearchServiceImpl implements EsGoodsSearchService {
//品牌判定
if (CharSequenceUtil.isNotEmpty(searchDTO.getBrandId())) {
String[] brands = searchDTO.getBrandId().split("@");
filterBuilder.must(QueryBuilders.termsQuery(ATTR_BRAND_ID, brands));
filterBuilder.filter(QueryBuilders.termsQuery(ATTR_BRAND_ID, brands));
}
if (searchDTO.getRecommend() != null) {
filterBuilder.filter(QueryBuilders.termQuery("recommend", searchDTO.getRecommend()));

View File

@@ -5,8 +5,6 @@ import cn.lili.cache.Cache;
import cn.lili.cache.CachePrefix;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.SmsTemplateProperties;
import cn.lili.common.properties.SystemSettingProperties;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.utils.CommonUtil;
import cn.lili.modules.member.entity.dos.Member;
@@ -48,12 +46,6 @@ public class SmsUtilAliImplService implements SmsUtil {
@Autowired
private SmsPluginFactory smsPluginFactory;
@Autowired
private SmsTemplateProperties smsTemplateProperties;
@Autowired
private SystemSettingProperties systemSettingProperties;
@Override
public void sendSmsCode(String mobile, VerificationEnums verificationEnums, String uuid) {
//获取短信配置
@@ -78,18 +70,18 @@ public class SmsUtilAliImplService implements SmsUtil {
switch (verificationEnums) {
//登录
case LOGIN: {
templateCode = smsTemplateProperties.getLOGIN();
templateCode = smsSetting.getLoginTemplateCode();
break;
}
//注册
case BIND_MOBILE:
case REGISTER: {
templateCode = smsTemplateProperties.getREGISTER();
templateCode = smsSetting.getRegisterTemplateCode();
break;
}
//找回密码
case FIND_USER: {
templateCode = smsTemplateProperties.getFIND_USER();
templateCode = smsSetting.getFindPasswordTemplateCode();
break;
}
//修改密码
@@ -100,7 +92,7 @@ public class SmsUtilAliImplService implements SmsUtil {
}
//更新为用户最新手机号
mobile = member.getMobile();
templateCode = smsTemplateProperties.getUPDATE_PASSWORD();
templateCode = smsSetting.getFindPasswordTemplateCode();
break;
}
//设置支付密码
@@ -108,7 +100,7 @@ public class SmsUtilAliImplService implements SmsUtil {
Member member = memberService.getById(UserContext.getCurrentUser().getId());
//更新为用户最新手机号
mobile = member.getMobile();
templateCode = smsTemplateProperties.getWALLET_PASSWORD();
templateCode = smsSetting.getWalletPasswordTemplateCode();
break;
}
//如果不是有效的验证码手段,则此处不进行短信操作
@@ -131,7 +123,7 @@ public class SmsUtilAliImplService implements SmsUtil {
@Override
public boolean verifyCode(String mobile, VerificationEnums verificationEnums, String uuid, String code) {
Object result = cache.get(cacheKey(verificationEnums, mobile, uuid));
if (code.equals(result) || code.equals("0")) {
if (code.equals(result) || "0".equals( code)) {
//校验之后,删除
cache.remove(cacheKey(verificationEnums, mobile, uuid));
return true;

View File

@@ -105,22 +105,22 @@ public class HuaweiSmsPlugin implements SmsPlugin {
}
// 发送短信
private void sendSms(String signName, String mobile, String param, String templateCode) throws Exception {
//必填,请参考"开发准备"获取如下数据,替换为实际值
String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1"; //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
String appKey = smsSetting.getHuaweiAppKey(); //APP_Key
String appSecret = smsSetting.getHuaweiAppSecret(); //APP_Secret
String sender = smsSetting.getHuaweiSender(); //国内短信签名通道号或国际/港澳台短信通道号
String templateId = templateCode; //模板ID
//APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1";
String appKey = smsSetting.getHuaweiAppKey();
String appSecret = smsSetting.getHuaweiAppSecret();
String sender = smsSetting.getHuaweiSender();
//条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
//国际/港澳台短信不用关注该参数
String signature = smsSetting.getHuaweiSignature(); //签名名称
// 模板ID
String templateId = templateCode;
//必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
String receiver = mobile; //短信接收人号码
// 签名名称
String signature = smsSetting.getHuaweiSignature();
//选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
String receiver = mobile;
String statusCallBack = "";
/**
@@ -130,7 +130,8 @@ public class HuaweiSmsPlugin implements SmsPlugin {
* 模板中的每个变量都必须赋值,且取值不能为空
* 查看更多模板和变量规范:产品介绍>模板和变量规范
*/
String templateParas = param; //模板变量此处以单变量验证码短信为例请客户自行生成6位验证码并定义为字符串类型以杜绝首位0丢失的问题例如002569变成了2569
//模板变量此处以单变量验证码短信为例请客户自行生成6位验证码并定义为字符串类型以杜绝首位0丢失的问题例如002569变成了2569
String templateParas = param;
//请求Body,不携带签名名称时,signature请填null
String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature);
@@ -179,12 +180,14 @@ public class HuaweiSmsPlugin implements SmsPlugin {
connection.connect();
out = new OutputStreamWriter(connection.getOutputStream());
out.write(body); //发送请求Body参数
// 发送请求Body参数
out.write(body);
out.flush();
out.close();
int status = connection.getResponseCode();
if (200 == status) { //200
if (200 == status) {
is = connection.getInputStream();
} else { //400/401
is = connection.getErrorStream();
@@ -194,7 +197,9 @@ public class HuaweiSmsPlugin implements SmsPlugin {
while ((line = in.readLine()) != null) {
result.append(line);
}
System.out.println(result.toString()); //打印响应消息实体
// 打印响应消息实体
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
@@ -275,8 +280,12 @@ public class HuaweiSmsPlugin implements SmsPlugin {
return null;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
String time = sdf.format(new Date()); //Created
String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce
// Created
String time = sdf.format(new Date());
// Nonce
String nonce = UUID.randomUUID().toString().replace("-", "");
MessageDigest md;
byte[] passwordDigest = null;
@@ -289,8 +298,8 @@ public class HuaweiSmsPlugin implements SmsPlugin {
e.printStackTrace();
}
//如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
// PasswordDigest
String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest);
//如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
//String passwordDigestBase64Str = Base64.encodeBase64String(passwordDigest); //PasswordDigest
//若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
@@ -303,14 +312,15 @@ public class HuaweiSmsPlugin implements SmsPlugin {
static void trustAllHttpsCertificates() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return;
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return;
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}

View File

@@ -0,0 +1,55 @@
package cn.lili.modules.statistics.entity.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 营业构成VO
* @author Bulbasaur
* @since 2025/08/25 7:07 下午
*/
@Data
public class BusinessCompositionDataVO {
//订单分类构成
@ApiModelProperty(value = "到店自提")
private Double storeSelf;
@ApiModelProperty(value = "快递发货")
private Double express;
@ApiModelProperty(value = "线上无需配送")
private Double online;
//营业收入
@ApiModelProperty(value = "商品销售")
private Double income;
@ApiModelProperty(value = "运费")
private Double freight;
@ApiModelProperty(value = "商品返现(分销返佣)")
private Double incomeBack;
@ApiModelProperty(value = "商品销售+费用构成")
private Double incomeComposition;
//退款统计
@ApiModelProperty(value = "退款订单笔数")
private Long refundOrderNum;
@ApiModelProperty(value = "退款金额")
private Double refund;
@ApiModelProperty(value = "退款率")
private Double refundRate;
//消费指标
@ApiModelProperty(value = "支付金额")
private Double pay;
@ApiModelProperty(value = "折后笔单价")
private Double price;
@ApiModelProperty(value = "支付人数")
private Long payNum;
@ApiModelProperty(value = "折后客单价")
private Double priceNum;
}

View File

@@ -0,0 +1,31 @@
package cn.lili.modules.statistics.entity.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 概览数据
* @author Bulbasaur
* @since 2025/08/25 7:07 下午
*/
@Data
public class OverViewDataVO {
@ApiModelProperty(value = "营业额")
private Double turnover;
@ApiModelProperty(value = "优惠金额")
private Double discount;
@ApiModelProperty(value = "营业收入不含数值储值金额")
private Double incomeNoStoreValue;
@ApiModelProperty(value = "支付订单数")
private Long payOrderNum;
@ApiModelProperty(value = "新增充值金额")
private Double recharge;
@ApiModelProperty(value = "使用充值金额")
private Long rechargeUse;
}

View File

@@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.Objects;
/**
* 流量数据展示VO
@@ -36,14 +37,14 @@ public class PlatformViewVO {
}
public Long getPvNum() {
if(pvNum==null){
if(Objects.isNull(pvNum)){
return 0L;
}
return pvNum;
}
public Long getUvNum() {
if(uvNum==null){
if(Objects.isNull(uvNum)){
return 0L;
}
return uvNum;

View File

@@ -0,0 +1,26 @@
package cn.lili.modules.statistics.entity.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 收款构成
*
* @author Bulbasaur
* @since 2025/08/25 7:07 下午
*/
@Data
public class SourceDataVO {
@ApiModelProperty(value = "支付方式")
private String payType;
@ApiModelProperty(value = "收款合计")
private Double total;
@ApiModelProperty(value = "营业收入")
private Double income;
@ApiModelProperty(value = "新增储值金额")
private Double recharge;
}

View File

@@ -1,6 +1,7 @@
package cn.lili.modules.statistics.mapper;
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.vo.OrderSimpleVO;
import cn.lili.modules.statistics.entity.vo.OrderStatisticsDataVO;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
@@ -60,4 +61,24 @@ public interface OrderStatisticsMapper extends BaseMapper<Order> {
" FROM li_order o INNER JOIN li_order_item AS oi on o.sn = oi.order_sn ${ew.customSqlSegment} ")
IPage<OrderSimpleVO> queryByParams(IPage<OrderSimpleVO> page, @Param(Constants.WRAPPER) Wrapper<OrderSimpleVO> queryWrapper);
/**
* 查询已付款未全部退款的订单数量
*/
@Select("SELECT COALESCE(COUNT(DISTINCT order_sn), 0) FROM li_order_item ${ew.customSqlSegment} ")
Long getPayOrderNum(@Param(Constants.WRAPPER) Wrapper<OrderItem> queryWrapper);
/**
* 查询已付款未全部退款的订单金额
*/
@Select("SELECT COALESCE(SUM( oi.flow_price )- SUM( oi.refund_price ), 0) FROM li_order_item oi INNER JOIN li_order o ON o.sn=oi.order_sn ${ew.customSqlSegment} ")
Double getPayOrderPrice(@Param(Constants.WRAPPER) Wrapper<OrderItem> queryWrapper);
/**
* 查询商品价格
*/
@Select("SELECT COALESCE(SUM(goods_price), 0) FROM li_order_item ${ew.customSqlSegment} ")
Double getGoodsPrice(@Param(Constants.WRAPPER) Wrapper<OrderItem> queryWrapper);
@Select("SELECT COALESCE(SUM( oi.refund_price ), 0) FROM li_order_item oi INNER JOIN li_order o ON o.sn=oi.order_sn ${ew.customSqlSegment} ")
Double getRefundPrice(@Param(Constants.WRAPPER) Wrapper<OrderItem> queryWrapper);
}

View File

@@ -1,14 +1,17 @@
package cn.lili.modules.statistics.service;
import cn.lili.common.vo.PageVO;
import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum;
import cn.lili.modules.order.order.entity.dos.Order;
import cn.lili.modules.order.order.entity.vo.OrderSimpleVO;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.statistics.entity.dto.StatisticsQueryParam;
import cn.lili.modules.statistics.entity.vo.OrderOverviewVO;
import cn.lili.modules.statistics.entity.vo.OrderStatisticsDataVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Date;
import java.util.List;
/**
@@ -34,6 +37,22 @@ public interface OrderStatisticsService extends IService<Order> {
* @return 订单总数量
*/
long orderNum(String orderStatus);
/**
* 获取订单总数量
*
* @param paymentMethod 支付方式
* @param dates 时间
*
* @return 订单总数量
*/
long orderNum(String paymentMethod, Date[] dates);
/**
* 获取所有优惠金额去除退款金额
* @param dates
* @return
*/
Double getDiscountPrice(Date[] dates);
/**
* 图表统计
@@ -51,4 +70,64 @@ public interface OrderStatisticsService extends IService<Order> {
* @return
*/
IPage<OrderSimpleVO> getStatistics(StatisticsQueryParam statisticsQueryParam, PageVO pageVO);
/**
* 获取付款订单数量 不含全部退款
*
* @param dates 时间
* @return 付款订单数量
*/
long getPayOrderNum(Date[] dates);
/**
* 获取付款订单金额去除退款金额
*
* @param dates 时间
* @param paymentMethodEnum 支付方式
* @param deliveryMethodEnum 配送方式
* @return 付款订单金额
*/
Double getPayOrderPrice(Date[] dates, PaymentMethodEnum paymentMethodEnum, DeliveryMethodEnum deliveryMethodEnum);
/**
* 获取商品价格
* @param dates 时间
* @return
*/
Double getGoodsPrice(Date[] dates);
/**
* 获取运费
* @param dates 时间
* @return
*/
Double getFreight(Date[] dates);
/**
* 获取分销返佣
* @param dates
* @return
*/
Double getDistribution(Date[] dates);
/**
* 获取退款订单数
* @param dates
* @return
*/
Long getRefundNum(Date[] dates);
/**
* 获取退款金额
* @param dates
* @return
*/
Double getRefundPrice(Date[] dates);
/**
* 获取退款率
* @param dates
* @return
*/
Double getRefundRate(Date[] dates);
}

View File

@@ -0,0 +1,47 @@
package cn.lili.modules.statistics.service;
import cn.lili.modules.statistics.entity.dto.StatisticsQueryParam;
import cn.lili.modules.statistics.entity.vo.BusinessCompositionDataVO;
import cn.lili.modules.statistics.entity.vo.OrderOverviewVO;
import cn.lili.modules.statistics.entity.vo.OverViewDataVO;
import cn.lili.modules.statistics.entity.vo.SourceDataVO;
import java.util.Date;
import java.util.List;
/**
* 营业概览统计
*
* @author Bulbasaur
* @since 2025/08/25 7:31 下午
*/
public interface OverViewStatisticsService {
/**
* 获取营业概览统计
*
* @param statisticsQueryParam 统计参数
* @return 营业概览统计
*/
OverViewDataVO getOverViewDataVO(StatisticsQueryParam statisticsQueryParam);
/**
* 获取收款构成列表
*
* @param statisticsQueryParam 统计参数
* @return 收款构成列表
*/
List<SourceDataVO> getSourceDataVOList(StatisticsQueryParam statisticsQueryParam);
/**
* 获取营业构成列表
*
* @param statisticsQueryParam 统计参数
* @return 营业构成列表
*/
BusinessCompositionDataVO businessCompositionDataVO(StatisticsQueryParam statisticsQueryParam);
}

View File

@@ -6,10 +6,17 @@ import cn.lili.common.security.enums.UserEnums;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.common.utils.StringUtils;
import cn.lili.common.vo.PageVO;
import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum;
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.PriceDetailDTO;
import cn.lili.modules.order.order.entity.enums.FlowTypeEnum;
import cn.lili.modules.order.order.entity.enums.OrderTypeEnum;
import cn.lili.modules.order.order.entity.enums.PayStatusEnum;
import cn.lili.modules.order.order.entity.enums.RefundStatusEnum;
import cn.lili.modules.order.order.entity.vo.OrderSimpleVO;
import cn.lili.modules.order.order.service.OrderItemService;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.statistics.entity.dto.StatisticsQueryParam;
import cn.lili.modules.statistics.entity.vo.OrderOverviewVO;
import cn.lili.modules.statistics.entity.vo.OrderStatisticsDataVO;
@@ -38,6 +45,8 @@ import java.util.*;
@Service
public class OrderStatisticsServiceImpl extends ServiceImpl<OrderStatisticsMapper, Order> implements OrderStatisticsService {
/**
* 平台PV统计
*/
@@ -46,6 +55,8 @@ public class OrderStatisticsServiceImpl extends ServiceImpl<OrderStatisticsMappe
@Autowired
private StoreFlowStatisticsService storeFlowStatisticsService;
@Autowired
private OrderItemService orderItemService;
@Override
public OrderOverviewVO overview(StatisticsQueryParam statisticsQueryParam) {
@@ -104,6 +115,197 @@ public class OrderStatisticsServiceImpl extends ServiceImpl<OrderStatisticsMappe
return this.count(queryWrapper);
}
@Override
public long orderNum(String paymentMethod, Date[] dates) {
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CharSequenceUtil.isNotEmpty(paymentMethod), Order::getPaymentMethod, paymentMethod);
queryWrapper.between(Order::getCreateTime, dates[0], dates[1]);
return this.count(queryWrapper);
}
@Override
public Double getDiscountPrice(Date[] dates) {
// 参数校验
if (dates == null || dates.length < 2) {
return 0.0;
}
// 构建查询条件:按时间范围过滤,排除全部退款的订单项
LambdaQueryWrapper<OrderItem> queryWrapper = new LambdaQueryWrapper<OrderItem>()
.ne(OrderItem::getIsRefund, RefundStatusEnum.ALL_REFUND.name())
.between(OrderItem::getCreateTime, dates[0], dates[1]);
List<OrderItem> orderItems = orderItemService.list(queryWrapper);
if (orderItems.isEmpty()) {
return 0.0;
}
Double totalDiscountPrice = 0.0;
for (OrderItem orderItem : orderItems) {
PriceDetailDTO priceDetailDTO = orderItem.getPriceDetailDTO();
if (priceDetailDTO == null) {
continue;
}
Double itemDiscountPrice = calculateItemDiscountPrice(priceDetailDTO);
if (RefundStatusEnum.NO_REFUND.name().equals(orderItem.getIsRefund())) {
// 未退款:计算全部优惠金额
totalDiscountPrice = CurrencyUtil.add(totalDiscountPrice, itemDiscountPrice);
} else {
// 部分退款:按比例计算剩余优惠金额
Double remainingDiscountPrice = calculateRemainingDiscountPrice(
itemDiscountPrice, orderItem.getNum(), orderItem.getReturnGoodsNumber());
totalDiscountPrice = CurrencyUtil.add(totalDiscountPrice, remainingDiscountPrice);
}
}
return totalDiscountPrice;
}
@Override
public long getPayOrderNum(Date[] dates) {
LambdaQueryWrapper<OrderItem> orderItemLambdaQueryWrapper=new LambdaQueryWrapper<>();
orderItemLambdaQueryWrapper.between(OrderItem::getCreateTime,dates[0], dates[1]);
orderItemLambdaQueryWrapper.ne(OrderItem::getIsRefund,RefundStatusEnum.ALL_REFUND.name());
return this.baseMapper.getPayOrderNum(orderItemLambdaQueryWrapper);
}
@Override
public Double getPayOrderPrice(Date[] dates, PaymentMethodEnum paymentMethodEnum, DeliveryMethodEnum deliveryMethodEnum) {
//查看付款金额
QueryWrapper queryWrapper = Wrappers.query();
queryWrapper.between("oi.create_time", dates[0], dates[1]);
queryWrapper.ne("oi.is_refund", RefundStatusEnum.ALL_REFUND.name());
if(Objects.nonNull(paymentMethodEnum)){
queryWrapper.eq("o.payment_method",paymentMethodEnum.name());
}
if(Objects.nonNull(deliveryMethodEnum)){
if(DeliveryMethodEnum.VIRTUAL.equals(deliveryMethodEnum)){
queryWrapper.eq("o.order_type", OrderTypeEnum.VIRTUAL.name());
}else{
queryWrapper.eq("o.delivery_method",deliveryMethodEnum.name());
}
}
return this.baseMapper.getPayOrderPrice(queryWrapper);
}
@Override
public Double getGoodsPrice(Date[] dates) {
LambdaQueryWrapper<OrderItem> orderItemLambdaQueryWrapper=new LambdaQueryWrapper<>();
orderItemLambdaQueryWrapper.between(OrderItem::getCreateTime,dates[0], dates[1]);
orderItemLambdaQueryWrapper.ne(OrderItem::getIsRefund,RefundStatusEnum.ALL_REFUND.name());
return this.baseMapper.getGoodsPrice(orderItemLambdaQueryWrapper);
}
@Override
public Double getFreight(Date[] dates) {
LambdaQueryWrapper<OrderItem> orderItemLambdaQueryWrapper=new LambdaQueryWrapper<>();
orderItemLambdaQueryWrapper.between(OrderItem::getCreateTime,dates[0], dates[1]);
orderItemLambdaQueryWrapper.ne(OrderItem::getIsRefund,RefundStatusEnum.ALL_REFUND.name());
List<OrderItem> orderItems=orderItemService.list(orderItemLambdaQueryWrapper);
Double freight=0D;
for (OrderItem orderItem:orderItems){
PriceDetailDTO priceDetailDTO=orderItem.getPriceDetailDTO();
freight=CurrencyUtil.add(freight,priceDetailDTO.getFreightPrice());
}
return freight;
}
@Override
public Double getDistribution(Date[] dates) {
LambdaQueryWrapper<OrderItem> orderItemLambdaQueryWrapper=new LambdaQueryWrapper<>();
orderItemLambdaQueryWrapper.between(OrderItem::getCreateTime,dates[0], dates[1]);
orderItemLambdaQueryWrapper.ne(OrderItem::getIsRefund,RefundStatusEnum.ALL_REFUND.name());
List<OrderItem> orderItems=orderItemService.list(orderItemLambdaQueryWrapper);
Double distributionCommission=0D;
for (OrderItem orderItem:orderItems){
PriceDetailDTO priceDetailDTO=orderItem.getPriceDetailDTO();
distributionCommission=CurrencyUtil.add(distributionCommission,priceDetailDTO.getDistributionCommission());
}
return distributionCommission;
}
@Override
public Long getRefundNum(Date[] dates) {
LambdaQueryWrapper<OrderItem> orderItemLambdaQueryWrapper=new LambdaQueryWrapper<>();
orderItemLambdaQueryWrapper.between(OrderItem::getCreateTime,dates[0], dates[1]);
orderItemLambdaQueryWrapper.eq(OrderItem::getIsRefund,RefundStatusEnum.ALL_REFUND.name());
return this.baseMapper.getPayOrderNum(orderItemLambdaQueryWrapper);
}
@Override
public Double getRefundPrice(Date[] dates) {
QueryWrapper queryWrapper = Wrappers.query();
queryWrapper.between("oi.create_time", dates[0], dates[1]);
queryWrapper.eq("oi.is_refund", RefundStatusEnum.ALL_REFUND.name());
return this.baseMapper.getRefundPrice(queryWrapper);
}
@Override
public Double getRefundRate(Date[] dates) {
QueryWrapper queryWrapper = Wrappers.query();
queryWrapper.between("create_time", dates[0], dates[1]);
Long orderNum= this.baseMapper.getPayOrderNum(queryWrapper);
return CurrencyUtil.mul(CurrencyUtil.div(this.getRefundNum(dates),orderNum),100);
}
/**
* 计算订单项的优惠金额
*/
private Double calculateItemDiscountPrice(PriceDetailDTO priceDetailDTO) {
Double discountPrice = priceDetailDTO.getDiscountPrice() != null ? priceDetailDTO.getDiscountPrice() : 0.0;
Double couponPrice = priceDetailDTO.getCouponPrice() != null ? priceDetailDTO.getCouponPrice() : 0.0;
return CurrencyUtil.add(discountPrice, couponPrice);
}
/**
* 计算部分退款后的剩余优惠金额
*/
private Double calculateRemainingDiscountPrice(Double totalDiscountPrice, Integer totalNum, Integer returnNum) {
if (totalNum == null || totalNum <= 0 || returnNum == null || returnNum < 0) {
return totalDiscountPrice;
}
Integer remainingNum = totalNum - returnNum;
if (remainingNum <= 0) {
return 0.0;
}
// 按剩余数量比例计算优惠金额
Double ratio = CurrencyUtil.div(remainingNum.doubleValue(), totalNum.doubleValue(), 4);
return CurrencyUtil.mul(totalDiscountPrice, ratio);
}
@Override
public List<OrderStatisticsDataVO> statisticsChart(StatisticsQueryParam statisticsQueryParam) {
Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam);

View File

@@ -0,0 +1,137 @@
package cn.lili.modules.statistics.serviceimpl;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.modules.order.cart.entity.enums.DeliveryMethodEnum;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.statistics.entity.dto.StatisticsQueryParam;
import cn.lili.modules.statistics.entity.vo.BusinessCompositionDataVO;
import cn.lili.modules.statistics.entity.vo.OrderOverviewVO;
import cn.lili.modules.statistics.entity.vo.OverViewDataVO;
import cn.lili.modules.statistics.entity.vo.SourceDataVO;
import cn.lili.modules.statistics.service.OrderStatisticsService;
import cn.lili.modules.statistics.service.OverViewStatisticsService;
import cn.lili.modules.statistics.util.StatisticsDateUtil;
import cn.lili.modules.wallet.service.RechargeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author Bulbasaur
* @since 2025/08/25 7:07 下午
*
*/
@Service
public class OverViewStatisticsServiceImpl implements OverViewStatisticsService {
@Autowired
private RechargeService rechargeService;
@Autowired
private OrderStatisticsService orderStatisticsService;
@Override
public OverViewDataVO getOverViewDataVO(StatisticsQueryParam statisticsQueryParam) {
Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam);
OverViewDataVO overViewDataVO = new OverViewDataVO();
//营业收入不含数值储值金额.订单扣除退款折扣后金额(不含储值充值)
overViewDataVO.setIncomeNoStoreValue(orderStatisticsService.getPayOrderPrice(dates,null,null));
//优惠金额.订单的优惠金额(扣除退款,不含储值充值)
overViewDataVO.setDiscount(orderStatisticsService.getDiscountPrice( dates));
//营业额.营业额=营业收入+优惠金额,订单扣除退款折扣前金额(不含储值充值)
overViewDataVO.setTurnover(CurrencyUtil.add(overViewDataVO.getIncomeNoStoreValue(), overViewDataVO.getDiscount()));
//支付订单数.按客户支付完成时间统计的成功付款的订单数(不含退款、储值充值)
overViewDataVO.setPayOrderNum(orderStatisticsService.getPayOrderNum(dates));
//新增充值金额.按客户支付完成时间统计的客户储值充值本金金额
overViewDataVO.setRecharge(rechargeService.getRecharge(dates,null));
//使用充值金额.使用储值支付本金金额:统计时间范围内,支付和退款成功的订单中,使用储值支付且扣除退款的金额
overViewDataVO.setRechargeUse(orderStatisticsService.orderNum(PaymentMethodEnum.WALLET.name(), dates));
return overViewDataVO;
}
@Override
public List<SourceDataVO> getSourceDataVOList(StatisticsQueryParam statisticsQueryParam) {
Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam);
List<SourceDataVO> sourceDataVOList=new ArrayList<>();
//微信
SourceDataVO sourceDataVO=new SourceDataVO();
sourceDataVO.setPayType(PaymentMethodEnum.WECHAT.paymentName());
sourceDataVO.setIncome(orderStatisticsService.getPayOrderPrice(dates,PaymentMethodEnum.WECHAT,null));
sourceDataVO.setRecharge(rechargeService.getRecharge(dates,PaymentMethodEnum.WECHAT));
sourceDataVO.setTotal(CurrencyUtil.add(sourceDataVO.getIncome(),sourceDataVO.getRecharge()));
sourceDataVOList.add(sourceDataVO);
//支付宝
SourceDataVO zhifubao=new SourceDataVO();
zhifubao.setPayType(PaymentMethodEnum.ALIPAY.paymentName());
zhifubao.setIncome(orderStatisticsService.getPayOrderPrice(dates,PaymentMethodEnum.ALIPAY,null));
zhifubao.setRecharge(rechargeService.getRecharge(dates,PaymentMethodEnum.ALIPAY));
zhifubao.setTotal(CurrencyUtil.add(zhifubao.getIncome(),zhifubao.getRecharge()));
sourceDataVOList.add(zhifubao);
//线下支付
SourceDataVO bankTransfer=new SourceDataVO();
bankTransfer.setPayType(PaymentMethodEnum.BANK_TRANSFER.paymentName());
bankTransfer.setIncome(orderStatisticsService.getPayOrderPrice(dates,PaymentMethodEnum.BANK_TRANSFER,null));
bankTransfer.setRecharge(rechargeService.getRecharge(dates,PaymentMethodEnum.BANK_TRANSFER));
bankTransfer.setTotal(CurrencyUtil.add(bankTransfer.getIncome(),bankTransfer.getRecharge()));
sourceDataVOList.add(bankTransfer);
//余额
SourceDataVO wallet=new SourceDataVO();
wallet.setPayType(PaymentMethodEnum.WALLET.paymentName());
wallet.setIncome(orderStatisticsService.getPayOrderPrice(dates,PaymentMethodEnum.WALLET,null));
wallet.setRecharge(rechargeService.getRecharge(dates,PaymentMethodEnum.WALLET));
wallet.setTotal(CurrencyUtil.add(wallet.getIncome(),wallet.getRecharge()));
sourceDataVOList.add(wallet);
return sourceDataVOList;
}
@Override
public BusinessCompositionDataVO businessCompositionDataVO(StatisticsQueryParam statisticsQueryParam) {
Date[] dates = StatisticsDateUtil.getDateArray(statisticsQueryParam);
BusinessCompositionDataVO businessCompositionDataVO=new BusinessCompositionDataVO();
//-----订单分类构成-----
businessCompositionDataVO.setStoreSelf(orderStatisticsService.getPayOrderPrice(dates,null, DeliveryMethodEnum.SELF_PICK_UP));
businessCompositionDataVO.setExpress(orderStatisticsService.getPayOrderPrice(dates,null, DeliveryMethodEnum.LOGISTICS));
businessCompositionDataVO.setOnline(orderStatisticsService.getPayOrderPrice(dates,null, DeliveryMethodEnum.VIRTUAL));
//-----营业收入-----
//商品销售
businessCompositionDataVO.setIncome(orderStatisticsService.getGoodsPrice(dates));
//运费
businessCompositionDataVO.setFreight(orderStatisticsService.getFreight(dates));
//商品返现(分销返佣)
businessCompositionDataVO.setIncomeBack(orderStatisticsService.getDistribution(dates));
//商品销售+费用构成
businessCompositionDataVO.setIncomeComposition(
CurrencyUtil.sub(CurrencyUtil.add(businessCompositionDataVO.getIncome(), businessCompositionDataVO.getFreight()),
businessCompositionDataVO.getIncomeBack()));
//-----退款统计-----
//退款订单笔数
businessCompositionDataVO.setRefundOrderNum(orderStatisticsService.getRefundNum(dates));
//退款金额
businessCompositionDataVO.setRefund(orderStatisticsService.getRefundPrice(dates));
//退款率
businessCompositionDataVO.setRefundRate(orderStatisticsService.getRefundRate(dates));
//-----消费指标-----
//支付金额
OrderOverviewVO overview=orderStatisticsService.overview(statisticsQueryParam);
businessCompositionDataVO.setPay(overview.getPaymentAmount());
//折后笔单价
businessCompositionDataVO.setPrice(CurrencyUtil.div(overview.getPaymentAmount(),overview.getPaymentOrderNum()));
//支付人数
businessCompositionDataVO.setPayNum(overview.getPaymentsNum());
//折后客单价
businessCompositionDataVO.setPriceNum(CurrencyUtil.div(overview.getPaymentAmount(),overview.getPaymentsNum()));
return businessCompositionDataVO;
}
}

View File

@@ -33,16 +33,16 @@ public class StoreLogistics extends BaseEntity {
@NotNull
private String logisticsId;
@ApiModelProperty(value = "客户代码")
@ApiModelProperty(value = "电子面单客户账户、月结账号、客户代码")
private String customerName;
@ApiModelProperty(value = "客户密码")
@ApiModelProperty(value = "客户密码、电子面单密码")
private String customerPwd;
@ApiModelProperty(value = "密钥")
@ApiModelProperty(value = "电子面单密钥")
private String monthCode;
@ApiModelProperty(value = "归属网点/网点编码")
@ApiModelProperty(value = "归属网点/网点编码,电子面单承载编号")
private String sendSite;
@ApiModelProperty(value = "收件快递员")
@@ -57,6 +57,11 @@ public class StoreLogistics extends BaseEntity {
@ApiModelProperty(value = "快递类型")
private String expType;
@ApiModelProperty(value = "电子面单客户账户名称")
private String partnerName;
public StoreLogistics(StoreLogisticsCustomerDTO storeLogisticsCustomerDTO){
this.customerName=storeLogisticsCustomerDTO.getCustomerName();
this.customerPwd=storeLogisticsCustomerDTO.getCustomerPwd();
@@ -66,6 +71,7 @@ public class StoreLogistics extends BaseEntity {
this.faceSheetFlag=storeLogisticsCustomerDTO.isFaceSheetFlag();
this.payType = storeLogisticsCustomerDTO.getPayType();
this.expType = storeLogisticsCustomerDTO.getExpType();
this.partnerName = storeLogisticsCustomerDTO.getPartnerName();
}

View File

@@ -38,4 +38,7 @@ public class StoreLogisticsCustomerDTO {
@ApiModelProperty(value = "快递类型")
private String expType;
@ApiModelProperty(value = "电子面单客户账户名称")
private String partnerName;
}

View File

@@ -194,7 +194,7 @@ public class BillServiceImpl extends ServiceImpl<BillMapper, Bill> implements Bi
@Override
public IPage<BillListVO> billPage(BillSearchParams billSearchParams) {
QueryWrapper<BillListVO> queryWrapper = billSearchParams.queryWrapper();
QueryWrapper<BillListVO> queryWrapper = billSearchParams.queryWrapperBillList();
return this.baseMapper.queryBillPage(PageUtil.initPage(billSearchParams), queryWrapper);
}

View File

@@ -1,15 +1,15 @@
package cn.lili.modules.system.aspect.interceptor;
import cn.lili.modules.system.aspect.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.IpUtils;
import cn.lili.common.utils.SpelUtil;
import cn.lili.common.utils.ThreadPoolUtil;
import cn.lili.modules.permission.entity.vo.SystemLogVO;
import cn.lili.common.utils.IpUtils;
import cn.lili.modules.permission.service.SystemLogService;
import cn.lili.modules.system.aspect.annotation.SystemLogPoint;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
@@ -37,7 +37,7 @@ import java.util.Map;
public class SystemLogAspect {
/**
* 启动线程异步记录日志
* 记录方法开始时间
*/
private static final ThreadLocal<Date> BEGIN_TIME_THREAD_LOCAL = new NamedThreadLocal<>("SYSTEM-LOG");
@@ -75,6 +75,11 @@ public class SystemLogAspect {
@AfterReturning(returning = "rvt", pointcut = "controllerAspect()")
public void after(JoinPoint joinPoint, Object rvt) {
try {
if (request == null || rvt == null) {
return;
}
Map map = spelFormat(joinPoint, rvt);
String description = map.get("description").toString();
String customerLog = map.get("customerLog").toString();

View File

@@ -37,22 +37,36 @@ public class WechatPaymentSetting {
* 商户号
*/
private String mchId;
/**
* 私钥
*/
private String apiclient_key;
/**
* pem 证书
*/
private String apiclient_cert_pem;
/**
* p12 证书
*/
private String apiclient_cert_p12;
/**
* 商户证书序列号
*/
private String serialNumber;
/**
* 私钥
*/
private String apiclientKey;
/**
* 公钥ID
*/
private String publicId;
/**
* 公钥
*/
private String publicKey;
/**
* 微信验证方式:公钥/证书(KEY/CERT)
*/
private String publicType;
// /**
// * pem 证书
// */
// private String apiclient_cert_pem;
// /**
// * p12 证书
// */
// private String apiclient_cert_p12;
/**
* apiv3私钥
*/

View File

@@ -27,6 +27,7 @@ public interface RegionService extends IService<Region> {
* @return
*/
@CacheEvict(allEntries = true)
@Override
boolean updateById(Region region);
/**
* 更新地区
@@ -35,6 +36,7 @@ public interface RegionService extends IService<Region> {
* @return
*/
@CacheEvict(allEntries = true)
@Override
boolean save(Region region);

View File

@@ -40,4 +40,13 @@ public interface VerificationService {
* @return 操作结果
*/
boolean check(String uuid, VerificationEnums verificationEnums);
/**
* 检测是否可以生成验证码
*
* @param type 验证码类型
* @param filePath 文件路径
*/
Boolean checkCreateVerification(String type, String filePath);
}

View File

@@ -1,5 +1,6 @@
package cn.lili.modules.verification.service.impl;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.cache.Cache;
import cn.lili.cache.CachePrefix;
import cn.lili.common.enums.ResultCode;
@@ -11,6 +12,7 @@ import cn.lili.modules.verification.SliderImageUtil;
import cn.lili.modules.verification.entity.dos.VerificationSource;
import cn.lili.modules.verification.entity.dto.VerificationDTO;
import cn.lili.modules.verification.entity.enums.VerificationEnums;
import cn.lili.modules.verification.entity.enums.VerificationSourceEnum;
import cn.lili.modules.verification.service.VerificationService;
import cn.lili.modules.verification.service.VerificationSourceService;
import lombok.extern.slf4j.Slf4j;
@@ -185,5 +187,47 @@ public class VerificationServiceImpl implements VerificationService {
return CachePrefix.VERIFICATION_RESULT.getPrefix() + verificationEnums.name() + uuid;
}
@Override
public Boolean checkCreateVerification(String type, String filePath) {
if (CharSequenceUtil.isBlank(type) || CharSequenceUtil.isBlank(filePath)) {
throw new ServiceException(ResultCode.ILLEGAL_REQUEST_ERROR);
}
//获取验证码配置
VerificationDTO verificationDTO = verificationSourceService.getVerificationCache();
List<VerificationSource> verificationResources = verificationDTO.getVerificationResources();
List<VerificationSource> verificationSlider = verificationDTO.getVerificationSlider();
//随机选择需要切的图片地址
String originalResource = verificationResources.get(0).getResource();
//随机选择剪切模版图片地址
String sliderResource = verificationSlider.get(0).getResource();
if (VerificationSourceEnum.RESOURCE.name().equals(type)) {
originalResource = filePath;
} else if (VerificationSourceEnum.SLIDER.name().equals(type)) {
sliderResource = filePath;
}
try {
//获取缓存中的资源
SerializableStream originalFile = getInputStream(originalResource);
SerializableStream sliderFile = getInputStream(sliderResource);
//生成数据
SliderImageUtil.pictureTemplatesCut(
sliderFile, sliderFile, originalFile,
verificationCodeProperties.getWatermark(),
verificationCodeProperties.getInterfereNum()
);
} catch (Exception e) {
throw new ServiceException("当前图片不符合规则,请上传正确格式的图片!");
}
return true;
}
}

View File

@@ -1,7 +1,12 @@
package cn.lili.modules.wallet.mapper;
import cn.lili.modules.order.order.entity.vo.PaymentLog;
import cn.lili.modules.wallet.entity.dos.Recharge;
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;
/**
* 预存款充值记录数据处理层
@@ -11,4 +16,13 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface RechargeMapper extends BaseMapper<Recharge> {
/**
* 获取会员预存款
*
* @return 会员预存款
*/
@Select("SELECT COALESCE(SUM(recharge_money), 0) FROM li_recharge ${ew.customSqlSegment}")
Double getRecharge(@Param(Constants.WRAPPER) Wrapper<Recharge> queryWrapper);
}

View File

@@ -2,10 +2,13 @@ package cn.lili.modules.wallet.service;
import cn.lili.common.vo.PageVO;
import cn.lili.modules.order.trade.entity.vo.RechargeQueryVO;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.wallet.entity.dos.Recharge;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Date;
/**
* 预存款充值业务层
*
@@ -56,4 +59,10 @@ public interface RechargeService extends IService<Recharge> {
*/
void rechargeOrderCancel(String sn);
/**
* 获取周期内的充值金额
*
* @return
*/
Double getRecharge(Date[] dates, PaymentMethodEnum paymentMethodEnum);
}

View File

@@ -10,6 +10,7 @@ import cn.lili.common.utils.SnowFlake;
import cn.lili.common.vo.PageVO;
import cn.lili.modules.order.order.entity.enums.PayStatusEnum;
import cn.lili.modules.order.trade.entity.vo.RechargeQueryVO;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.wallet.entity.dos.Recharge;
import cn.lili.modules.wallet.entity.dto.MemberWalletUpdateDTO;
import cn.lili.modules.wallet.entity.enums.DepositServiceTypeEnum;
@@ -17,6 +18,7 @@ import cn.lili.modules.wallet.mapper.RechargeMapper;
import cn.lili.modules.wallet.service.MemberWalletService;
import cn.lili.modules.wallet.service.RechargeService;
import cn.lili.mybatis.util.PageUtil;
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;
@@ -24,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Objects;
/**
* 预存款业务层实现
@@ -116,4 +119,15 @@ public class RechargeServiceImpl extends ServiceImpl<RechargeMapper, Recharge> i
this.updateById(recharge);
}
}
@Override
public Double getRecharge(Date[] dates, PaymentMethodEnum paymentMethodEnum) {
LambdaQueryWrapper<Recharge> queryWrapper = new LambdaQueryWrapper<Recharge>();
queryWrapper.eq(Recharge::getPayStatus, PayStatusEnum.PAID.name());
queryWrapper.between(Recharge::getPayTime, dates[0], dates[1]);
if(Objects.nonNull(paymentMethodEnum)){
queryWrapper.eq(Recharge::getRechargeWay,paymentMethodEnum.name());
}
return this.baseMapper.getRecharge(queryWrapper);
}
}

Some files were not shown because too many files have changed in this diff Show More