Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5b1c38f71 | ||
|
|
e1f834313a | ||
|
|
dedcc0a556 | ||
|
|
33fcce1cc9 | ||
|
|
85c8c5d021 | ||
|
|
2ab2c4ff5d | ||
|
|
feff49c79b | ||
|
|
c813ed0062 | ||
|
|
f658d43a5a | ||
|
|
fe0b9c37a4 | ||
|
|
28cc0617c3 | ||
|
|
f2cb6b56ea | ||
|
|
81b7da07f4 | ||
|
|
db3f775c50 | ||
|
|
93a45319ac | ||
|
|
7627614284 | ||
|
|
ca5887028a | ||
|
|
1a3736fd29 | ||
|
|
09d00260f6 | ||
|
|
2e019ef933 | ||
|
|
6acc99c7cf | ||
|
|
b57e7813d6 | ||
|
|
09e6f4a1a1 | ||
|
|
872bb220dc | ||
|
|
2498e57600 | ||
|
|
cfbb673387 | ||
|
|
05db6501f7 | ||
|
|
a5aa090f4a | ||
|
|
09aae17a02 | ||
|
|
7f5739f89b | ||
|
|
131d4eb156 | ||
|
|
ffa0b0db30 | ||
|
|
7bd0084802 | ||
|
|
d88762f3ab | ||
|
|
ebdbed6d08 | ||
|
|
efdf505e63 | ||
|
|
98f25179d3 | ||
|
|
9a0c7dd73d | ||
|
|
0cf464e549 | ||
|
|
2e6ddeafdd | ||
|
|
633b94c375 | ||
|
|
6441018d95 | ||
|
|
e706431df5 | ||
|
|
db1a3566ae | ||
|
|
9225b4ff10 | ||
|
|
586a507bb1 | ||
|
|
034f29c734 | ||
|
|
478dd1d201 | ||
|
|
554aed024c | ||
|
|
b2e0fe1de4 | ||
|
|
b69a558df0 | ||
|
|
2f180a73ed | ||
|
|
82f3223a03 | ||
|
|
cfe48d539b | ||
|
|
8ef00ae285 | ||
|
|
ed032653ee | ||
|
|
9108a1585c | ||
|
|
d4855b702b | ||
|
|
40822ca05b | ||
|
|
4dac80f084 | ||
|
|
8609216ef5 | ||
|
|
9678c7f506 | ||
|
|
63cdebdf1f | ||
|
|
e89be8eb8c | ||
|
|
3559971a8d | ||
|
|
dc1d1a7e7e | ||
|
|
10ecce1e0d | ||
|
|
2520c27f77 | ||
|
|
63d684c972 | ||
|
|
35739b64a3 | ||
|
|
ddcda57a08 | ||
|
|
16ae69ed06 | ||
|
|
fd067d8abf | ||
|
|
42b3c72977 |
30
.cursor/rules/01-project-overview.mdc
Normal file
30
.cursor/rules/01-project-overview.mdc
Normal 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
|
||||
38
.cursor/rules/02-technical-architecture.mdc
Normal file
38
.cursor/rules/02-technical-architecture.mdc
Normal 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
|
||||
43
.cursor/rules/03-development-guidelines.mdc
Normal file
43
.cursor/rules/03-development-guidelines.mdc
Normal 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
|
||||
37
.cursor/rules/04-deployment-guide.mdc
Normal file
37
.cursor/rules/04-deployment-guide.mdc
Normal 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
14
DB/index.sql
Normal 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);
|
||||
@@ -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
286
README.md
@@ -1,205 +1,167 @@
|
||||
## Lilishop 商城系统
|
||||
# Lilishop B2B2C 商城系统
|
||||
|
||||
[](https://github.com/hongyehuicheng/lilishop)
|
||||
[](https://gitee.com/beijing_hongye_huicheng/lilishop)
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.html)
|
||||
[](https://spring.io/projects/spring-boot)
|
||||
[](https://vuejs.org/)
|
||||
[](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)**:
|
||||

|
||||
|
||||
#### 欢迎交流需求,交流业务,交流技术(基础问题自行解决,其他问题先看文档后提问)
|
||||
---
|
||||
|
||||
#### 不用削尖脑袋往老群里加,老群活跃度较低,很多潜水党,新群相对而言活跃一些 :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 体验,扫描二维码
|
||||
---
|
||||
|
||||

|
||||
### 5. 技术架构
|
||||
|
||||
[](https://gitee.com/beijing_hongye_huicheng/lilishop/stargazers)
|
||||

|
||||
#### 5.1 架构图
|
||||

|
||||
|
||||
#### 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**:扫描二维码
|
||||
|
||||

|
||||
|
||||
### 快速本地部署
|
||||
|
||||
[点击跳转](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"/>
|
||||
|
||||
#### 平台管理端
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
### 技术选型
|
||||
### 8. 开源与授权
|
||||
|
||||
#### 架构图
|
||||
1. **开源协议**: 本项目遵循 `AGPL-3.0` 开源协议。
|
||||
2. **使用范围**: 仅允许用于个人学习、研究和非商业用途。
|
||||
3. **禁止行为**: 禁止将本项目的代码和资源用于任何形式的商业销售。
|
||||
4. **商业授权**: 如需商业使用,必须获得官方授权。授权为一次性永久授权,并提供持续的版本升级服务。详情请联系官网客服。
|
||||
5. **软件著作权**: 本软件受国家计算机软件著作权保护(登记号:2021SR0805085)。
|
||||
|
||||

|
||||
---
|
||||
|
||||
##### 后台技术选型
|
||||
### 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群**:
|
||||

|
||||
|
||||
| 说明 | 框架 | 说明 | 框架 |
|
||||
| ---------- | ---------- | ---------- | ------- |
|
||||
| 构建工具 | 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>
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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=》返回A,B包含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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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字符串转换为字节
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 "手机号码格式不正确";
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
//创建索引
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品查询
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
//过滤商品咨询敏感词
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* 查询导出售后信息
|
||||
*
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -19,7 +19,11 @@ public enum DeliveryMethodEnum {
|
||||
/**
|
||||
* "物流"
|
||||
*/
|
||||
LOGISTICS("物流");
|
||||
LOGISTICS("物流"),
|
||||
/**
|
||||
* 虚拟发货
|
||||
*/
|
||||
VIRTUAL("虚拟发货");
|
||||
|
||||
private final String description;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 + "]");
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ public enum DeliverStatusEnum {
|
||||
* 发货状态
|
||||
*/
|
||||
UNDELIVERED("未发货"),
|
||||
PARTS_DELIVERED("部分发货"),
|
||||
DELIVERED("已发货"),
|
||||
RECEIVED("已收货");
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ public enum OrderStatusEnum {
|
||||
UNPAID("未付款"),
|
||||
PAID("已付款"),
|
||||
UNDELIVERED("待发货"),
|
||||
PARTS_DELIVERED("部分发货"),
|
||||
DELIVERED("已发货"),
|
||||
COMPLETED("已完成"),
|
||||
STAY_PICKED_UP("待自提"),
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
/**
|
||||
* 查询订单信息
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* 订单信息
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取证书
|
||||
*
|
||||
|
||||
@@ -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("私钥数据为空");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查积分商品是否重复存在
|
||||
*
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -19,6 +19,18 @@ import java.util.Map;
|
||||
**/
|
||||
public interface EsGoodsIndexService {
|
||||
|
||||
/**
|
||||
* 删除下架商品索引
|
||||
*/
|
||||
Boolean deleteGoodsDown();
|
||||
|
||||
/**
|
||||
* 删除不存在的索引
|
||||
* @return
|
||||
*/
|
||||
Boolean delSkuIndex();
|
||||
|
||||
Boolean goodsCache();
|
||||
/**
|
||||
* 全局索引数据初始化
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,4 +38,7 @@ public class StoreLogisticsCustomerDTO {
|
||||
@ApiModelProperty(value = "快递类型")
|
||||
private String expType;
|
||||
|
||||
@ApiModelProperty(value = "电子面单客户账户名称")
|
||||
private String partnerName;
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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私钥
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
@@ -40,4 +40,13 @@ public interface VerificationService {
|
||||
* @return 操作结果
|
||||
*/
|
||||
boolean check(String uuid, VerificationEnums verificationEnums);
|
||||
|
||||
|
||||
/**
|
||||
* 检测是否可以生成验证码
|
||||
*
|
||||
* @param type 验证码类型
|
||||
* @param filePath 文件路径
|
||||
*/
|
||||
Boolean checkCreateVerification(String type, String filePath);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user