Merge remote-tracking branch 'origin/dev_xm' into glt_xm
# Conflicts: # src/main/resources/application-dev.yml # src/main/resources/application-local.yml
This commit is contained in:
61
.workbuddy/expert-history.json
Normal file
61
.workbuddy/expert-history.json
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"sessions": {
|
||||||
|
"7759a9e57f984a0bb5af2ffd05be2f63": [
|
||||||
|
{
|
||||||
|
"expertId": "SeniorDeveloper",
|
||||||
|
"name": "Will",
|
||||||
|
"profession": "高级开发工程师",
|
||||||
|
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png",
|
||||||
|
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md",
|
||||||
|
"usedAt": 1775972794982,
|
||||||
|
"industryId": "all"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"e7c3c15a2556446884e56ce4d588e133": [
|
||||||
|
{
|
||||||
|
"expertId": "SeniorDeveloper",
|
||||||
|
"name": "Will",
|
||||||
|
"profession": "高级开发工程师",
|
||||||
|
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png",
|
||||||
|
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md",
|
||||||
|
"usedAt": 1776000797914,
|
||||||
|
"industryId": "all"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"44c34a14b6dc4139b39ff61239e259ea": [
|
||||||
|
{
|
||||||
|
"expertId": "SeniorDeveloper",
|
||||||
|
"name": "Will",
|
||||||
|
"profession": "高级开发工程师",
|
||||||
|
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png",
|
||||||
|
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md",
|
||||||
|
"usedAt": 1776000797914,
|
||||||
|
"industryId": "all"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"d11a5ebd8e064cc19ff4a85b8d931dac": [
|
||||||
|
{
|
||||||
|
"expertId": "SeniorDeveloper",
|
||||||
|
"name": "吴八哥",
|
||||||
|
"profession": "高级开发工程师",
|
||||||
|
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png",
|
||||||
|
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md",
|
||||||
|
"usedAt": 1776443595917,
|
||||||
|
"industryId": "02-Engineering"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"e339ec20b1ef45479756bdfdf93c3654": [
|
||||||
|
{
|
||||||
|
"expertId": "SeniorDeveloper",
|
||||||
|
"name": "吴八哥",
|
||||||
|
"profession": "高级开发工程师",
|
||||||
|
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png",
|
||||||
|
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md",
|
||||||
|
"usedAt": 1776696820692,
|
||||||
|
"industryId": "02-Engineering"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lastUpdated": 1776699418893
|
||||||
|
}
|
||||||
13
.workbuddy/memory/2026-04-12.md
Normal file
13
.workbuddy/memory/2026-04-12.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 2026-04-12 工作日志
|
||||||
|
|
||||||
|
## 修复登录日志时间显示问题
|
||||||
|
|
||||||
|
**问题描述**:小程序后台登录日志中的登录时间显示不正确,实际登录时间 9:20:20,显示为 17:16:31,相差约 8 小时。
|
||||||
|
|
||||||
|
**问题原因**:`LoginRecord` 实体类中的 `createTime` 和 `updateTime` 字段使用了 `@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")` 注解,但没有指定 `timezone` 属性。对于 `LocalDateTime` 类型,Jackson 序列化时未正确应用全局时区配置,导致时间多了 8 小时。
|
||||||
|
|
||||||
|
**修复方案**:为 `@JsonFormat` 注解添加 `timezone = "GMT+8"` 属性。
|
||||||
|
|
||||||
|
**修改文件**:`src/main/java/com/gxwebsoft/common/system/entity/LoginRecord.java`
|
||||||
|
|
||||||
|
**状态**:已修复
|
||||||
15
.workbuddy/memory/2026-04-13.md
Normal file
15
.workbuddy/memory/2026-04-13.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# 2026-04-13 工作日志
|
||||||
|
|
||||||
|
## 修复支付回调订单状态不更新问题
|
||||||
|
|
||||||
|
**问题描述**:支付成功后,订单支付状态没有更新,回调地址 `https://glt-api.websoft.top/api/shop/shop-order/notify` 接收到了通知但订单状态未改变。
|
||||||
|
|
||||||
|
**问题原因**:`ShopOrderController.java` 的 `wxNotify` 方法中,使用 `StrUtil.equals("支付成功", transaction.getTradeStateDesc())` 来判断支付状态。但微信返回的 `tradeStateDesc` 可能不是固定的 "支付成功" 字符串(可能是 "SUCCESS" 或其他描述),导致支付成功的回调没有被正确处理。
|
||||||
|
|
||||||
|
**修复方案**:将状态判断从字符串比较改为枚举值比较:
|
||||||
|
- 原代码:`if (StrUtil.equals("支付成功", transaction.getTradeStateDesc()))`
|
||||||
|
- 修复后:`if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState()))`
|
||||||
|
|
||||||
|
**修改文件**:`src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java`
|
||||||
|
|
||||||
|
**状态**:已修复
|
||||||
75
.workbuddy/memory/2026-04-16.md
Normal file
75
.workbuddy/memory/2026-04-16.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# 2026-04-16 工作记录
|
||||||
|
|
||||||
|
## 支付回调状态不更新问题诊断与修复
|
||||||
|
|
||||||
|
**问题接口**: `POST /api/shop/shop-order/notify/{tenantId}`
|
||||||
|
|
||||||
|
### 发现的 Bug
|
||||||
|
|
||||||
|
1. **根因 Bug**:`ShopOrderServiceImpl.updateByOutTradeNo()` 第837行有 `order.setExpirationTime(null)`,强制覆盖了 Controller 中设置的 `expirationTime`(`LocalDateTime.now().plusYears(10)`),导致 XML 中 expirationTime 条件不生效。**已修复**:删除了该行。
|
||||||
|
|
||||||
|
2. **XML 缺少 `update_time`**:`ShopOrderMapper.xml` 的 `updateByOutTradeNo` SQL 的 `<set>` 块中没有 `update_time = NOW()` 和 `expiration_time` 字段。**已修复**:新增了这两个字段更新。
|
||||||
|
|
||||||
|
3. **回调地址路由问题**:Controller 路由为 `/notify/{tenantId}`,但测试访问的 `/notify`(无 tenantId)不存在,返回 fail。正确回调地址格式为 `https://glt-api.websoft.top/api/shop/shop-order/notify/{tenantId}`(需带租户ID)。**待检查**:数据库 Payment 表的 `notify_url` 字段是否正确配置了带 tenantId 的完整路径。
|
||||||
|
|
||||||
|
### 修复文件
|
||||||
|
- `src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java`
|
||||||
|
- `src/main/java/com/gxwebsoft/shop/mapper/xml/ShopOrderMapper.xml`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 支付回调签名验证失败:Transaction 类错误(00:29修复)
|
||||||
|
|
||||||
|
**错误日志关键信息**:
|
||||||
|
```
|
||||||
|
signature verification failed, signType[WECHATPAY2-SHA256-RSA2048]
|
||||||
|
serial[test] message[test\ntest\n{"test":"test"}] sign[test]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 根本原因(最致命)
|
||||||
|
|
||||||
|
`ShopOrderController.java` 导入了 **服务商模式** 的 Transaction 类:
|
||||||
|
```java
|
||||||
|
// 错误(服务商模式)
|
||||||
|
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
|
||||||
|
```
|
||||||
|
而 `ShopOrderServiceImpl.java` 下单时用的是**直连商户模式**:
|
||||||
|
```java
|
||||||
|
// 正确(直连商户模式)
|
||||||
|
import com.wechat.pay.java.service.payments.model.Transaction;
|
||||||
|
```
|
||||||
|
两个 Transaction 包路径不同,字段结构有差异(服务商 Transaction 有 spAppid/spMchid 等字段),用错误的类解析回调会导致字段映射失败,交易状态无法正确读取。**已修复**:改为正确的直连商户模式 Transaction。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配送员提成直接入账(01:15修改)
|
||||||
|
|
||||||
|
**文件**:`src/main/java/com/gxwebsoft/glt/service/impl/GltTicketOrderServiceImpl.java`
|
||||||
|
|
||||||
|
**变更**:配送员提成(ticketOrderId 关联送水订单)从进入 `freeze_money` 改为直接进入 `money`(可提现余额)。修改了 2 处 `LambdaUpdateWrapper` SQL(`freeze_money` → `money`),注释同步更新。`total_money` 不变(仍累计)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 分销佣金解冻任务分析(DealerCommissionUnfreeze10584Task)
|
||||||
|
|
||||||
|
**订单号**:2038841514750459904
|
||||||
|
|
||||||
|
### 解冻规则
|
||||||
|
- **送水套餐**(shop_order.form_id IN 水票模板的 goods_id):该订单关联的水票第一条送水订单 deliveryStatus=40(已完成)才触发解冻
|
||||||
|
- **非送水套餐**(form_id 不在水票模板中):shop_order.order_status=1 即触发解冻
|
||||||
|
|
||||||
|
### "已送达"≠"已完成"的关键区别
|
||||||
|
- deliveryStatus=30(送达待确认):配送员拍照确认送达,此时**不触发解冻**
|
||||||
|
- deliveryStatus=40(已完成):需用户手动确认收货 OR 超时24h自动确认后才到达此状态
|
||||||
|
|
||||||
|
### 常见未解冻原因(按排查优先级)
|
||||||
|
1. 送水订单停在 deliveryStatus=30(送达待确认),未到 40(已完成)
|
||||||
|
2. shop_order.form_id 在水票模板里,走的是"送水套餐"逻辑,但没有找到对应的 glt_user_ticket 记录
|
||||||
|
3. glt_user_ticket 记录缺失或 order_no 字段为空
|
||||||
|
4. 已有 flowType=50 的解冻 marker(说明已解冻)
|
||||||
|
|
||||||
|
### 次要原因
|
||||||
|
|
||||||
|
`RSAAutoCertificateConfig` 每次回调都重新 `build()`,SDK 内部会发一次 `serial=test` 的探测验签,网络问题或并发场景下可能导致首次回调失败。**已优化**:添加 `notifyConfigCache`(ConcurrentHashMap)按 mchId 缓存 config,避免重复初始化。
|
||||||
13
.workbuddy/memory/2026-04-18.md
Normal file
13
.workbuddy/memory/2026-04-18.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 2026-04-18 工作日志
|
||||||
|
|
||||||
|
## 排查解冻任务未触发问题
|
||||||
|
- 用户反馈:GltTicketOrder订单已完成配送,但部分订单未触发解冻(freezeMoney未转到money)
|
||||||
|
- 完整梳理了资金流转链路:结算→冻结→解冻
|
||||||
|
- 识别出5个可能原因:
|
||||||
|
1. GltTicketOrder.userTicketId为NULL(解冻任务硬性过滤条件)
|
||||||
|
2. GltUserTicket.orderNo缺失导致关联断裂
|
||||||
|
3. isFirstTicketOrderFinished()"第一条"逻辑阻断后续订单解冻
|
||||||
|
4. loadWaterFormIds()返回空集导致整个解冻任务跳过
|
||||||
|
5. 配送员提成orderNo格式不匹配(非bug,配送员提成本身不经过冻结)
|
||||||
|
- 提供了5条排查SQL和修复建议
|
||||||
|
- 关键文件:DealerCommissionUnfreeze10584Task.java、GltTicketOrderServiceImpl.java
|
||||||
18
.workbuddy/memory/2026-04-21.md
Normal file
18
.workbuddy/memory/2026-04-21.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# 2026-04-21 日志
|
||||||
|
|
||||||
|
## WxLoginController.getOrderQRCodeUnlimited 修复(完整)
|
||||||
|
|
||||||
|
### 根因
|
||||||
|
1. `extractTenantIdFromScene` 通过 `selectByIdIgnoreTenant` 反查用户获取 tenantId,userId=35280 在多租户下有2条记录 → `TooManyResultsException`
|
||||||
|
2. 异常被 catch 后 fallback 到默认租户 10550,Redis 中无 `mp-weixin:10550` 缓存 → 最终失败
|
||||||
|
3. 第 452 行 `website.getRunning().equals(2)` 存在 NPE 风险
|
||||||
|
|
||||||
|
### 修复内容
|
||||||
|
- **后端 WxLoginController**: scene 格式改为 `uid_userId_tenantId`,优先从 scene 直接解析 tenantId;兼容旧 `uid_userId` 格式时改用 `selectList` 避免多条记录异常
|
||||||
|
- **后端 UserMapper/UserService**: `selectByIdIgnoreTenant` 返回类型从 `User` 改为 `List<User>`;新增 `listByIdIgnoreTenant` 方法
|
||||||
|
- **后端 NPE 修复**: `website.getRunning().equals(2)` → `website != null && Integer.valueOf(2).equals(website.getRunning())`
|
||||||
|
- **前端 3 个 vue**: scene 从 `uid_${userId}` 改为 `uid_${userId}_${tenantId}`(从 tenantStore.company.tenantId 获取)
|
||||||
|
- shopDealerUser/index.vue
|
||||||
|
- shopDealerUserShop/index.vue
|
||||||
|
- shopDealerUserDelivery/index.vue
|
||||||
|
|
||||||
32
.workbuddy/memory/MEMORY.md
Normal file
32
.workbuddy/memory/MEMORY.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# MEMORY.md - 长期记忆
|
||||||
|
|
||||||
|
## 项目概况
|
||||||
|
- 后端:/Users/gxwebsoft/JAVA/java-10584(Spring Boot + MyBatis-Plus)
|
||||||
|
- 后台管理:/Users/gxwebsoft/VUE/mp-10584
|
||||||
|
- 小程序端:/Users/gxwebsoft/VUE/template-10584
|
||||||
|
- 多租户架构(tenantId 隔离),主力租户 10584
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
- 后端:Spring Boot + MyBatis-Plus + FastJSON 2.x
|
||||||
|
- 前端:Nuxt/Vue3 + TypeScript + Ant Design Vue4 + Tailwind
|
||||||
|
- 小程序:Uni-app/Taro
|
||||||
|
- 开发环境:Mac + Node.js v22 + JetBrains + Docker + pnpm
|
||||||
|
|
||||||
|
## 业务规则备忘
|
||||||
|
|
||||||
|
### 分销佣金解冻规则(10584)
|
||||||
|
- 结算:DealerOrderSettlement10584Task 每10秒,佣金先入 freezeMoney
|
||||||
|
- 解冻:DealerCommissionUnfreeze10584Task 每20秒,freezeMoney→money
|
||||||
|
- 送水套餐解冻条件:同一userTicketId下第一条送水订单deliveryStatus=40
|
||||||
|
- 非送水套餐解冻条件:ShopOrder.orderStatus=1 且 payStatus=true
|
||||||
|
- 幂等标记:ShopDealerCapital(flowType=50, comments="佣金解冻(capitalId=xxx)")
|
||||||
|
- 配送员提成:直接入money(不经过冻结),orderNo格式="gltTicketOrder:"+id
|
||||||
|
|
||||||
|
### 送水订单状态流转
|
||||||
|
- 10(待配送)→20(配送中)→30(待客户确认)→40(已完成)
|
||||||
|
- delivered()配送员确认送达时就会同步ShopOrder.orderStatus=1
|
||||||
|
- confirmReceive()/autoConfirmTimeout()也会同步
|
||||||
|
|
||||||
|
### 已知排查问题
|
||||||
|
- 解冻任务可能因 userTicketId为空、GltUserTicket.orderNo缺失、"第一条未完成"阻断等原因未触发
|
||||||
|
- 解冻任务依赖 loadWaterFormIds() 不为空,否则整个任务跳过
|
||||||
5
.workbuddy/settings.local.json
Normal file
5
.workbuddy/settings.local.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"enabledPlugins": {
|
||||||
|
"modern-webapp@cb_teams_marketplace": true
|
||||||
|
}
|
||||||
|
}
|
||||||
4
pom.xml
4
pom.xml
@@ -4,10 +4,10 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>com.gxwebsoft</groupId>
|
<groupId>com.gxwebsoft</groupId>
|
||||||
<artifactId>glt-api</artifactId>
|
<artifactId>mp-api</artifactId>
|
||||||
<version>1.0</version>
|
<version>1.0</version>
|
||||||
|
|
||||||
<name>glt-api</name>
|
<name>mp-api</name>
|
||||||
<description>WebSoftApi project for Spring Boot</description>
|
<description>WebSoftApi project for Spring Boot</description>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
|
|||||||
7
sql/glt_ticket_order_delivery_fields.sql
Normal file
7
sql/glt_ticket_order_delivery_fields.sql
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
-- 配送方式、楼层、配送费字段
|
||||||
|
-- 对应需求:送水订单下单时选择配送方式(电梯/步梯/一楼商铺),步梯送上楼需选楼层,配送费 = 数量 × (楼层-1)
|
||||||
|
|
||||||
|
ALTER TABLE glt_ticket_order
|
||||||
|
ADD COLUMN delivery_method VARCHAR(32) DEFAULT NULL COMMENT '配送方式:elevator(电梯) / stairs(步梯) / groundFloor(一楼商铺/其他)' AFTER buyer_remarks,
|
||||||
|
ADD COLUMN delivery_floor INT DEFAULT NULL COMMENT '楼层(步梯+送上楼时有值,从2开始)' AFTER delivery_method,
|
||||||
|
ADD COLUMN delivery_fee DECIMAL(10,2) DEFAULT NULL COMMENT '配送费(数量 × (楼层-1))' AFTER delivery_floor;
|
||||||
@@ -227,6 +227,10 @@ public class CmsNavigation implements Serializable {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String text;
|
private String text;
|
||||||
|
|
||||||
|
@Schema(description = "栏目文章")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private List<CmsArticle> articles;
|
||||||
|
|
||||||
public String getCategoryName() {
|
public String getCategoryName() {
|
||||||
return this.title;
|
return this.title;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import cn.hutool.core.util.ObjectUtil;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.gxwebsoft.cms.entity.CmsArticle;
|
||||||
import com.gxwebsoft.cms.entity.CmsDesign;
|
import com.gxwebsoft.cms.entity.CmsDesign;
|
||||||
import com.gxwebsoft.cms.entity.CmsModel;
|
import com.gxwebsoft.cms.entity.CmsModel;
|
||||||
import com.gxwebsoft.cms.mapper.CmsNavigationMapper;
|
import com.gxwebsoft.cms.mapper.CmsNavigationMapper;
|
||||||
|
import com.gxwebsoft.cms.service.CmsArticleService;
|
||||||
import com.gxwebsoft.cms.service.CmsDesignService;
|
import com.gxwebsoft.cms.service.CmsDesignService;
|
||||||
import com.gxwebsoft.cms.service.CmsModelService;
|
import com.gxwebsoft.cms.service.CmsModelService;
|
||||||
import com.gxwebsoft.cms.service.CmsNavigationService;
|
import com.gxwebsoft.cms.service.CmsNavigationService;
|
||||||
@@ -38,6 +40,8 @@ public class CmsNavigationServiceImpl extends ServiceImpl<CmsNavigationMapper, C
|
|||||||
private CmsModelService cmsModelService;
|
private CmsModelService cmsModelService;
|
||||||
@Resource
|
@Resource
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
@Resource
|
||||||
|
private CmsArticleService cmsArticleService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<CmsNavigation> pageRel(CmsNavigationParam param) {
|
public PageResult<CmsNavigation> pageRel(CmsNavigationParam param) {
|
||||||
@@ -53,6 +57,11 @@ public class CmsNavigationServiceImpl extends ServiceImpl<CmsNavigationMapper, C
|
|||||||
// 排序
|
// 排序
|
||||||
PageParam<CmsNavigation, CmsNavigationParam> page = new PageParam<>();
|
PageParam<CmsNavigation, CmsNavigationParam> page = new PageParam<>();
|
||||||
page.setDefaultOrder("sort_number asc, position asc,navigation_id asc");
|
page.setDefaultOrder("sort_number asc, position asc,navigation_id asc");
|
||||||
|
list.forEach(item -> {
|
||||||
|
// 关联信息
|
||||||
|
final List<CmsArticle> articleList = cmsArticleService.list(new LambdaQueryWrapper<CmsArticle>().eq(CmsArticle::getCategoryId, item.getNavigationId()).last("limit 5"));
|
||||||
|
item.setArticles(articleList);
|
||||||
|
});
|
||||||
return page.sortRecords(list);
|
return page.sortRecords(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class CertificateProperties {
|
|||||||
/**
|
/**
|
||||||
* 开发环境证书路径前缀
|
* 开发环境证书路径前缀
|
||||||
*/
|
*/
|
||||||
private String devCertPath = "dev";
|
private String devCertPath = "local";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 微信支付证书配置
|
* 微信支付证书配置
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class ConfigProperties {
|
|||||||
/**
|
/**
|
||||||
* token过期时间, 单位秒
|
* token过期时间, 单位秒
|
||||||
*/
|
*/
|
||||||
private Long tokenExpireTime = 60 * 60 * 365 * 24L;
|
private Long tokenExpireTime = 60 * 60 * 30 * 24L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* token快要过期自动刷新时间, 单位分钟
|
* token快要过期自动刷新时间, 单位分钟
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public class MybatisPlusConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean ignoreTable(String tableName) {
|
public boolean ignoreTable(String tableName) {
|
||||||
|
// TenantContext.setIgnoreTenant(Boolean.TRUE);
|
||||||
// 如果当前上下文设置了忽略租户隔离,则忽略所有表的租户隔离
|
// 如果当前上下文设置了忽略租户隔离,则忽略所有表的租户隔离
|
||||||
if (TenantContext.isIgnoreTenant()) {
|
if (TenantContext.isIgnoreTenant()) {
|
||||||
return true;
|
return true;
|
||||||
@@ -91,18 +92,18 @@ public class MybatisPlusConfig {
|
|||||||
"sys_dictionary",
|
"sys_dictionary",
|
||||||
"sys_dictionary_data",
|
"sys_dictionary_data",
|
||||||
"apps_test_data",
|
"apps_test_data",
|
||||||
"cms_lang"
|
"cms_lang",
|
||||||
// "hjm_car",
|
"shop_goods",
|
||||||
// "hjm_fence"
|
"shop_order" ,
|
||||||
// "cms_website"
|
"shop_order_goods",
|
||||||
// "sys_user"
|
"glt_ticket_template",
|
||||||
// "cms_domain"
|
"glt_user_ticket",
|
||||||
// "shop_order_goods",
|
"glt_user_ticket_release",
|
||||||
// "shop_goods"
|
"glt_user_ticket_log",
|
||||||
// "shop_users",
|
"shop_dealer_user",
|
||||||
// "shop_order" // 移除shop_order,改为通过注解控制
|
"shop_dealer_order",
|
||||||
// "shop_order_info",
|
"shop_dealer_referee",
|
||||||
// "booking_user_invoice"
|
"shop_store_rider"
|
||||||
).contains(tableName);
|
).contains(tableName);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -87,14 +87,15 @@ public class SwaggerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OA 模块分组
|
* Glt 模块分组
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public GroupedOpenApi oaApi() {
|
public GroupedOpenApi gltApi() {
|
||||||
return GroupedOpenApi.builder()
|
return GroupedOpenApi.builder()
|
||||||
.group("oa")
|
.group("glt")
|
||||||
.pathsToMatch("/api/oa/**")
|
// 订单等用户侧接口在 shop 包内,但路径使用 /api/user/**(前端统一 user 侧 API 前缀)
|
||||||
.packagesToScan("com.gxwebsoft.oa")
|
.pathsToMatch("/api/glt/**", "/api/user/**")
|
||||||
|
.packagesToScan("com.gxwebsoft.glt")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,7 @@ package com.gxwebsoft.common.core.constants;
|
|||||||
|
|
||||||
public class BaseConstants {
|
public class BaseConstants {
|
||||||
public static final String[] STATUS = {"未定义","显示","隐藏"};
|
public static final String[] STATUS = {"未定义","显示","隐藏"};
|
||||||
|
|
||||||
|
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import java.util.Map;
|
|||||||
@Tag(name = "数据库修复工具")
|
@Tag(name = "数据库修复工具")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/database-fix")
|
@RequestMapping("/api/database-fix")
|
||||||
// @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
|
// @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "local")
|
||||||
public class DatabaseFixController extends BaseController {
|
public class DatabaseFixController extends BaseController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import java.util.Map;
|
|||||||
@Tag(name = "开发环境管理")
|
@Tag(name = "开发环境管理")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/dev")
|
@RequestMapping("/api/dev")
|
||||||
// @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
|
// @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "local")
|
||||||
public class DevEnvironmentController extends BaseController {
|
public class DevEnvironmentController extends BaseController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.gxwebsoft.common.core.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户分销钱包交易业务分类
|
||||||
|
* @author xm
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum ShopDealerCapitalUpdateEnum {
|
||||||
|
|
||||||
|
DISTRIBUTION_INCOME(10, "分销收入", true),
|
||||||
|
MANAGEMENT_INCOME(11, "团队管理津贴收入", true),
|
||||||
|
DIVIDEND_INCOME(12, "分红收入", true),
|
||||||
|
PROMOTION_INCOME(13, "现场推广收入", true),
|
||||||
|
PROMOTION_PARENT_INCOME(14, "现场推广分佣", true),
|
||||||
|
WITHDRAW_PAYMENT(20, "提现支出", false),
|
||||||
|
TRANSFER_PAYMENT(30, "转账支出", false),
|
||||||
|
TRANSFER_INCOME(40, "转账收入", true),
|
||||||
|
FREEZE_MONEY_THAW(50, "佣金解冻", true),
|
||||||
|
DELIVERY_REWARD(60, "配送奖励", true),
|
||||||
|
DELIVERY_INCOME(61, "配送提成", true),
|
||||||
|
DELIVERY_FLOOR_FEE(62, "配送步梯费", true),
|
||||||
|
ORDER_REFUND(70, "佣金退回(退单)", false),
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务分类
|
||||||
|
*/
|
||||||
|
private final Integer type;
|
||||||
|
/**
|
||||||
|
* 说明
|
||||||
|
*/
|
||||||
|
private final String description;
|
||||||
|
/**
|
||||||
|
* 是否为增加余额
|
||||||
|
*/
|
||||||
|
private final boolean add;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.gxwebsoft.common.core.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单状态枚举
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum ShopDealerTypeEnum {
|
||||||
|
|
||||||
|
|
||||||
|
FREEZE_ACCOUNT(1, "操作冻结账户余额"),
|
||||||
|
WITHDRAW_ACCOUNT(2, "操作提现账户余额【直接结算】"),
|
||||||
|
DEFROST(3, "解冻"),
|
||||||
|
ORDER_REFUND(4, "退单");
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
public Integer getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDesc() {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.gxwebsoft.common.core.enums;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局用户类型枚举
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum UserTypeEnum {
|
||||||
|
|
||||||
|
// 面向 a 端,管理后台
|
||||||
|
RIDER(0, "骑手"),
|
||||||
|
// 面向 c 端,普通用户
|
||||||
|
MEMBER(1, "会员"),
|
||||||
|
|
||||||
|
STORE(3, "门店"),
|
||||||
|
// 面向 b 端,管理后台
|
||||||
|
ADMIN(2, "管理员"),
|
||||||
|
|
||||||
|
CHAT(4, "群聊");
|
||||||
|
|
||||||
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
private final Integer value;
|
||||||
|
/**
|
||||||
|
* 类型名
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public static UserTypeEnum valueOf(Integer value) {
|
||||||
|
return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.gxwebsoft.common.core.exception;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码对象
|
||||||
|
*
|
||||||
|
* 全局错误码,占用 [0, 999], 参见 {@link com.gxwebsoft.common.core.exception.enums.GlobalErrorCodeConstants}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ErrorCode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码
|
||||||
|
*/
|
||||||
|
private final Integer code;
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
*/
|
||||||
|
private final String msg;
|
||||||
|
|
||||||
|
public ErrorCode(Integer code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.msg = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.gxwebsoft.common.core.exception.enums;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.exception.ErrorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局错误码枚举
|
||||||
|
* 0-999 系统异常编码保留
|
||||||
|
*
|
||||||
|
* 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
|
||||||
|
* 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的
|
||||||
|
* 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。
|
||||||
|
*
|
||||||
|
* @author xm
|
||||||
|
*/
|
||||||
|
public interface GlobalErrorCodeConstants {
|
||||||
|
|
||||||
|
ErrorCode SUCCESS = new ErrorCode(0, "成功");
|
||||||
|
|
||||||
|
// ========== 客户端错误段 ==========
|
||||||
|
|
||||||
|
ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确");
|
||||||
|
ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录");
|
||||||
|
ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限");
|
||||||
|
ErrorCode NOT_FOUND = new ErrorCode(404, "查询无此数据");
|
||||||
|
ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
|
||||||
|
ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
|
||||||
|
ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试");
|
||||||
|
|
||||||
|
// ========== 服务端错误段 ==========
|
||||||
|
|
||||||
|
ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
|
||||||
|
ErrorCode NOT_IMPLEMENTED = new ErrorCode(501, "功能未实现/未开启");
|
||||||
|
|
||||||
|
// ========== 自定义错误段 ==========
|
||||||
|
ErrorCode REPEATED_REQUESTS = new ErrorCode(900, "重复请求,请稍后重试"); // 重复请求
|
||||||
|
ErrorCode DEMO_DENY = new ErrorCode(901, "演示模式,禁止写操作");
|
||||||
|
|
||||||
|
ErrorCode UNKNOWN = new ErrorCode(999, "未知错误");
|
||||||
|
ErrorCode FINANCE_BILL_NOT_EXISTS = new ErrorCode(600, "门店财务账单不存在");
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.gxwebsoft.common.core.service;
|
package com.gxwebsoft.common.core.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.gxwebsoft.common.system.entity.Payment;
|
import com.gxwebsoft.common.system.entity.Payment;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -26,6 +27,9 @@ public class EnvironmentAwarePaymentService {
|
|||||||
@Value("${config.server-url:}")
|
@Value("${config.server-url:}")
|
||||||
private String serverUrl;
|
private String serverUrl;
|
||||||
|
|
||||||
|
@Value("${config.api-url:}")
|
||||||
|
private String apiUrl;
|
||||||
|
|
||||||
// 开发环境回调地址配置
|
// 开发环境回调地址配置
|
||||||
@Value("${payment.dev.notify-url:http://frps-10550.s209.websoft.top/api/shop/shop-order/notify}")
|
@Value("${payment.dev.notify-url:http://frps-10550.s209.websoft.top/api/shop/shop-order/notify}")
|
||||||
private String devNotifyUrl;
|
private String devNotifyUrl;
|
||||||
@@ -66,15 +70,15 @@ public class EnvironmentAwarePaymentService {
|
|||||||
* 根据当前环境获取回调地址
|
* 根据当前环境获取回调地址
|
||||||
*/
|
*/
|
||||||
private String getEnvironmentNotifyUrl() {
|
private String getEnvironmentNotifyUrl() {
|
||||||
if ("dev".equals(activeProfile) || "test".equals(activeProfile)) {
|
if ("local".equals(activeProfile) || "test".equals(activeProfile)) {
|
||||||
// 开发/测试环境使用本地回调地址
|
// 开发/测试环境使用本地回调地址
|
||||||
return devNotifyUrl;
|
return devNotifyUrl;
|
||||||
} else if ("prod".equals(activeProfile)) {
|
} else if ("prod".equals(activeProfile)) {
|
||||||
// 生产环境使用生产回调地址
|
// 生产环境使用生产回调地址
|
||||||
return prodNotifyUrl;
|
return prodNotifyUrl;
|
||||||
} else {
|
} else {
|
||||||
// 默认使用配置的服务器地址
|
// 默认使用 API 网关地址(支付回调需要公网可访问的 API 地址)
|
||||||
return serverUrl + "/shop/shop-order/notify";
|
return (StrUtil.isNotBlank(apiUrl) ? apiUrl : serverUrl) + "/shop/shop-order/notify";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +135,7 @@ public class EnvironmentAwarePaymentService {
|
|||||||
* 是否为开发环境
|
* 是否为开发环境
|
||||||
*/
|
*/
|
||||||
public boolean isDevelopmentEnvironment() {
|
public boolean isDevelopmentEnvironment() {
|
||||||
return "dev".equals(activeProfile) || "test".equals(activeProfile);
|
return "local".equals(activeProfile) || "test".equals(activeProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.gxwebsoft.common.core.utils;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
|
public class LoginUserUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录的user
|
||||||
|
*
|
||||||
|
* @return User
|
||||||
|
*/
|
||||||
|
public static User getLoginUser() {
|
||||||
|
try {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication != null) {
|
||||||
|
Object object = authentication.getPrincipal();
|
||||||
|
if (object instanceof User) {
|
||||||
|
return (User) object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -84,7 +84,7 @@ public class WechatCertAutoConfig {
|
|||||||
String apiV3Key = "0kF5OlPr482EZwtn9zGufUcqa7ovgxRL";
|
String apiV3Key = "0kF5OlPr482EZwtn9zGufUcqa7ovgxRL";
|
||||||
|
|
||||||
// 根据环境选择证书路径
|
// 根据环境选择证书路径
|
||||||
if ("dev".equals(activeProfile)) {
|
if ("local".equals(activeProfile)) {
|
||||||
// 开发环境:使用配置文件upload-path拼接证书路径
|
// 开发环境:使用配置文件upload-path拼接证书路径
|
||||||
String uploadPath = configProperties.getUploadPath(); // 配置文件路径
|
String uploadPath = configProperties.getUploadPath(); // 配置文件路径
|
||||||
String tenantId = "10550"; // 租户ID
|
String tenantId = "10550"; // 租户ID
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class WechatPayCertificateDiagnostic {
|
|||||||
* 检查证书文件
|
* 检查证书文件
|
||||||
*/
|
*/
|
||||||
private void checkCertificateFiles(Payment payment, Integer tenantId, String environment, DiagnosticResult result) {
|
private void checkCertificateFiles(Payment payment, Integer tenantId, String environment, DiagnosticResult result) {
|
||||||
if ("dev".equals(environment)) {
|
if ("local".equals(environment)) {
|
||||||
// 开发环境证书检查
|
// 开发环境证书检查
|
||||||
String tenantCertPath = "dev/wechat/" + tenantId;
|
String tenantCertPath = "dev/wechat/" + tenantId;
|
||||||
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
||||||
@@ -152,7 +152,7 @@ public class WechatPayCertificateDiagnostic {
|
|||||||
*/
|
*/
|
||||||
private void validateCertificateContent(Payment payment, Integer tenantId, String environment, DiagnosticResult result) {
|
private void validateCertificateContent(Payment payment, Integer tenantId, String environment, DiagnosticResult result) {
|
||||||
try {
|
try {
|
||||||
if ("dev".equals(environment)) {
|
if ("local".equals(environment)) {
|
||||||
String tenantCertPath = "dev/wechat/" + tenantId;
|
String tenantCertPath = "dev/wechat/" + tenantId;
|
||||||
String apiclientCertPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getApiclientCertFile();
|
String apiclientCertPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getApiclientCertFile();
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public class WechatPayCertificateFixer {
|
|||||||
* 修复证书文件问题
|
* 修复证书文件问题
|
||||||
*/
|
*/
|
||||||
private void fixCertificateFiles(Payment payment, Integer tenantId, String environment, FixResult result) {
|
private void fixCertificateFiles(Payment payment, Integer tenantId, String environment, FixResult result) {
|
||||||
if ("dev".equals(environment)) {
|
if ("local".equals(environment)) {
|
||||||
fixDevCertificateFiles(tenantId, result);
|
fixDevCertificateFiles(tenantId, result);
|
||||||
} else {
|
} else {
|
||||||
fixProdCertificateFiles(payment, result);
|
fixProdCertificateFiles(payment, result);
|
||||||
@@ -169,7 +169,7 @@ public class WechatPayCertificateFixer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 在开发环境中,尝试从证书文件中提取序列号进行验证
|
// 在开发环境中,尝试从证书文件中提取序列号进行验证
|
||||||
if ("dev".equals(environment)) {
|
if ("local".equals(environment)) {
|
||||||
try {
|
try {
|
||||||
String tenantCertPath = "dev/wechat/" + tenantId;
|
String tenantCertPath = "dev/wechat/" + tenantId;
|
||||||
String apiclientCertPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getApiclientCertFile();
|
String apiclientCertPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getApiclientCertFile();
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public class WechatPayConfigValidator {
|
|||||||
* 验证证书文件
|
* 验证证书文件
|
||||||
*/
|
*/
|
||||||
private void validateCertificateFiles(Integer tenantId, ValidationResult result) {
|
private void validateCertificateFiles(Integer tenantId, ValidationResult result) {
|
||||||
if ("dev".equals(activeProfile)) {
|
if ("local".equals(activeProfile)) {
|
||||||
// 开发环境证书验证
|
// 开发环境证书验证
|
||||||
String tenantCertPath = "dev/wechat/" + tenantId;
|
String tenantCertPath = "dev/wechat/" + tenantId;
|
||||||
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
||||||
@@ -195,7 +195,7 @@ public class WechatPayConfigValidator {
|
|||||||
|
|
||||||
// 证书文件检查
|
// 证书文件检查
|
||||||
report.append("当前环境: ").append(activeProfile).append("\n");
|
report.append("当前环境: ").append(activeProfile).append("\n");
|
||||||
if ("dev".equals(activeProfile)) {
|
if ("local".equals(activeProfile)) {
|
||||||
String tenantCertPath = "dev/wechat/" + tenantId;
|
String tenantCertPath = "dev/wechat/" + tenantId;
|
||||||
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
||||||
boolean certExists = certificateLoader.certificateExists(privateKeyPath);
|
boolean certExists = certificateLoader.certificateExists(privateKeyPath);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class WechatPayDiagnostic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 生产环境检查证书文件
|
// 生产环境检查证书文件
|
||||||
if (!"dev".equals(environment)) {
|
if (!"local".equals(environment)) {
|
||||||
if (payment.getApiclientCert() != null) {
|
if (payment.getApiclientCert() != null) {
|
||||||
log.info("商户证书文件配置: {}", payment.getApiclientCert());
|
log.info("商户证书文件配置: {}", payment.getApiclientCert());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -449,7 +450,7 @@ public class WxLoginController extends BaseController {
|
|||||||
|
|
||||||
// 判断应用运行状态
|
// 判断应用运行状态
|
||||||
final CmsWebsite website = cmsWebsiteService.getByTenantId(tenantId);
|
final CmsWebsite website = cmsWebsiteService.getByTenantId(tenantId);
|
||||||
if(website.getRunning().equals(2)){
|
if(website != null && Integer.valueOf(2).equals(website.getRunning())){
|
||||||
map.put("check_path",false);
|
map.put("check_path",false);
|
||||||
map.put("env_version","trial");
|
map.put("env_version","trial");
|
||||||
}
|
}
|
||||||
@@ -725,27 +726,44 @@ public class WxLoginController extends BaseController {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 从scene参数中提取租户ID
|
* 从scene参数中提取租户ID
|
||||||
* scene格式可能是: uid_33103 或其他包含用户ID的格式
|
* scene格式: uid_userId_tenantId(优先)或 uid_userId(兼容旧格式)
|
||||||
*/
|
*/
|
||||||
private Integer extractTenantIdFromScene(String scene) {
|
private Integer extractTenantIdFromScene(String scene) {
|
||||||
try {
|
try {
|
||||||
System.out.println("解析scene参数: " + scene);
|
System.out.println("解析scene参数: " + scene);
|
||||||
|
|
||||||
// 如果scene包含uid_前缀,提取用户ID
|
|
||||||
if (scene != null && scene.startsWith("uid_")) {
|
if (scene != null && scene.startsWith("uid_")) {
|
||||||
String userIdStr = scene.substring(4); // 去掉"uid_"前缀
|
String content = scene.substring(4); // 去掉"uid_"前缀
|
||||||
Integer userId = Integer.parseInt(userIdStr);
|
|
||||||
System.out.println("userId = " + userId);
|
|
||||||
|
|
||||||
// 根据用户ID查询用户信息,获取租户ID
|
// 优先解析 uid_userId_tenantId 格式
|
||||||
User user = userService.getByIdIgnoreTenant(userId);
|
String[] parts = content.split("_");
|
||||||
System.out.println("user = " + user);
|
if (parts.length >= 2) {
|
||||||
if (user != null) {
|
try {
|
||||||
System.out.println("从用户ID " + userId + " 获取到租户ID: " + user.getTenantId());
|
Integer tenantId = Integer.parseInt(parts[1]);
|
||||||
return user.getTenantId();
|
System.out.println("从scene直接解析到tenantId = " + tenantId);
|
||||||
|
return tenantId;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
System.err.println("scene中tenantId格式异常: " + parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容旧格式 uid_userId:根据用户ID查询租户ID
|
||||||
|
if (parts.length == 1) {
|
||||||
|
Integer userId = Integer.parseInt(parts[0]);
|
||||||
|
System.out.println("userId = " + userId);
|
||||||
|
try {
|
||||||
|
List<User> users = userService.listByIdIgnoreTenant(userId);
|
||||||
|
System.out.println("查询到用户数量 = " + (users != null ? users.size() : 0));
|
||||||
|
if (users != null && !users.isEmpty()) {
|
||||||
|
System.out.println("从用户ID " + userId + " 获取到租户ID: " + users.get(0).getTenantId());
|
||||||
|
return users.get(0).getTenantId();
|
||||||
} else {
|
} else {
|
||||||
System.err.println("未找到用户ID: " + userId);
|
System.err.println("未找到用户ID: " + userId);
|
||||||
}
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.err.println("查询用户异常: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果无法解析,默认使用租户10550
|
// 如果无法解析,默认使用租户10550
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ public class LoginRecord implements Serializable {
|
|||||||
private Integer tenantId;
|
private Integer tenantId;
|
||||||
|
|
||||||
@Schema(description = "操作时间")
|
@Schema(description = "操作时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
@Schema(description = "修改时间")
|
@Schema(description = "修改时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
private LocalDateTime updateTime;
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
@Schema(description = "用户id")
|
@Schema(description = "用户id")
|
||||||
|
|||||||
@@ -262,6 +262,9 @@ public class User implements UserDetails {
|
|||||||
@Schema(description = "微信unionid")
|
@Schema(description = "微信unionid")
|
||||||
private String unionid;
|
private String unionid;
|
||||||
|
|
||||||
|
@Schema(description = "核销权限是否开启 0-未开启 1-已开启")
|
||||||
|
private Integer verifyFlag;
|
||||||
|
|
||||||
@Schema(description = "关联用户ID")
|
@Schema(description = "关联用户ID")
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private Integer sysUserId;
|
private Integer sysUserId;
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ public interface UserMapper extends BaseMapper<User> {
|
|||||||
@InterceptorIgnore(tenantLine = "true")
|
@InterceptorIgnore(tenantLine = "true")
|
||||||
List<User> getOne(@Param("param") UserParam param);
|
List<User> getOne(@Param("param") UserParam param);
|
||||||
|
|
||||||
|
@InterceptorIgnore(tenantLine = "true")
|
||||||
|
User getById(@Param("userId") Integer userId);
|
||||||
|
|
||||||
List<User> selectListStatisticsRel(@Param("param") UserParam param);
|
List<User> selectListStatisticsRel(@Param("param") UserParam param);
|
||||||
|
|
||||||
@InterceptorIgnore(tenantLine = "true")
|
@InterceptorIgnore(tenantLine = "true")
|
||||||
@@ -60,11 +63,18 @@ public interface UserMapper extends BaseMapper<User> {
|
|||||||
* @return User
|
* @return User
|
||||||
*/
|
*/
|
||||||
@InterceptorIgnore(tenantLine = "true")
|
@InterceptorIgnore(tenantLine = "true")
|
||||||
User selectByIdIgnoreTenant(@Param("userId") Integer userId);
|
List<User> selectByIdIgnoreTenant(@Param("userId") Integer userId);
|
||||||
|
|
||||||
@InterceptorIgnore(tenantLine = "true")
|
@InterceptorIgnore(tenantLine = "true")
|
||||||
List<User> pageAdminByPhone(@Param("param") UserParam param);
|
List<User> pageAdminByPhone(@Param("param") UserParam param);
|
||||||
|
|
||||||
@InterceptorIgnore(tenantLine = "true")
|
@InterceptorIgnore(tenantLine = "true")
|
||||||
List<User> listByAlert();
|
List<User> listByAlert();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量查询用户信息
|
||||||
|
* @param userIdList 用户ID集合
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<User> selectByUserIdList(@Param("userIdList") List<Integer> userIdList);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -260,5 +260,25 @@
|
|||||||
WHERE a.user_id = #{userId}
|
WHERE a.user_id = #{userId}
|
||||||
AND a.deleted = 0
|
AND a.deleted = 0
|
||||||
</select>
|
</select>
|
||||||
|
<select id="getById" resultType="com.gxwebsoft.common.system.entity.User">
|
||||||
|
SELECT * FROM gxwebsoft_core.sys_user WHERE user_id = #{userId} and deleted = 0
|
||||||
|
</select>
|
||||||
|
<select id="selectByUserIdList" resultType="com.gxwebsoft.common.system.entity.User">
|
||||||
|
SELECT
|
||||||
|
user_id,
|
||||||
|
username,
|
||||||
|
nickname,
|
||||||
|
phone,
|
||||||
|
real_name,
|
||||||
|
create_time
|
||||||
|
FROM
|
||||||
|
gxwebsoft_core.sys_user
|
||||||
|
WHERE
|
||||||
|
deleted = 0
|
||||||
|
AND user_id IN
|
||||||
|
<foreach collection="userIdList" item="item" index="index" open="(" close=")" separator=",">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.gxwebsoft.common.system.redis;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付序号的 Redis DAO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
public class OrderNoUtils {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成序号
|
||||||
|
* @param prefix 前缀
|
||||||
|
* @return 序号
|
||||||
|
*/
|
||||||
|
public String generate(String prefix) {
|
||||||
|
// 递增序号
|
||||||
|
String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN);
|
||||||
|
String key = noPrefix;
|
||||||
|
Long no = stringRedisTemplate.opsForValue().increment(key);
|
||||||
|
// 设置过期时间
|
||||||
|
stringRedisTemplate.expire(key, Duration.ofMinutes(1L));
|
||||||
|
return noPrefix + no;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -117,6 +117,11 @@ public interface UserService extends IService<User>, UserDetailsService {
|
|||||||
*/
|
*/
|
||||||
User getByIdIgnoreTenant(Integer userId);
|
User getByIdIgnoreTenant(Integer userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID查询用户列表(忽略租户隔离)
|
||||||
|
*/
|
||||||
|
List<User> listByIdIgnoreTenant(Integer userId);
|
||||||
|
|
||||||
List<User> pageAdminByPhone(UserParam param);
|
List<User> pageAdminByPhone(UserParam param);
|
||||||
|
|
||||||
List<User> listByAlert();
|
List<User> listByAlert();
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public class SettingServiceImpl extends ServiceImpl<SettingMapper, Setting> impl
|
|||||||
final String apiV3key = jsonObject.getString("wechatApiKey");
|
final String apiV3key = jsonObject.getString("wechatApiKey");
|
||||||
if(config == null){
|
if(config == null){
|
||||||
// 根据环境选择不同的证书路径配置
|
// 根据环境选择不同的证书路径配置
|
||||||
if ("dev".equals(activeProfile)) {
|
if ("local".equals(activeProfile)) {
|
||||||
// 开发环境:使用配置文件的upload-path拼接证书路径 - 租户ID 10550
|
// 开发环境:使用配置文件的upload-path拼接证书路径 - 租户ID 10550
|
||||||
System.out.println("=== 开发环境:使用配置文件upload-path拼接证书路径 ===");
|
System.out.println("=== 开发环境:使用配置文件upload-path拼接证书路径 ===");
|
||||||
String uploadPath = pathConfig.getUploadPath(); // 获取配置的upload-path
|
String uploadPath = pathConfig.getUploadPath(); // 获取配置的upload-path
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package com.gxwebsoft.common.system.service.impl;
|
|||||||
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
@@ -224,6 +227,15 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
|||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
List<User> users = baseMapper.selectByIdIgnoreTenant(userId);
|
||||||
|
return users != null && !users.isEmpty() ? users.get(0) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<User> listByIdIgnoreTenant(Integer userId) {
|
||||||
|
if (userId == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
return baseMapper.selectByIdIgnoreTenant(userId);
|
return baseMapper.selectByIdIgnoreTenant(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
package com.gxwebsoft.glt.controller;
|
package com.gxwebsoft.glt.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import com.gxwebsoft.common.core.exception.BusinessException;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.core.exception.BusinessException;
|
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import com.gxwebsoft.common.system.mapper.UserMapper;
|
||||||
|
import com.gxwebsoft.glt.dto.GltTransferOrderDto;
|
||||||
|
import com.gxwebsoft.glt.dto.NoticeRiderNewOrderDto;
|
||||||
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
||||||
import com.gxwebsoft.glt.param.GltTicketOrderDeliveredParam;
|
import com.gxwebsoft.glt.param.GltTicketOrderDeliveredParam;
|
||||||
import com.gxwebsoft.glt.param.GltTicketOrderParam;
|
import com.gxwebsoft.glt.param.GltTicketOrderParam;
|
||||||
|
import com.gxwebsoft.glt.service.GltSubscribeMessageService;
|
||||||
import com.gxwebsoft.glt.service.GltTicketOrderService;
|
import com.gxwebsoft.glt.service.GltTicketOrderService;
|
||||||
import com.gxwebsoft.shop.entity.ShopStoreRider;
|
import com.gxwebsoft.shop.entity.ShopStoreRider;
|
||||||
import com.gxwebsoft.shop.entity.ShopUserAddress;
|
import com.gxwebsoft.shop.entity.ShopUserAddress;
|
||||||
@@ -19,12 +24,14 @@ import com.gxwebsoft.shop.service.ShopUserAddressService;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springdoc.api.annotations.ParameterObject;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 送水订单控制器
|
* 送水订单控制器
|
||||||
@@ -32,6 +39,7 @@ import java.util.List;
|
|||||||
* @author 科技小王子
|
* @author 科技小王子
|
||||||
* @since 2026-02-05 18:50:21
|
* @since 2026-02-05 18:50:21
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Tag(name = "送水订单管理")
|
@Tag(name = "送水订单管理")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/glt/glt-ticket-order")
|
@RequestMapping("/api/glt/glt-ticket-order")
|
||||||
@@ -44,10 +52,14 @@ public class GltTicketOrderController extends BaseController {
|
|||||||
private ShopStoreFenceService shopStoreFenceService;
|
private ShopStoreFenceService shopStoreFenceService;
|
||||||
@Resource
|
@Resource
|
||||||
private ShopStoreRiderService shopStoreRiderService;
|
private ShopStoreRiderService shopStoreRiderService;
|
||||||
|
@Resource
|
||||||
|
private GltSubscribeMessageService gltSubscribeMessageService;
|
||||||
|
@Resource
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
@Operation(summary = "分页查询送水订单")
|
@Operation(summary = "分页查询送水订单")
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
public ApiResult<PageResult<GltTicketOrder>> page(GltTicketOrderParam param) {
|
public ApiResult<PageResult<GltTicketOrder>> page(@ParameterObject GltTicketOrderParam param) {
|
||||||
// 使用关联查询
|
// 使用关联查询
|
||||||
return success(gltTicketOrderService.pageRel(param));
|
return success(gltTicketOrderService.pageRel(param));
|
||||||
}
|
}
|
||||||
@@ -170,6 +182,71 @@ public class GltTicketOrderController extends BaseController {
|
|||||||
return success("下单成功");
|
return success("下单成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知所有在线配送员有新订单
|
||||||
|
*/
|
||||||
|
private void notifyRidersOfNewOrder(GltTicketOrder order, Integer tenantId) {
|
||||||
|
if (order == null || tenantId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询所有启用且在线的配送员
|
||||||
|
List<ShopStoreRider> onlineRiders = shopStoreRiderService.list(
|
||||||
|
new LambdaQueryWrapper<ShopStoreRider>()
|
||||||
|
.eq(ShopStoreRider::getTenantId, tenantId)
|
||||||
|
.eq(ShopStoreRider::getIsDelete, 0)
|
||||||
|
.eq(ShopStoreRider::getStatus, 1)
|
||||||
|
.eq(ShopStoreRider::getWorkStatus, 1) // 在线状态
|
||||||
|
.or()
|
||||||
|
.eq(ShopStoreRider::getTenantId, tenantId)
|
||||||
|
.eq(ShopStoreRider::getIsDelete, 0)
|
||||||
|
.eq(ShopStoreRider::getStatus, 1)
|
||||||
|
.isNull(ShopStoreRider::getWorkStatus) // 兼容未设置状态的配送员
|
||||||
|
);
|
||||||
|
|
||||||
|
if (onlineRiders == null || onlineRiders.isEmpty()) {
|
||||||
|
log.info("当前无在线配送员,无需发送订阅消息");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取配送员的 userId 列表
|
||||||
|
List<Integer> riderUserIds = onlineRiders.stream()
|
||||||
|
.map(ShopStoreRider::getUserId)
|
||||||
|
.filter(id -> id != null && id > 0)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (riderUserIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量查询配送员的 openId
|
||||||
|
List<User> riders = userMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<User>()
|
||||||
|
.select(User::getUserId, User::getOpenid)
|
||||||
|
.in(User::getUserId, riderUserIds)
|
||||||
|
.isNotNull(User::getOpenid)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 发送订阅消息
|
||||||
|
for (User rider : riders) {
|
||||||
|
if (StrUtil.isNotBlank(rider.getOpenid())) {
|
||||||
|
try {
|
||||||
|
gltSubscribeMessageService.sendNewOrderNotice(order, rider.getOpenid(), tenantId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("发送订阅消息给配送员失败 - userId={}, error={}", rider.getUserId(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("isAuthenticated()")
|
||||||
|
@Operation(summary = "配送员调度单通知")
|
||||||
|
@PostMapping("/sendRiderNewOrderNotice")
|
||||||
|
public ApiResult<?> sendRiderNewOrderNotice(@RequestBody NoticeRiderNewOrderDto noticeRiderNewOrderDto) {
|
||||||
|
gltSubscribeMessageService.sendRiderNewOrderNotice(noticeRiderNewOrderDto);
|
||||||
|
return success("发送消息成功!");
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("isAuthenticated()")
|
@PreAuthorize("isAuthenticated()")
|
||||||
@Operation(summary = "配送员接单")
|
@Operation(summary = "配送员接单")
|
||||||
@PostMapping("/{id}/accept")
|
@PostMapping("/{id}/accept")
|
||||||
@@ -184,6 +261,13 @@ public class GltTicketOrderController extends BaseController {
|
|||||||
return success("接单成功");
|
return success("接单成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("isAuthenticated()")
|
||||||
|
@Operation(summary = "配送员转单")
|
||||||
|
@PostMapping("/transferOrder")
|
||||||
|
public ApiResult<Boolean> transferOrder(@RequestBody GltTransferOrderDto orderDto) {
|
||||||
|
return success(gltTicketOrderService.transferOrder(orderDto));
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("isAuthenticated()")
|
@PreAuthorize("isAuthenticated()")
|
||||||
@Operation(summary = "配送员开始配送")
|
@Operation(summary = "配送员开始配送")
|
||||||
@PostMapping("/{id}/start")
|
@PostMapping("/{id}/start")
|
||||||
@@ -215,6 +299,13 @@ public class GltTicketOrderController extends BaseController {
|
|||||||
return success("确认送达");
|
return success("确认送达");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "订单调度")
|
||||||
|
@PostMapping("/dispatchOrder")
|
||||||
|
public ApiResult<?> dispatchOrder(@RequestParam("orderNo") String orderNo, @RequestParam("tenantId") Integer tenantId) {
|
||||||
|
gltTicketOrderService.dispatchOrder(orderNo, tenantId);
|
||||||
|
return success("success!");
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("isAuthenticated()")
|
@PreAuthorize("isAuthenticated()")
|
||||||
@Operation(summary = "用户确认收货")
|
@Operation(summary = "用户确认收货")
|
||||||
@PostMapping("/{id}/confirm-receive")
|
@PostMapping("/{id}/confirm-receive")
|
||||||
@@ -253,16 +344,14 @@ public class GltTicketOrderController extends BaseController {
|
|||||||
if (addr == null) {
|
if (addr == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (StrUtil.isNotBlank(addr.getFullAddress())) {
|
|
||||||
return addr.getFullAddress();
|
|
||||||
}
|
|
||||||
// 兼容旧数据:fullAddress 为空时,拼接省市区 + 详细地址
|
// 兼容旧数据:fullAddress 为空时,拼接省市区 + 详细地址
|
||||||
return StrUtil.blankToDefault(
|
return StrUtil.blankToDefault(
|
||||||
StrUtil.join("",
|
StrUtil.join("",
|
||||||
StrUtil.nullToEmpty(addr.getProvince()),
|
StrUtil.nullToEmpty(addr.getProvince()),
|
||||||
StrUtil.nullToEmpty(addr.getCity()),
|
StrUtil.nullToEmpty(addr.getCity()),
|
||||||
StrUtil.nullToEmpty(addr.getRegion()),
|
StrUtil.nullToEmpty(addr.getRegion()),
|
||||||
StrUtil.nullToEmpty(addr.getAddress())
|
StrUtil.nullToEmpty(addr.getAddress()),
|
||||||
|
StrUtil.nullToEmpty(addr.getFullAddress())
|
||||||
),
|
),
|
||||||
addr.getAddress()
|
addr.getAddress()
|
||||||
);
|
);
|
||||||
@@ -300,23 +389,14 @@ public class GltTicketOrderController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gltTicketOrderService.updateById(gltTicketOrder)) {
|
if (gltTicketOrderService.updateById(gltTicketOrder)) {
|
||||||
// 后台指派配送员(直接改 riderId)时,同步商城订单为“已发货”(deliveryStatus=20)
|
gltTicketOrderService.markShopOrderShippedAfterRiderAssigned(gltTicketOrder.getId(), tenantId, gltTicketOrder.getRiderId());
|
||||||
if (gltTicketOrder != null
|
|
||||||
&& gltTicketOrder.getId() != null
|
|
||||||
&& gltTicketOrder.getRiderId() != null
|
|
||||||
&& gltTicketOrder.getRiderId() > 0) {
|
|
||||||
gltTicketOrderService.markShopOrderShippedAfterRiderAssigned(
|
|
||||||
gltTicketOrder.getId(),
|
|
||||||
tenantId,
|
|
||||||
gltTicketOrder.getRiderId()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 后台直接改“已完成”(deliveryStatus=40)时,同步商城订单为“已完成”(orderStatus=1)
|
// 后台直接改“已完成”(deliveryStatus=40)时,同步商城订单为“已完成”(orderStatus=1)
|
||||||
if (gltTicketOrder != null
|
if (gltTicketOrder != null
|
||||||
&& gltTicketOrder.getId() != null
|
&& gltTicketOrder.getId() != null
|
||||||
&& gltTicketOrder.getDeliveryStatus() != null
|
&& gltTicketOrder.getDeliveryStatus() != null
|
||||||
&& gltTicketOrder.getDeliveryStatus() == GltTicketOrderService.DELIVERY_STATUS_FINISHED) {
|
&& gltTicketOrder.getDeliveryStatus() == GltTicketOrderService.DELIVERY_STATUS_FINISHED) {
|
||||||
gltTicketOrderService.markShopOrderCompletedAfterTicketFinished(gltTicketOrder.getId(), tenantId);
|
gltTicketOrderService.markShopOrderCompletedAfterTicketFinished(gltTicketOrder.getId());
|
||||||
}
|
}
|
||||||
return success("修改成功");
|
return success("修改成功");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ public class GltTicketTemplateController extends BaseController {
|
|||||||
User loginUser = getLoginUser();
|
User loginUser = getLoginUser();
|
||||||
if (loginUser != null) {
|
if (loginUser != null) {
|
||||||
gltTicketTemplate.setUserId(loginUser.getUserId());
|
gltTicketTemplate.setUserId(loginUser.getUserId());
|
||||||
|
gltTicketTemplate.setTenantId(loginUser.getTenantId());
|
||||||
}
|
}
|
||||||
if (gltTicketTemplateService.save(gltTicketTemplate)) {
|
if (gltTicketTemplateService.save(gltTicketTemplate)) {
|
||||||
return success("添加成功");
|
return success("添加成功");
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
package com.gxwebsoft.glt.controller;
|
package com.gxwebsoft.glt.controller;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.glt.service.GltUserTicketReleaseService;
|
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
|
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
|
||||||
import com.gxwebsoft.glt.param.GltUserTicketReleaseParam;
|
import com.gxwebsoft.glt.param.GltUserTicketReleaseParam;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.glt.service.GltUserTicketReleaseService;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.glt.service.impl.GltUserTicketAutoReleaseServiceImpl;
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@@ -31,6 +31,9 @@ public class GltUserTicketReleaseController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private GltUserTicketReleaseService gltUserTicketReleaseService;
|
private GltUserTicketReleaseService gltUserTicketReleaseService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private GltUserTicketAutoReleaseServiceImpl gltUserTicketAutoReleaseService;
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('glt:gltUserTicketRelease:list')")
|
@PreAuthorize("hasAuthority('glt:gltUserTicketRelease:list')")
|
||||||
@Operation(summary = "分页查询水票释放")
|
@Operation(summary = "分页查询水票释放")
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@@ -126,4 +129,11 @@ public class GltUserTicketReleaseController extends BaseController {
|
|||||||
return fail("删除失败");
|
return fail("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "水票释放测试")
|
||||||
|
@PostMapping("/releaseTask")
|
||||||
|
public ApiResult<?> releaseTask() {
|
||||||
|
gltUserTicketAutoReleaseService.releaseTask();
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/main/java/com/gxwebsoft/glt/dto/GltTransferOrderDto.java
Normal file
25
src/main/java/com/gxwebsoft/glt/dto/GltTransferOrderDto.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package com.gxwebsoft.glt.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转单请求类
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "ShopOrderMyVerifyDto", description = "转单请求类")
|
||||||
|
public class GltTransferOrderDto {
|
||||||
|
|
||||||
|
@Schema(description = "订单ID")
|
||||||
|
@NotEmpty(message = "订单ID不能为空!")
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "转单用户ID")
|
||||||
|
@NotEmpty(message = "转单用户ID不能为空!")
|
||||||
|
private Integer userId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.gxwebsoft.glt.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配送员新订单配送通知
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "ShopOrderMyVerifyDto", description = "配送员新订单配送通知")
|
||||||
|
public class NoticeRiderNewOrderDto {
|
||||||
|
|
||||||
|
@Schema(description = "订单号")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
@Schema(description = "商品名称")
|
||||||
|
private String goodsName;
|
||||||
|
|
||||||
|
@Schema(description = "配送数量")
|
||||||
|
private Integer productCount;
|
||||||
|
|
||||||
|
@Schema(description = "期望送达时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "openID")
|
||||||
|
private String openId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package com.gxwebsoft.glt.entity;
|
package com.gxwebsoft.glt.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -22,17 +19,20 @@ import java.time.LocalDateTime;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Schema(name = "GltTicketOrder对象", description = "送水订单")
|
@Schema(name = "GltTicketOrder对象", description = "送水订单")
|
||||||
|
@TableName("glt_ticket_order")
|
||||||
public class GltTicketOrder implements Serializable {
|
public class GltTicketOrder implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@TableId(value = "id", type = IdType.AUTO)
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
private Integer id;
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "订单编号")
|
||||||
|
private String no;
|
||||||
|
|
||||||
@Schema(description = "用户水票ID")
|
@Schema(description = "用户水票ID")
|
||||||
private Integer userTicketId;
|
private Integer userTicketId;
|
||||||
|
|
||||||
@Schema(description = "订单编号")
|
@Schema(description = "关联订单编号")
|
||||||
@TableField(exist = false)
|
|
||||||
private String orderNo;
|
private String orderNo;
|
||||||
|
|
||||||
@Schema(description = "订单状态")
|
@Schema(description = "订单状态")
|
||||||
@@ -46,6 +46,10 @@ public class GltTicketOrder implements Serializable {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String storeName;
|
private String storeName;
|
||||||
|
|
||||||
|
@Schema(description = "品名")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String goodsName;
|
||||||
|
|
||||||
@Schema(description = "门店地址")
|
@Schema(description = "门店地址")
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String storeAddress;
|
private String storeAddress;
|
||||||
@@ -191,6 +195,19 @@ public class GltTicketOrder implements Serializable {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String warehouseLngAndLat;
|
private String warehouseLngAndLat;
|
||||||
|
|
||||||
|
@Schema(description = "配送方式:elevator(电梯) / stairs(步梯) / groundFloor(一楼商铺/其他)")
|
||||||
|
private String deliveryMethod;
|
||||||
|
|
||||||
|
@Schema(description = "楼层(步梯+送上楼时有值,从2开始)")
|
||||||
|
private Integer deliveryFloor;
|
||||||
|
|
||||||
|
@Schema(description = "详细地址")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String fullAddress;
|
||||||
|
|
||||||
|
@Schema(description = "配送费(步梯+送上楼时计算:数量 × (楼层-1))")
|
||||||
|
private BigDecimal deliveryFee;
|
||||||
|
|
||||||
@Schema(description = "排序(数字越小越靠前)")
|
@Schema(description = "排序(数字越小越靠前)")
|
||||||
private Integer sortNumber;
|
private Integer sortNumber;
|
||||||
|
|
||||||
@@ -200,6 +217,9 @@ public class GltTicketOrder implements Serializable {
|
|||||||
@Schema(description = "状态, 0正常, 1冻结")
|
@Schema(description = "状态, 0正常, 1冻结")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "水票标识 0-非 1-是")
|
||||||
|
private Integer waterTicketFlag;
|
||||||
|
|
||||||
@Schema(description = "是否删除, 0否, 1是")
|
@Schema(description = "是否删除, 0否, 1是")
|
||||||
@TableLogic
|
@TableLogic
|
||||||
private Integer deleted;
|
private Integer deleted;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.annotation.TableId;
|
|||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@@ -19,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Schema(name = "GltTicketTemplate对象", description = "水票")
|
@Schema(name = "GltTicketTemplate对象", description = "水票")
|
||||||
|
@TableName("glt_ticket_template")
|
||||||
public class GltTicketTemplate implements Serializable {
|
public class GltTicketTemplate implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package com.gxwebsoft.glt.entity;
|
package com.gxwebsoft.glt.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -22,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Schema(name = "GltUserTicket对象", description = "我的水票")
|
@Schema(name = "GltUserTicket对象", description = "我的水票")
|
||||||
|
@TableName("glt_user_ticket")
|
||||||
public class GltUserTicket implements Serializable {
|
public class GltUserTicket implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package com.gxwebsoft.glt.entity;
|
package com.gxwebsoft.glt.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -20,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Schema(name = "GltUserTicketLog对象", description = "消费日志")
|
@Schema(name = "GltUserTicketLog对象", description = "消费日志")
|
||||||
|
@TableName("glt_user_ticket_log")
|
||||||
public class GltUserTicketLog implements Serializable {
|
public class GltUserTicketLog implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package com.gxwebsoft.glt.entity;
|
package com.gxwebsoft.glt.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -20,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Schema(name = "GltUserTicketRelease对象", description = "水票释放")
|
@Schema(name = "GltUserTicketRelease对象", description = "水票释放")
|
||||||
|
@TableName("glt_user_ticket_release")
|
||||||
public class GltUserTicketRelease implements Serializable {
|
public class GltUserTicketRelease implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ public class GltUserTicketRelease implements Serializable {
|
|||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "水票ID")
|
@Schema(description = "水票ID")
|
||||||
private Long userTicketId;
|
private Integer userTicketId;
|
||||||
|
|
||||||
@Schema(description = "用户ID")
|
@Schema(description = "用户ID")
|
||||||
private Integer userId;
|
private Integer userId;
|
||||||
@@ -57,6 +56,9 @@ public class GltUserTicketRelease implements Serializable {
|
|||||||
@Schema(description = "状态")
|
@Schema(description = "状态")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
@Schema(description = "是否删除, 0否, 1是")
|
@Schema(description = "是否删除, 0否, 1是")
|
||||||
@TableLogic
|
@TableLogic
|
||||||
private Integer deleted;
|
private Integer deleted;
|
||||||
|
|||||||
@@ -37,6 +37,13 @@ public interface GltUserTicketReleaseMapper extends BaseMapper<GltUserTicketRele
|
|||||||
*/
|
*/
|
||||||
List<GltUserTicketRelease> selectListRel(@Param("param") GltUserTicketReleaseParam param);
|
List<GltUserTicketRelease> selectListRel(@Param("param") GltUserTicketReleaseParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当月待释放水票数据
|
||||||
|
* @param limitNum 查询数量
|
||||||
|
* @return List<User>
|
||||||
|
*/
|
||||||
|
List<GltUserTicketRelease> getThisMonthReleaseList(@Param("limitNum") Integer limitNum);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询待释放且到期的记录(加行锁,防止多实例重复处理)
|
* 查询待释放且到期的记录(加行锁,防止多实例重复处理)
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
d.name as receiverName, d.phone as receiverPhone,
|
d.name as receiverName, d.phone as receiverPhone,
|
||||||
d.province as receiverProvince, d.city as receiverCity, d.region as receiverRegion,
|
d.province as receiverProvince, d.city as receiverCity, d.region as receiverRegion,
|
||||||
d.address as receiverAddress, d.full_address as receiverFullAddress, d.lat as receiverLat, d.lng as receiverLng,
|
d.address as receiverAddress, d.full_address as receiverFullAddress, d.lat as receiverLat, d.lng as receiverLng,
|
||||||
COALESCE(o.order_no, f.order_no) as orderNo, o.order_status as orderStatus
|
COALESCE(o.order_no, a.order_no) as orderNo, o.order_status as orderStatus
|
||||||
FROM glt_ticket_order a
|
FROM glt_ticket_order a
|
||||||
LEFT JOIN shop_store b ON a.store_id = b.id
|
LEFT JOIN shop_store b ON a.store_id = b.id
|
||||||
LEFT JOIN shop_store_warehouse w ON a.warehouse_id = w.id
|
LEFT JOIN shop_store_warehouse w ON a.warehouse_id = w.id
|
||||||
@@ -20,7 +20,6 @@
|
|||||||
LEFT JOIN shop_user_address d ON a.address_id = d.id
|
LEFT JOIN shop_user_address d ON a.address_id = d.id
|
||||||
LEFT JOIN glt_user_ticket f ON a.user_ticket_id = f.id
|
LEFT JOIN glt_user_ticket f ON a.user_ticket_id = f.id
|
||||||
LEFT JOIN shop_order o ON f.order_id = o.order_id AND f.tenant_id = o.tenant_id AND o.deleted = 0
|
LEFT JOIN shop_order o ON f.order_id = o.order_id AND f.tenant_id = o.tenant_id AND o.deleted = 0
|
||||||
|
|
||||||
<where>
|
<where>
|
||||||
<if test="param.id != null">
|
<if test="param.id != null">
|
||||||
AND a.id = #{param.id}
|
AND a.id = #{param.id}
|
||||||
@@ -88,7 +87,7 @@
|
|||||||
AND a.create_time <= #{param.createTimeEnd}
|
AND a.create_time <= #{param.createTimeEnd}
|
||||||
</if>
|
</if>
|
||||||
<if test="param.orderNo != null">
|
<if test="param.orderNo != null">
|
||||||
AND (a.id = #{param.orderNo} OR COALESCE(o.order_no, f.order_no) = #{param.orderNo})
|
AND (a.id = #{param.orderNo} OR COALESCE(o.order_no, a.order_no) = #{param.orderNo})
|
||||||
</if>
|
</if>
|
||||||
<if test="param.phone != null">
|
<if test="param.phone != null">
|
||||||
AND u.phone = #{param.phone}
|
AND u.phone = #{param.phone}
|
||||||
@@ -103,6 +102,12 @@
|
|||||||
OR u.phone LIKE CONCAT('%', #{param.keywords}, '%')
|
OR u.phone LIKE CONCAT('%', #{param.keywords}, '%')
|
||||||
)
|
)
|
||||||
</if>
|
</if>
|
||||||
|
<if test="param.waterTicketFlag != null">
|
||||||
|
AND a.water_ticket_flag = #{param.waterTicketFlag}
|
||||||
|
</if>
|
||||||
|
<if test="param.deliveryStatus != null and param.deliveryStatus == 10">
|
||||||
|
AND o.order_status in (0, 1, 5)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
|
|||||||
@@ -58,5 +58,23 @@
|
|||||||
<select id="selectListRel" resultType="com.gxwebsoft.glt.entity.GltUserTicketRelease">
|
<select id="selectListRel" resultType="com.gxwebsoft.glt.entity.GltUserTicketRelease">
|
||||||
<include refid="selectSql"></include>
|
<include refid="selectSql"></include>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="getThisMonthReleaseList" resultType="com.gxwebsoft.glt.entity.GltUserTicketRelease">
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
user_ticket_id,
|
||||||
|
user_id,
|
||||||
|
release_qty,
|
||||||
|
tenant_id
|
||||||
|
FROM
|
||||||
|
glt_user_ticket_release
|
||||||
|
WHERE
|
||||||
|
STATUS = 0
|
||||||
|
AND deleted = 0
|
||||||
|
AND release_qty > 0
|
||||||
|
AND DATE_FORMAT(release_time, '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m')
|
||||||
|
ORDER BY
|
||||||
|
release_time ASC
|
||||||
|
LIMIT #{limitNum}
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -94,4 +94,7 @@ public class GltTicketOrderParam extends BaseParam {
|
|||||||
@QueryField(type = QueryType.EQ)
|
@QueryField(type = QueryType.EQ)
|
||||||
private Integer orderStatus;
|
private Integer orderStatus;
|
||||||
|
|
||||||
|
@Schema(description = "水票订单标识 0-否 1-是")
|
||||||
|
private Integer waterTicketFlag;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.gxwebsoft.glt.service;
|
||||||
|
|
||||||
|
import com.gxwebsoft.glt.dto.NoticeRiderNewOrderDto;
|
||||||
|
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信订阅消息服务接口
|
||||||
|
*/
|
||||||
|
public interface GltSubscribeMessageService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送新订单通知给配送员
|
||||||
|
* @param order 订单信息
|
||||||
|
* @param riderOpenId 配送员微信openId
|
||||||
|
* @param tenantId 租户ID
|
||||||
|
* @return 是否发送成功
|
||||||
|
*/
|
||||||
|
boolean sendNewOrderNotice(GltTicketOrder order, String riderOpenId, Integer tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送订单状态变更通知
|
||||||
|
* @param order 订单信息
|
||||||
|
* @param riderOpenId 配送员微信openId
|
||||||
|
* @param statusText 状态描述
|
||||||
|
* @param tenantId 租户ID
|
||||||
|
* @return 是否发送成功
|
||||||
|
*/
|
||||||
|
boolean sendOrderStatusNotice(GltTicketOrder order, String riderOpenId, String statusText, Integer tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送配送师傅接单提醒
|
||||||
|
* @param noticeRiderNewOrderDto
|
||||||
|
*/
|
||||||
|
void sendRiderNewOrderNotice(NoticeRiderNewOrderDto noticeRiderNewOrderDto);
|
||||||
|
}
|
||||||
@@ -1,28 +1,26 @@
|
|||||||
package com.gxwebsoft.glt.service;
|
package com.gxwebsoft.glt.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.gxwebsoft.glt.entity.*;
|
||||||
import com.gxwebsoft.glt.entity.GltTicketTemplate;
|
import com.gxwebsoft.glt.task.DealerOrderSettlement10584Task;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicket;
|
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketLog;
|
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
||||||
|
import com.gxwebsoft.shop.mapper.ShopOrderMapper;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 套票发放(从订单生成用户套票 + 释放计划)的业务逻辑。
|
* 套票发放(从订单生成用户套票 + 释放计划)的业务逻辑。
|
||||||
@@ -37,6 +35,9 @@ import java.util.Set;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class GltTicketIssueService {
|
public class GltTicketIssueService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private GltTicketOrderService gltTicketOrderService;
|
||||||
|
|
||||||
public static final int CHANGE_TYPE_ISSUE = 10;
|
public static final int CHANGE_TYPE_ISSUE = 10;
|
||||||
|
|
||||||
private enum IssueOutcome {
|
private enum IssueOutcome {
|
||||||
@@ -46,7 +47,7 @@ public class GltTicketIssueService {
|
|||||||
NO_TEMPLATE
|
NO_TEMPLATE
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ShopOrderService shopOrderService;
|
private final ShopOrderMapper shopOrderMapper;
|
||||||
private final ShopOrderGoodsService shopOrderGoodsService;
|
private final ShopOrderGoodsService shopOrderGoodsService;
|
||||||
|
|
||||||
private final GltTicketTemplateService gltTicketTemplateService;
|
private final GltTicketTemplateService gltTicketTemplateService;
|
||||||
@@ -54,6 +55,7 @@ public class GltTicketIssueService {
|
|||||||
private final GltUserTicketReleaseService gltUserTicketReleaseService;
|
private final GltUserTicketReleaseService gltUserTicketReleaseService;
|
||||||
private final GltUserTicketLogService gltUserTicketLogService;
|
private final GltUserTicketLogService gltUserTicketLogService;
|
||||||
private final TransactionTemplate transactionTemplate;
|
private final TransactionTemplate transactionTemplate;
|
||||||
|
private final DealerOrderSettlement10584Task dealerOrderSettlement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扫描“今日订单”,执行套票发放。
|
* 扫描“今日订单”,执行套票发放。
|
||||||
@@ -84,7 +86,7 @@ public class GltTicketIssueService {
|
|||||||
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
|
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
|
||||||
LocalDateTime tomorrowStart = todayStart.plusDays(1);
|
LocalDateTime tomorrowStart = todayStart.plusDays(1);
|
||||||
|
|
||||||
List<ShopOrder> orders = shopOrderService.list(
|
List<ShopOrder> orders = shopOrderMapper.selectList(
|
||||||
new LambdaQueryWrapper<ShopOrder>()
|
new LambdaQueryWrapper<ShopOrder>()
|
||||||
.eq(ShopOrder::getTenantId, tenantId)
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
.in(ShopOrder::getFormId, uniqueGoodsIds)
|
.in(ShopOrder::getFormId, uniqueGoodsIds)
|
||||||
@@ -128,6 +130,74 @@ public class GltTicketIssueService {
|
|||||||
tenantId, uniqueGoodsIds, orders.size(), success, skipped, failed);
|
tenantId, uniqueGoodsIds, orders.size(), success, skipped, failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品订单支付成功后调后需处理业务
|
||||||
|
* @param orderNo 订单号
|
||||||
|
* @param tenantId 租户ID
|
||||||
|
*/
|
||||||
|
@Async
|
||||||
|
public void paySuccessTask(String orderNo, Integer tenantId){
|
||||||
|
//1.发送水票
|
||||||
|
suerTicketRelease(orderNo, tenantId);
|
||||||
|
|
||||||
|
//2.执行分销员分销、统计门店/服务商分销业务
|
||||||
|
dealerOrderSettlement.orderSettlement(orderNo);
|
||||||
|
|
||||||
|
//3.执行平台分红业务 TODO 待开发
|
||||||
|
|
||||||
|
//4.普通商品【非水票订单】如果是需要自配送的,则会同步生成派单信息
|
||||||
|
gltTicketOrderService.dispatchOrder(orderNo, tenantId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单支付成功,直接发送水票【后期优化订单类型,为水票的订单才需要执行此业务】
|
||||||
|
* @param orderNo 订单号
|
||||||
|
* @param tenantId 租户ID
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void suerTicketRelease(String orderNo, Integer tenantId){
|
||||||
|
//1.订单为空跳过执行
|
||||||
|
ShopOrder shopOrder = shopOrderMapper.selectOne(new LambdaQueryWrapper<ShopOrder>()
|
||||||
|
.eq(ShopOrder::getOrderNo, orderNo)
|
||||||
|
.eq(ShopOrder::getTenantId, tenantId));
|
||||||
|
if(shopOrder == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//2.只有水票订单才需要发送水票
|
||||||
|
if(!(shopOrder.getWaterTicketFlag() != null && shopOrder.getWaterTicketFlag() == 1)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//3.跳过已完成发放套票订单
|
||||||
|
if(shopOrder.getOrderStatus() == 1){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//4.订单商品为空跳过执行
|
||||||
|
List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(shopOrder.getOrderId());
|
||||||
|
if (CollectionUtils.isEmpty(goodsList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//5.执行水票发放业务
|
||||||
|
AtomicBoolean release = new AtomicBoolean(false);
|
||||||
|
goodsList.forEach(orderGood ->{
|
||||||
|
IssueOutcome outcome = transactionTemplate.execute(status -> doIssueOne(tenantId, shopOrder, orderGood));
|
||||||
|
if(Arrays.asList(IssueOutcome.ISSUED, IssueOutcome.ALREADY_ISSUED).contains(outcome)){
|
||||||
|
release.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//6.更新商品订单为已完成、已收到赠品状态
|
||||||
|
if (release.get()) {
|
||||||
|
shopOrder.setHasTakeGift(true);
|
||||||
|
shopOrder.setUpdateTime(LocalDateTime.now());
|
||||||
|
shopOrderMapper.updateById(shopOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private int issueForOrder(Integer tenantId, Set<Integer> goodsIds, ShopOrder order) {
|
private int issueForOrder(Integer tenantId, Set<Integer> goodsIds, ShopOrder order) {
|
||||||
List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
|
List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
|
||||||
if (goodsList == null || goodsList.isEmpty()) {
|
if (goodsList == null || goodsList.isEmpty()) {
|
||||||
@@ -155,15 +225,10 @@ public class GltTicketIssueService {
|
|||||||
if (shouldCompleteOrder) {
|
if (shouldCompleteOrder) {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
// 任务执行完后将订单置为“已完成”,避免后续扫描重复处理(幂等虽可挡住,但会产生大量无意义查询)。
|
// 任务执行完后将订单置为“已完成”,避免后续扫描重复处理(幂等虽可挡住,但会产生大量无意义查询)。
|
||||||
shopOrderService.update(
|
order.setOrderStatus(1);
|
||||||
new LambdaUpdateWrapper<ShopOrder>()
|
order.setHasTakeGift(true);
|
||||||
.eq(ShopOrder::getOrderId, order.getOrderId())
|
order.setUpdateTime(LocalDateTime.now());
|
||||||
.eq(ShopOrder::getTenantId, tenantId)
|
shopOrderMapper.updateById(order);
|
||||||
.eq(ShopOrder::getOrderStatus, 0)
|
|
||||||
.set(ShopOrder::getOrderStatus, 1)
|
|
||||||
.set(ShopOrder::getHasTakeGift, true)
|
|
||||||
.set(ShopOrder::getUpdateTime, now)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return issuedCount;
|
return issuedCount;
|
||||||
@@ -184,7 +249,7 @@ public class GltTicketIssueService {
|
|||||||
// - 这里先对商城订单行加行锁,保证同一订单在同一时刻只会被一个事务处理。
|
// - 这里先对商城订单行加行锁,保证同一订单在同一时刻只会被一个事务处理。
|
||||||
// (注意:需数据库支持 SELECT ... FOR UPDATE,且 shop_order.order_id 为主键/有索引)
|
// (注意:需数据库支持 SELECT ... FOR UPDATE,且 shop_order.order_id 为主键/有索引)
|
||||||
if (order.getOrderId() != null) {
|
if (order.getOrderId() != null) {
|
||||||
shopOrderService.getOne(
|
shopOrderMapper.selectOne(
|
||||||
new LambdaQueryWrapper<ShopOrder>()
|
new LambdaQueryWrapper<ShopOrder>()
|
||||||
.eq(ShopOrder::getOrderId, order.getOrderId())
|
.eq(ShopOrder::getOrderId, order.getOrderId())
|
||||||
.eq(ShopOrder::getTenantId, tenantId)
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
@@ -304,12 +369,14 @@ public class GltTicketIssueService {
|
|||||||
|
|
||||||
// 若启用了 releasePeriods 且首期释放时机为“支付成功当刻”,则将首期释放量直接计入可用,
|
// 若启用了 releasePeriods 且首期释放时机为“支付成功当刻”,则将首期释放量直接计入可用,
|
||||||
// 避免用户刚购买后短时间内无可用水票;后续期数仍由自动释放任务按 release_time 释放。
|
// 避免用户刚购买后短时间内无可用水票;后续期数仍由自动释放任务按 release_time 释放。
|
||||||
if (useReleasePeriods && !releases.isEmpty() && !Objects.equals(template.getFirstReleaseMode(), 1)) {
|
// if (useReleasePeriods && !releases.isEmpty() && !Objects.equals(template.getFirstReleaseMode(), 1)) {
|
||||||
|
if (!releases.isEmpty() && !Objects.equals(template.getFirstReleaseMode(), 1)) {
|
||||||
GltUserTicketRelease first = releases.get(0);
|
GltUserTicketRelease first = releases.get(0);
|
||||||
Integer firstQtyObj = first.getReleaseQty();
|
Integer firstQtyObj = first.getReleaseQty();
|
||||||
LocalDateTime firstTime = first.getReleaseTime();
|
LocalDateTime firstTime = first.getReleaseTime();
|
||||||
int firstQty = firstQtyObj != null ? firstQtyObj : 0;
|
int firstQty = firstQtyObj != null ? firstQtyObj : 0;
|
||||||
if (firstQty > 0 && (firstTime == null || !firstTime.isAfter(now))) {
|
// if (firstQty > 0 && (firstTime == null || !firstTime.isAfter(now))) {
|
||||||
|
if (firstQty > 0) {
|
||||||
first.setStatus(1);
|
first.setStatus(1);
|
||||||
first.setUpdateTime(now);
|
first.setUpdateTime(now);
|
||||||
|
|
||||||
@@ -376,10 +443,13 @@ public class GltTicketIssueService {
|
|||||||
|
|
||||||
// 首期释放时间
|
// 首期释放时间
|
||||||
LocalDateTime firstReleaseTime;
|
LocalDateTime firstReleaseTime;
|
||||||
|
LocalDateTime referenceTime;
|
||||||
if (Objects.equals(template.getFirstReleaseMode(), 1)) {
|
if (Objects.equals(template.getFirstReleaseMode(), 1)) {
|
||||||
firstReleaseTime = nextMonthSameDay(baseTime);
|
firstReleaseTime = nextMonthSameDay(baseTime);
|
||||||
|
referenceTime = firstReleaseTime.withDayOfMonth(1).toLocalDate().atStartOfDay();
|
||||||
} else {
|
} else {
|
||||||
firstReleaseTime = baseTime;
|
firstReleaseTime = baseTime;
|
||||||
|
referenceTime = firstReleaseTime.withDayOfMonth(1).toLocalDate().atStartOfDay();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 每期释放数量计算
|
// 每期释放数量计算
|
||||||
@@ -393,7 +463,11 @@ public class GltTicketIssueService {
|
|||||||
if (qty <= 0) {
|
if (qty <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i), now));
|
if(i == 0){
|
||||||
|
list.add(buildRelease(userTicket, i, qty, firstReleaseTime, now));
|
||||||
|
}else {
|
||||||
|
list.add(buildRelease(userTicket, i, qty, referenceTime.plusMonths(i), now));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -410,7 +484,11 @@ public class GltTicketIssueService {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
remaining -= qty;
|
remaining -= qty;
|
||||||
list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i), now));
|
if(i == 0){
|
||||||
|
list.add(buildRelease(userTicket, i, qty, firstReleaseTime, now));
|
||||||
|
}else {
|
||||||
|
list.add(buildRelease(userTicket, i, qty, referenceTime.plusMonths(i), now));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
@@ -422,7 +500,7 @@ public class GltTicketIssueService {
|
|||||||
LocalDateTime releaseTime,
|
LocalDateTime releaseTime,
|
||||||
LocalDateTime now) {
|
LocalDateTime now) {
|
||||||
GltUserTicketRelease r = new GltUserTicketRelease();
|
GltUserTicketRelease r = new GltUserTicketRelease();
|
||||||
r.setUserTicketId(userTicket.getId() != null ? userTicket.getId().longValue() : null);
|
r.setUserTicketId(userTicket.getId() != null ? userTicket.getId() : null);
|
||||||
r.setUserId(userTicket.getUserId());
|
r.setUserId(userTicket.getUserId());
|
||||||
r.setPeriodNo(periodNo);
|
r.setPeriodNo(periodNo);
|
||||||
r.setReleaseQty(releaseQty);
|
r.setReleaseQty(releaseQty);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.gxwebsoft.glt.service;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.glt.dto.GltTransferOrderDto;
|
||||||
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
||||||
import com.gxwebsoft.glt.param.GltTicketOrderParam;
|
import com.gxwebsoft.glt.param.GltTicketOrderParam;
|
||||||
|
|
||||||
@@ -64,6 +65,13 @@ public interface GltTicketOrderService extends IService<GltTicketOrder> {
|
|||||||
*/
|
*/
|
||||||
void accept(Integer id, Integer riderId, Integer tenantId);
|
void accept(Integer id, Integer riderId, Integer tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配送员转单
|
||||||
|
* @param orderDto
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Boolean transferOrder(GltTransferOrderDto orderDto);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 指派/接单成功后,同步关联商城订单发货状态为“已发货”(deliveryStatus=20)。
|
* 指派/接单成功后,同步关联商城订单发货状态为“已发货”(deliveryStatus=20)。
|
||||||
*
|
*
|
||||||
@@ -76,7 +84,7 @@ public interface GltTicketOrderService extends IService<GltTicketOrder> {
|
|||||||
*
|
*
|
||||||
* <p>用于后台直接改 deliveryStatus=40 等不经过 confirmReceive/autoConfirmTimeout 的兜底同步。</p>
|
* <p>用于后台直接改 deliveryStatus=40 等不经过 confirmReceive/autoConfirmTimeout 的兜底同步。</p>
|
||||||
*/
|
*/
|
||||||
void markShopOrderCompletedAfterTicketFinished(Integer ticketOrderId, Integer tenantId);
|
void markShopOrderCompletedAfterTicketFinished(Integer id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配送员开始配送:10 -> 20,并写 sendStartTime。
|
* 配送员开始配送:10 -> 20,并写 sendStartTime。
|
||||||
@@ -106,4 +114,10 @@ public interface GltTicketOrderService extends IService<GltTicketOrder> {
|
|||||||
*/
|
*/
|
||||||
int autoConfirmTimeout(Integer tenantId, LocalDateTime now, int timeoutHours, int batchSize);
|
int autoConfirmTimeout(Integer tenantId, LocalDateTime now, int timeoutHours, int batchSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 派送订单调度
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Boolean dispatchOrder(String orderNo, Integer tenantId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public class GltTicketRevokeService {
|
|||||||
LambdaUpdateWrapper<GltUserTicketRelease> uw = new LambdaUpdateWrapper<GltUserTicketRelease>()
|
LambdaUpdateWrapper<GltUserTicketRelease> uw = new LambdaUpdateWrapper<GltUserTicketRelease>()
|
||||||
.eq(GltUserTicketRelease::getTenantId, tenantId)
|
.eq(GltUserTicketRelease::getTenantId, tenantId)
|
||||||
.eq(GltUserTicketRelease::getDeleted, 0)
|
.eq(GltUserTicketRelease::getDeleted, 0)
|
||||||
.eq(GltUserTicketRelease::getUserTicketId, userTicketId.longValue())
|
.eq(GltUserTicketRelease::getUserTicketId, userTicketId)
|
||||||
// status 为空时也视为“未完成”
|
// status 为空时也视为“未完成”
|
||||||
.and(w -> w.ne(GltUserTicketRelease::getStatus, RELEASE_STATUS_DONE)
|
.and(w -> w.ne(GltUserTicketRelease::getStatus, RELEASE_STATUS_DONE)
|
||||||
.or().isNull(GltUserTicketRelease::getStatus))
|
.or().isNull(GltUserTicketRelease::getStatus))
|
||||||
|
|||||||
@@ -17,10 +17,8 @@ public interface GltUserTicketAutoReleaseService {
|
|||||||
int releaseDue(LocalDateTime now, int batchSize);
|
int releaseDue(LocalDateTime now, int batchSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 释放到期的冻结水票(使用系统当前时间)
|
* 释放到期的冻结水票【当次执行1000条】
|
||||||
*/
|
*/
|
||||||
default int releaseDue(int batchSize) {
|
void releaseTask();
|
||||||
return releaseDue(LocalDateTime.now(), batchSize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,266 @@
|
|||||||
|
package com.gxwebsoft.glt.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.gxwebsoft.common.core.exception.BusinessException;
|
||||||
|
import com.gxwebsoft.common.core.utils.DateTimeUtil;
|
||||||
|
import com.gxwebsoft.glt.dto.NoticeRiderNewOrderDto;
|
||||||
|
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
||||||
|
import com.gxwebsoft.glt.service.GltSubscribeMessageService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.gxwebsoft.common.core.constants.RedisConstants.ACCESS_TOKEN_KEY;
|
||||||
|
import static com.gxwebsoft.common.core.constants.RedisConstants.MP_WX_KEY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信订阅消息服务实现
|
||||||
|
*
|
||||||
|
* <p>功能:
|
||||||
|
* <ul>
|
||||||
|
* <li>新订单通知配送员</li>
|
||||||
|
* <li>订单状态变更通知</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class GltSubscribeMessageServiceImpl implements GltSubscribeMessageService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订阅消息模板ID(需在微信公众平台配置)
|
||||||
|
* 模板名称:订单配送通知
|
||||||
|
* 关键词:订单编号、订单内容、配送地址、订单金额
|
||||||
|
*/
|
||||||
|
private static final String ORDER_DELIVERY_ID = "vSMSqGVy3aG1RuzQUZlk282p5hCvuMBcHvix1AFhN90";
|
||||||
|
|
||||||
|
|
||||||
|
public void sendRiderNewOrderNotice(NoticeRiderNewOrderDto entity){
|
||||||
|
//1.获取微信accessToken
|
||||||
|
String accessToken = getAccessToken(entity.getTenantId());
|
||||||
|
if(accessToken == null){
|
||||||
|
log.error("配送发单消息发生失败:获取accessToken失败!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//2.品名字段最大能发送20个长度字符,大于15的长度需做处理
|
||||||
|
String goodsName = entity.getGoodsName();
|
||||||
|
int keepLength = 15;
|
||||||
|
if (goodsName.length() > keepLength) {
|
||||||
|
entity.setGoodsName(goodsName.substring(0, keepLength) + "...");
|
||||||
|
}
|
||||||
|
|
||||||
|
//3.组装入参
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("character_string1", Map.of("value", String.valueOf(entity.getOrderNo())));
|
||||||
|
data.put("thing22", Map.of("value", String.valueOf(entity.getGoodsName())));
|
||||||
|
data.put("number20", Map.of("value", String.valueOf(entity.getProductCount())));
|
||||||
|
data.put("time24", Map.of("value", DateTimeUtil.formatDateTime(entity.getCreateTime(), "yyyy-MM-dd HH:mm:ss")));
|
||||||
|
|
||||||
|
//推送订阅消息
|
||||||
|
sendSubscribeMessage(accessToken, entity.getOpenId(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送新订单通知给配送员
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean sendNewOrderNotice(GltTicketOrder order, String riderOpenId, Integer tenantId) {
|
||||||
|
if (order == null || StrUtil.isBlank(riderOpenId) || tenantId == null) {
|
||||||
|
log.warn("发送订阅消息参数不完整");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String accessToken = getAccessToken(tenantId);
|
||||||
|
if (StrUtil.isBlank(accessToken)) {
|
||||||
|
log.warn("获取access_token失败");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建消息内容
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("phrase1", Map.of("value", "待配送")); // 订单状态
|
||||||
|
data.put("character_string2", Map.of("value", String.valueOf(order.getOrderNo()))); // 订单编号
|
||||||
|
data.put("thing3", Map.of("value", truncateStr(order.getAddress(), 20))); // 配送地址
|
||||||
|
data.put("number4", Map.of("value", String.valueOf(order.getTotalNum()))); // 商品数量
|
||||||
|
data.put("time5", Map.of("value", formatTime(order.getSendTime()))); // 期望送达时间
|
||||||
|
|
||||||
|
// 发送订阅消息
|
||||||
|
return sendSubscribeMessage(accessToken, riderOpenId, data);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送新订单订阅消息失败 - orderId={}, riderOpenId={}, error={}",
|
||||||
|
order.getId(), riderOpenId, e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送订单状态变更通知
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean sendOrderStatusNotice(GltTicketOrder order, String riderOpenId, String statusText, Integer tenantId) {
|
||||||
|
if (order == null || StrUtil.isBlank(riderOpenId) || tenantId == null) {
|
||||||
|
log.warn("发送订阅消息参数不完整");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String accessToken = getAccessToken(tenantId);
|
||||||
|
if (StrUtil.isBlank(accessToken)) {
|
||||||
|
log.warn("获取access_token失败");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建消息内容
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("phrase1", Map.of("value", truncateStr(statusText, 5))); // 状态描述
|
||||||
|
data.put("character_string2", Map.of("value", String.valueOf(order.getId()))); // 订单编号
|
||||||
|
data.put("time3", Map.of("value", formatTime(null))); // 通知时间
|
||||||
|
|
||||||
|
// 发送订阅消息
|
||||||
|
return sendSubscribeMessage(accessToken, riderOpenId, data);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送订单状态变更订阅消息失败 - orderId={}, riderOpenId={}, error={}",
|
||||||
|
order.getId(), riderOpenId, e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取小程序的 access_token
|
||||||
|
*/
|
||||||
|
private String getAccessToken(Integer tenantId) {
|
||||||
|
if (tenantId == null) {
|
||||||
|
throw new BusinessException("tenantId 不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String tokenCacheKey = ACCESS_TOKEN_KEY + ":" + tenantId;
|
||||||
|
|
||||||
|
// 1) 优先从缓存取
|
||||||
|
String cachedValue = stringRedisTemplate.opsForValue().get(tokenCacheKey);
|
||||||
|
if (StrUtil.isNotBlank(cachedValue)) {
|
||||||
|
try {
|
||||||
|
JSONObject cachedJson = JSON.parseObject(cachedValue);
|
||||||
|
String accessToken = cachedJson.getString("access_token");
|
||||||
|
if (StrUtil.isNotBlank(accessToken)) {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 旧格式:直接存 token
|
||||||
|
return cachedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) 缓存没有则从租户配置获取 appId/appSecret
|
||||||
|
final String wxConfigKey = MP_WX_KEY + tenantId;
|
||||||
|
final String wxConfigValue = stringRedisTemplate.opsForValue().get(wxConfigKey);
|
||||||
|
if (StrUtil.isBlank(wxConfigValue)) {
|
||||||
|
log.warn("未找到微信小程序配置,请检查缓存key: {}", wxConfigKey);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject wxConfig;
|
||||||
|
try {
|
||||||
|
wxConfig = JSON.parseObject(wxConfigValue);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("微信小程序配置格式错误: {}", e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String appId = wxConfig.getString("appId");
|
||||||
|
final String appSecret = wxConfig.getString("appSecret");
|
||||||
|
if (StrUtil.isBlank(appId) || StrUtil.isBlank(appSecret)) {
|
||||||
|
log.error("微信小程序配置不完整(appId/appSecret)");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) 调用微信接口获取 token
|
||||||
|
final String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
|
||||||
|
+ "&appid=" + appId + "&secret=" + appSecret;
|
||||||
|
String result = HttpUtil.get(apiUrl);
|
||||||
|
|
||||||
|
JSONObject json = JSON.parseObject(result);
|
||||||
|
if (json.containsKey("errcode") && json.getIntValue("errcode") != 0) {
|
||||||
|
log.error("获取小程序access_token失败: {}", json.getString("errmsg"));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String accessToken = json.getString("access_token");
|
||||||
|
Integer expiresIn = json.getInteger("expires_in");
|
||||||
|
if (StrUtil.isBlank(accessToken)) {
|
||||||
|
log.error("获取小程序access_token失败: access_token为空");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) 缓存,提前5分钟过期
|
||||||
|
long ttlSeconds = 7000L;
|
||||||
|
if (expiresIn != null && expiresIn > 300) {
|
||||||
|
ttlSeconds = expiresIn - 300L;
|
||||||
|
}
|
||||||
|
stringRedisTemplate.opsForValue().set(tokenCacheKey, result, ttlSeconds, java.util.concurrent.TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
log.info("获取小程序access_token成功 - tenantId={}, ttlSeconds={}", tenantId, ttlSeconds);
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送订阅消息
|
||||||
|
*/
|
||||||
|
private boolean sendSubscribeMessage(String accessToken, String openId, Map<String, Object> data) {
|
||||||
|
String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken;
|
||||||
|
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("touser", openId); // 用户 openid
|
||||||
|
params.put("template_id", ORDER_DELIVERY_ID); // 模板ID
|
||||||
|
// params.put("page", "pages/rider/orders/index"); // 点击后跳转的页面
|
||||||
|
params.put("data", data);
|
||||||
|
|
||||||
|
String response = HttpUtil.createPost(url)
|
||||||
|
.contentType("application/json")
|
||||||
|
.body(JSON.toJSONString(params))
|
||||||
|
.timeout(10000)
|
||||||
|
.execute()
|
||||||
|
.body();
|
||||||
|
|
||||||
|
JSONObject result = JSON.parseObject(response);
|
||||||
|
int errCode = result.getIntValue("errcode");
|
||||||
|
|
||||||
|
if (errCode == 0) {
|
||||||
|
log.info("订阅消息发送成功 - openId={}", openId);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
log.warn("订阅消息发送失败 - openId={}, errcode={}, errmsg={}",
|
||||||
|
openId, errCode, result.getString("errmsg"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截断字符串
|
||||||
|
*/
|
||||||
|
private String truncateStr(String str, int maxLen) {
|
||||||
|
if (str == null) return "";
|
||||||
|
return str.length() > maxLen ? str.substring(0, maxLen) : str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化时间
|
||||||
|
*/
|
||||||
|
private String formatTime(String timeStr) {
|
||||||
|
if (StrUtil.isBlank(timeStr)) {
|
||||||
|
return cn.hutool.core.date.DateUtil.now();
|
||||||
|
}
|
||||||
|
return timeStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,44 +1,60 @@
|
|||||||
package com.gxwebsoft.glt.service.impl;
|
package com.gxwebsoft.glt.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum;
|
||||||
|
import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum;
|
||||||
import com.gxwebsoft.common.core.exception.BusinessException;
|
import com.gxwebsoft.common.core.exception.BusinessException;
|
||||||
|
import com.gxwebsoft.common.core.exception.enums.GlobalErrorCodeConstants;
|
||||||
|
import com.gxwebsoft.common.core.utils.LoginUserUtil;
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
import com.gxwebsoft.common.core.web.PageParam;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.common.system.mapper.UserMapper;
|
import com.gxwebsoft.common.system.mapper.UserMapper;
|
||||||
|
import com.gxwebsoft.common.system.redis.OrderNoUtils;
|
||||||
|
import com.gxwebsoft.glt.dto.GltTransferOrderDto;
|
||||||
|
import com.gxwebsoft.glt.dto.NoticeRiderNewOrderDto;
|
||||||
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicket;
|
import com.gxwebsoft.glt.entity.GltUserTicket;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketLog;
|
import com.gxwebsoft.glt.entity.GltUserTicketLog;
|
||||||
import com.gxwebsoft.glt.mapper.GltTicketOrderMapper;
|
import com.gxwebsoft.glt.mapper.GltTicketOrderMapper;
|
||||||
import com.gxwebsoft.glt.mapper.GltUserTicketMapper;
|
import com.gxwebsoft.glt.mapper.GltUserTicketMapper;
|
||||||
import com.gxwebsoft.glt.param.GltTicketOrderParam;
|
import com.gxwebsoft.glt.param.GltTicketOrderParam;
|
||||||
|
import com.gxwebsoft.glt.service.GltSubscribeMessageService;
|
||||||
import com.gxwebsoft.glt.service.GltTicketOrderService;
|
import com.gxwebsoft.glt.service.GltTicketOrderService;
|
||||||
import com.gxwebsoft.glt.service.GltUserTicketLogService;
|
import com.gxwebsoft.glt.service.GltUserTicketLogService;
|
||||||
import com.gxwebsoft.glt.service.GltUserTicketService;
|
import com.gxwebsoft.glt.service.GltUserTicketService;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerCapital;
|
import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
import com.gxwebsoft.shop.entity.*;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
import com.gxwebsoft.shop.mapper.ShopGoodsMapper;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
import com.gxwebsoft.shop.mapper.ShopStoreRiderMapper;
|
||||||
|
import com.gxwebsoft.shop.mapper.ShopUserAddressMapper;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderService;
|
import com.gxwebsoft.shop.service.ShopOrderService;
|
||||||
|
import com.gxwebsoft.shop.vo.ShopOrderGoodsInfoVO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.util.StringUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 送水订单Service实现
|
* 送水订单Service实现
|
||||||
@@ -83,11 +99,54 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
@Resource
|
@Resource
|
||||||
private ShopOrderGoodsService shopOrderGoodsService;
|
private ShopOrderGoodsService shopOrderGoodsService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ShopUserAddressMapper shopUserAddressMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private OrderNoUtils orderNoUtils;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ShopGoodsMapper shopGoodsMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ShopStoreRiderMapper shopStoreRiderMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private GltSubscribeMessageService gltSubscribeMessageService;
|
||||||
|
|
||||||
|
// 轮询指针(高并发安全)
|
||||||
|
private final AtomicInteger index = new AtomicInteger(0);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<GltTicketOrder> pageRel(GltTicketOrderParam param) {
|
public PageResult<GltTicketOrder> pageRel(GltTicketOrderParam param) {
|
||||||
PageParam<GltTicketOrder, GltTicketOrderParam> page = new PageParam<>(param);
|
PageParam<GltTicketOrder, GltTicketOrderParam> page = new PageParam<>(param);
|
||||||
page.setDefaultOrder("sort_number asc, create_time desc");
|
page.setDefaultOrder("create_time desc");
|
||||||
List<GltTicketOrder> list = baseMapper.selectPageRel(page, param);
|
List<GltTicketOrder> list = baseMapper.selectPageRel(page, param);
|
||||||
|
if(CollectionUtils.isNotEmpty(list)){
|
||||||
|
List<Integer> addressIdList = list.stream().map(GltTicketOrder::getAddressId).distinct().collect(Collectors.toList());
|
||||||
|
List<String> orderNoList = list.stream().filter(gltTicketOrder -> StrUtil.isNotBlank(gltTicketOrder.getOrderNo())).map(GltTicketOrder::getOrderNo).distinct().collect(Collectors.toList());
|
||||||
|
List<ShopOrderGoodsInfoVO> orderGoodsInfoVOList = new ArrayList<>();
|
||||||
|
if(CollectionUtils.isNotEmpty(orderNoList)){
|
||||||
|
orderGoodsInfoVOList = shopOrderService.getOrderGoodsInfoByOrderNos(orderNoList);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ShopUserAddress> userAddressList = shopUserAddressMapper.selectBatchIds(addressIdList);
|
||||||
|
List<ShopOrderGoodsInfoVO> finalOrderGoodsInfoVOList = orderGoodsInfoVOList;
|
||||||
|
list.forEach(ticketOrder ->{
|
||||||
|
ShopUserAddress shopUserAddress = userAddressList.stream().filter(address -> ticketOrder.getAddressId().equals(address.getId())).findFirst().orElse(null);
|
||||||
|
if(shopUserAddress != null){
|
||||||
|
ticketOrder.setFullAddress(shopUserAddress.getFullAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(StrUtil.isNotBlank(ticketOrder.getOrderNo())){
|
||||||
|
ShopOrderGoodsInfoVO shopOrderGoodsInfoVO = finalOrderGoodsInfoVOList.stream().filter(orderGoodsInfoVO -> ticketOrder.getOrderNo().equals(orderGoodsInfoVO.getOrderNo())).findFirst().orElse(null);
|
||||||
|
if(shopOrderGoodsInfoVO != null){
|
||||||
|
ticketOrder.setGoodsName(shopOrderGoodsInfoVO.getGoodsName());
|
||||||
|
ticketOrder.setOrderStatus(shopOrderGoodsInfoVO.getOrderStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return new PageResult<>(list, page.getTotal());
|
return new PageResult<>(list, page.getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +175,10 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
throw new BusinessException("请先登录");
|
throw new BusinessException("请先登录");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String no = orderNoUtils.generate("S");
|
||||||
|
gltTicketOrder.setNo(no);
|
||||||
|
|
||||||
Integer userTicketId = gltTicketOrder.getUserTicketId();
|
Integer userTicketId = gltTicketOrder.getUserTicketId();
|
||||||
if (userTicketId == null) {
|
if (userTicketId == null) {
|
||||||
throw new BusinessException("userTicketId不能为空");
|
throw new BusinessException("userTicketId不能为空");
|
||||||
@@ -151,6 +214,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4) 插入 glt_ticket_order(storeId/addressId/totalNum/buyerRemarks…)
|
// 4) 插入 glt_ticket_order(storeId/addressId/totalNum/buyerRemarks…)
|
||||||
|
gltTicketOrder.setOrderNo(userTicket.getOrderNo());
|
||||||
gltTicketOrder.setUserId(userId);
|
gltTicketOrder.setUserId(userId);
|
||||||
// 订单基础字段由后端兜底,避免前端误传/恶意传参
|
// 订单基础字段由后端兜底,避免前端误传/恶意传参
|
||||||
gltTicketOrder.setStatus(0);
|
gltTicketOrder.setStatus(0);
|
||||||
@@ -168,10 +232,13 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
gltTicketOrder.setCreateTime(now);
|
gltTicketOrder.setCreateTime(now);
|
||||||
}
|
}
|
||||||
// “立刻送水”下单场景不再需要前端选择配送时间;若未传则默认当前时间,便于排序与派单。
|
// “立刻送水”下单场景不再需要前端选择配送时间;若未传则默认当前时间,便于排序与派单。
|
||||||
if (!StringUtils.hasText(gltTicketOrder.getSendTime())) {
|
if(gltTicketOrder.getSendEndTime() == null){
|
||||||
gltTicketOrder.setSendTime(now.format(SEND_TIME_FMT));
|
gltTicketOrder.setSendTime(now.format(SEND_TIME_FMT));
|
||||||
}
|
}
|
||||||
|
|
||||||
gltTicketOrder.setUpdateTime(now);
|
gltTicketOrder.setUpdateTime(now);
|
||||||
|
//每个骑手平等获取派单
|
||||||
|
gltTicketOrder.setRiderId(getRiderUserId());
|
||||||
if (!this.save(gltTicketOrder)) {
|
if (!this.save(gltTicketOrder)) {
|
||||||
throw new BusinessException("创建订单失败");
|
throw new BusinessException("创建订单失败");
|
||||||
}
|
}
|
||||||
@@ -207,6 +274,29 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
throw new BusinessException("写入核销记录失败");
|
throw new BusinessException("写入核销记录失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//发送配送师傅派单配送消息
|
||||||
|
if(gltTicketOrder.getRiderId() != null){
|
||||||
|
User user = userMapper.getById(gltTicketOrder.getRiderId());
|
||||||
|
if(user != null){
|
||||||
|
NoticeRiderNewOrderDto noticeDto = new NoticeRiderNewOrderDto();
|
||||||
|
noticeDto.setOrderNo(gltTicketOrder.getOrderNo());
|
||||||
|
noticeDto.setGoodsName("商品信息见订单详情");
|
||||||
|
noticeDto.setProductCount(gltTicketOrder.getTotalNum());
|
||||||
|
noticeDto.setCreateTime(gltTicketOrder.getCreateTime());
|
||||||
|
noticeDto.setTenantId(tenantId);
|
||||||
|
noticeDto.setOpenId(user.getOpenid());
|
||||||
|
|
||||||
|
ShopOrder shopOrder = shopOrderService.getByOrderNo(gltTicketOrder.getOrderNo(), tenantId);
|
||||||
|
if(shopOrder != null){
|
||||||
|
Integer formId = shopOrder.getFormId();
|
||||||
|
ShopGoods shopGoods = shopGoodsMapper.selectById(formId);
|
||||||
|
if(shopGoods != null){
|
||||||
|
noticeDto.setGoodsName(shopGoods.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gltSubscribeMessageService.sendRiderNewOrderNotice(noticeDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
return gltTicketOrder;
|
return gltTicketOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +364,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
.update();
|
.update();
|
||||||
if (ok) {
|
if (ok) {
|
||||||
// 接单成功后,同步商城订单发货状态:10未发货 -> 20已发货
|
// 接单成功后,同步商城订单发货状态:10未发货 -> 20已发货
|
||||||
updateShopOrderDeliveryStatusAfterAccept(id, tenantId, riderId, now);
|
updateShopOrderDeliveryStatusAfterAcceptV2(id, tenantId, riderId, now);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,13 +384,34 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void markShopOrderShippedAfterRiderAssigned(Integer ticketOrderId, Integer tenantId, Integer riderId) {
|
public Boolean transferOrder(GltTransferOrderDto orderDto) {
|
||||||
updateShopOrderDeliveryStatusAfterAccept(ticketOrderId, tenantId, riderId, LocalDateTime.now());
|
User loginUser = LoginUserUtil.getLoginUser();
|
||||||
|
if(loginUser == null){
|
||||||
|
throw new BusinessException(GlobalErrorCodeConstants.UNAUTHORIZED.getMsg());
|
||||||
|
}
|
||||||
|
|
||||||
|
GltTicketOrder gltTicketOrder = baseMapper.selectById(orderDto.getId());
|
||||||
|
if(gltTicketOrder != null){
|
||||||
|
if(!gltTicketOrder.getRiderId().equals(loginUser.getUserId())){
|
||||||
|
throw new BusinessException("该订单归属非本人,转单操作失败!");
|
||||||
|
}
|
||||||
|
gltTicketOrder.setRiderId(orderDto.getUserId());
|
||||||
|
gltTicketOrder.setUpdateTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
return baseMapper.updateById(gltTicketOrder) > 0;
|
||||||
|
}else {
|
||||||
|
throw new BusinessException(GlobalErrorCodeConstants.NOT_FOUND.getMsg());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void markShopOrderCompletedAfterTicketFinished(Integer ticketOrderId, Integer tenantId) {
|
public void markShopOrderShippedAfterRiderAssigned(Integer ticketOrderId, Integer tenantId, Integer riderId) {
|
||||||
updateShopOrderOrderStatusAfterTicketFinished(ticketOrderId, tenantId, LocalDateTime.now());
|
updateShopOrderDeliveryStatusAfterAcceptV2(ticketOrderId, tenantId, riderId, LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markShopOrderCompletedAfterTicketFinished(Integer id) {
|
||||||
|
updateShopOrderOrderStatusAfterTicketFinishedV2(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateShopOrderDeliveryStatusAfterAccept(Integer ticketOrderId, Integer tenantId, Integer riderId, LocalDateTime now) {
|
private void updateShopOrderDeliveryStatusAfterAccept(Integer ticketOrderId, Integer tenantId, Integer riderId, LocalDateTime now) {
|
||||||
@@ -370,22 +481,6 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
shopOrderNo = order.getOrderNo();
|
shopOrderNo = order.getOrderNo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaUpdateWrapper<GltUserTicket> backfill = new LambdaUpdateWrapper<GltUserTicket>()
|
|
||||||
.eq(GltUserTicket::getTenantId, tenantId)
|
|
||||||
.eq(GltUserTicket::getDeleted, 0)
|
|
||||||
.eq(GltUserTicket::getId, userTicket.getId());
|
|
||||||
backfill.set(GltUserTicket::getOrderId, shopOrderId);
|
|
||||||
if (!StringUtils.hasText(userTicket.getOrderNo()) && StringUtils.hasText(shopOrderNo)) {
|
|
||||||
backfill.set(GltUserTicket::getOrderNo, shopOrderNo);
|
|
||||||
}
|
|
||||||
backfill.set(GltUserTicket::getUpdateTime, now);
|
|
||||||
try {
|
|
||||||
gltUserTicketService.update(backfill);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.debug("回填水票关联商城订单信息失败(不影响主流程) - tenantId={}, userTicketId={}, orderId={}, orderNo={}",
|
|
||||||
tenantId, userTicket.getId(), shopOrderId, shopOrderNo, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
|
LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
|
||||||
@@ -433,6 +528,20 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateShopOrderDeliveryStatusAfterAcceptV2(Integer ticketOrderId, Integer tenantId, Integer riderId, LocalDateTime now) {
|
||||||
|
GltTicketOrder gltTicketOrder = baseMapper.selectById(ticketOrderId);
|
||||||
|
if(gltTicketOrder != null && StrUtil.isNotBlank(gltTicketOrder.getOrderNo())){
|
||||||
|
String orderNo = gltTicketOrder.getOrderNo();
|
||||||
|
|
||||||
|
ShopOrder shopOrder = shopOrderService.getByOrderNo(orderNo, tenantId);
|
||||||
|
if(shopOrder != null && shopOrder.getOrderStatus() == 0){
|
||||||
|
shopOrder.setDeliveryStatus(20);
|
||||||
|
shopOrder.setUpdateTime(now);
|
||||||
|
shopOrderService.updateById(shopOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Integer id, Integer riderId, Integer tenantId) {
|
public void start(Integer id, Integer riderId, Integer tenantId) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
@@ -656,7 +765,80 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
return confirmed;
|
return confirmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateShopOrderOrderStatusAfterTicketFinished(Integer ticketOrderId, Integer tenantId, LocalDateTime now) {
|
@Override
|
||||||
|
public Boolean dispatchOrder(String orderNo, Integer tenantId) {
|
||||||
|
ShopOrder shopOrder = shopOrderService.getByOrderNo(orderNo, tenantId);
|
||||||
|
if(shopOrder != null){
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
//只有:1-及时自配送、3-预约自配送 方式会生成调度单
|
||||||
|
if(Arrays.asList(1, 3).contains(shopOrder.getOrderType())){
|
||||||
|
GltTicketOrder dispatchOrder = BeanUtil.toBean(shopOrder, GltTicketOrder.class);
|
||||||
|
|
||||||
|
String no = orderNoUtils.generate("S");
|
||||||
|
dispatchOrder.setNo(no);
|
||||||
|
dispatchOrder.setRiderId(getRiderUserId());
|
||||||
|
dispatchOrder.setSendTime(now.format(SEND_TIME_FMT));
|
||||||
|
Integer deliveryMethod = shopOrder.getDeliveryMethod();
|
||||||
|
if(deliveryMethod != null && deliveryMethod == 1){
|
||||||
|
dispatchOrder.setDeliveryMethod("stairs");
|
||||||
|
}else {
|
||||||
|
dispatchOrder.setDeliveryMethod("elevator");
|
||||||
|
}
|
||||||
|
dispatchOrder.setComments("系统自动派单");
|
||||||
|
dispatchOrder.setCreateTime(now);
|
||||||
|
dispatchOrder.setUpdateTime(now);
|
||||||
|
|
||||||
|
baseMapper.insert(dispatchOrder);
|
||||||
|
|
||||||
|
//推送配送师傅接单提醒
|
||||||
|
if(dispatchOrder.getRiderId() != null){
|
||||||
|
User user = userMapper.getById(dispatchOrder.getRiderId());
|
||||||
|
if(user != null){
|
||||||
|
NoticeRiderNewOrderDto noticeDto = new NoticeRiderNewOrderDto();
|
||||||
|
noticeDto.setOrderNo(dispatchOrder.getOrderNo());
|
||||||
|
noticeDto.setGoodsName("商品信息见订单详情");
|
||||||
|
noticeDto.setProductCount(dispatchOrder.getTotalNum());
|
||||||
|
noticeDto.setCreateTime(dispatchOrder.getCreateTime());
|
||||||
|
noticeDto.setTenantId(tenantId);
|
||||||
|
noticeDto.setOpenId(user.getOpenid());
|
||||||
|
|
||||||
|
if(shopOrder != null){
|
||||||
|
Integer formId = shopOrder.getFormId();
|
||||||
|
ShopGoods shopGoods = shopGoodsMapper.selectById(formId);
|
||||||
|
if(shopGoods != null){
|
||||||
|
noticeDto.setGoodsName(shopGoods.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gltSubscribeMessageService.sendRiderNewOrderNotice(noticeDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Boolean.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每个骑手平等获取派单
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Integer getRiderUserId(){
|
||||||
|
List<ShopStoreRider> shopStoreRiders = shopStoreRiderMapper.selectList(new LambdaQueryWrapper<ShopStoreRider>().select(ShopStoreRider::getUserId).in(ShopStoreRider::getWorkStatus, Arrays.asList(1, 2)));
|
||||||
|
if(CollectionUtils.isNotEmpty(shopStoreRiders)){
|
||||||
|
List<Integer> riderIdList = shopStoreRiders.stream().map(ShopStoreRider::getUserId).distinct().collect(Collectors.toList());
|
||||||
|
int i = index.getAndIncrement() % riderIdList.size();
|
||||||
|
return riderIdList.get(i);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新送水订单为已完成状态
|
||||||
|
* @param ticketOrderId
|
||||||
|
* @param tenantId
|
||||||
|
* @param now
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void updateShopOrderOrderStatusAfterTicketFinished(Integer ticketOrderId, Integer tenantId, LocalDateTime now) {
|
||||||
if (ticketOrderId == null || tenantId == null) {
|
if (ticketOrderId == null || tenantId == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -666,7 +848,8 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
|
|
||||||
// 找到关联水票的商城订单(glt_user_ticket.orderId / orderNo)
|
// 找到关联水票的商城订单(glt_user_ticket.orderId / orderNo)
|
||||||
GltTicketOrder ticketOrder = this.lambdaQuery()
|
GltTicketOrder ticketOrder = this.lambdaQuery()
|
||||||
.select(GltTicketOrder::getId, GltTicketOrder::getUserTicketId)
|
.select(GltTicketOrder::getId, GltTicketOrder::getUserTicketId, GltTicketOrder::getTotalNum, GltTicketOrder::getRiderId,
|
||||||
|
GltTicketOrder::getUserId, GltTicketOrder::getNo)
|
||||||
.eq(GltTicketOrder::getId, ticketOrderId)
|
.eq(GltTicketOrder::getId, ticketOrderId)
|
||||||
.eq(GltTicketOrder::getTenantId, tenantId)
|
.eq(GltTicketOrder::getTenantId, tenantId)
|
||||||
.eq(GltTicketOrder::getDeleted, 0)
|
.eq(GltTicketOrder::getDeleted, 0)
|
||||||
@@ -742,6 +925,80 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShopOrder order = shopOrderService.getByOrderNo(userTicket.getOrderNo(), userTicket.getTenantId());
|
||||||
|
//生成配送师傅可提现账户分佣金额数据【配送奖励(按商品设置)、配送提成(每桶0.1)】
|
||||||
|
int qty = ticketOrder.getTotalNum() == null ? 0 : ticketOrder.getTotalNum();
|
||||||
|
if (qty > 0) { //配送提成(每桶0.1)
|
||||||
|
BigDecimal money = RIDER_UNIT_COMMISSION
|
||||||
|
.multiply(BigDecimal.valueOf(qty))
|
||||||
|
.setScale(RIDER_COMMISSION_SCALE, RoundingMode.HALF_UP);
|
||||||
|
if (money.signum() > 0) {
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.WITHDRAW_ACCOUNT);
|
||||||
|
reduceDto.setUserId(ticketOrder.getRiderId());
|
||||||
|
reduceDto.setOrderUserId(ticketOrder.getUserId());
|
||||||
|
if(Arrays.asList(1, 3).contains(order.getOrderType())){
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
}else {
|
||||||
|
reduceDto.setOrderNo(ticketOrder.getNo());
|
||||||
|
}
|
||||||
|
reduceDto.setPrice(money);
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DELIVERY_INCOME);
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//配送奖励(按商品设置)
|
||||||
|
Integer goodsId = userTicket.getGoodsId();
|
||||||
|
ShopGoods shopGood = shopGoodsMapper.selectById(goodsId);
|
||||||
|
if(shopGood != null && order != null){
|
||||||
|
Integer commissionType = shopGood.getCommissionType();
|
||||||
|
BigDecimal money = BigDecimal.ZERO;
|
||||||
|
if(commissionType == 10){ //按金额
|
||||||
|
money = shopGood.getDeliveryMoney();
|
||||||
|
}else { //按比率
|
||||||
|
money = order.getPayPrice().multiply(shopGood.getDeliveryMoney()).divide(BigDecimal.valueOf(100), 3, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
if(money.compareTo(BigDecimal.ZERO) > 0){
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.WITHDRAW_ACCOUNT);
|
||||||
|
reduceDto.setUserId(ticketOrder.getRiderId());
|
||||||
|
reduceDto.setOrderUserId(ticketOrder.getUserId());
|
||||||
|
if(Arrays.asList(1, 3).contains(order.getOrderType())){
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
}else {
|
||||||
|
reduceDto.setOrderNo(ticketOrder.getNo());
|
||||||
|
}
|
||||||
|
reduceDto.setPrice(money);
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DELIVERY_REWARD);
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//配送费结算
|
||||||
|
if(Arrays.asList(1, 3).contains(order.getOrderType()) && order.getDeliveryFee().compareTo(BigDecimal.ZERO) > 0){
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.WITHDRAW_ACCOUNT);
|
||||||
|
reduceDto.setUserId(ticketOrder.getRiderId());
|
||||||
|
reduceDto.setOrderUserId(ticketOrder.getUserId());
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
reduceDto.setPrice(order.getDeliveryFee());
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DELIVERY_FLOOR_FEE);
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询未完成订单,完成资金解冻
|
||||||
|
if(order != null && order.getOrderStatus() == 0){
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.DEFROST);
|
||||||
|
reduceDto.setOrderUserId(order.getUserId());
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.FREEZE_MONEY_THAW);
|
||||||
|
|
||||||
|
//按订单号资金解冻
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
|
||||||
LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
|
LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
|
||||||
.eq(ShopOrder::getTenantId, tenantId)
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
.eq(ShopOrder::getDeleted, 0)
|
.eq(ShopOrder::getDeleted, 0)
|
||||||
@@ -753,7 +1010,6 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
} else {
|
} else {
|
||||||
uw.eq(ShopOrder::getOrderNo, shopOrderNo);
|
uw.eq(ShopOrder::getOrderNo, shopOrderNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean updated = shopOrderService.update(uw);
|
boolean updated = shopOrderService.update(uw);
|
||||||
if (updated) {
|
if (updated) {
|
||||||
return;
|
return;
|
||||||
@@ -775,6 +1031,104 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新送水订单为已完成状态
|
||||||
|
* @param id 送水订单ID
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void updateShopOrderOrderStatusAfterTicketFinishedV2(Integer id) {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
if(id == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//1.找到关联水票的商城订单
|
||||||
|
GltTicketOrder ticketOrder = baseMapper.selectById(id);
|
||||||
|
if (ticketOrder == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShopOrder order = shopOrderService.getByOrderNo(ticketOrder.getOrderNo(), ticketOrder.getTenantId());
|
||||||
|
//2.配送提成(每桶0.1)
|
||||||
|
int qty = ticketOrder.getTotalNum() == null ? 0 : ticketOrder.getTotalNum();
|
||||||
|
if (qty > 0) {
|
||||||
|
BigDecimal money = RIDER_UNIT_COMMISSION
|
||||||
|
.multiply(BigDecimal.valueOf(qty))
|
||||||
|
.setScale(RIDER_COMMISSION_SCALE, RoundingMode.HALF_UP);
|
||||||
|
if (money.signum() > 0) {
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.WITHDRAW_ACCOUNT);
|
||||||
|
reduceDto.setUserId(ticketOrder.getRiderId());
|
||||||
|
reduceDto.setOrderUserId(ticketOrder.getUserId());
|
||||||
|
if(Arrays.asList(1, 3).contains(order.getOrderType())){
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
}else {
|
||||||
|
reduceDto.setOrderNo(ticketOrder.getNo());
|
||||||
|
}
|
||||||
|
reduceDto.setPrice(money);
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DELIVERY_INCOME);
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//3.配送奖励(按商品设置)
|
||||||
|
Integer goodsId = order.getFormId();
|
||||||
|
ShopGoods shopGood = shopGoodsMapper.selectById(goodsId);
|
||||||
|
if(shopGood != null && order != null){
|
||||||
|
Integer commissionType = shopGood.getCommissionType();
|
||||||
|
BigDecimal money = BigDecimal.ZERO;
|
||||||
|
if(commissionType == 10){ //按金额
|
||||||
|
money = shopGood.getDeliveryMoney();
|
||||||
|
}else { //按比率
|
||||||
|
money = order.getPayPrice().multiply(shopGood.getDeliveryMoney()).divide(BigDecimal.valueOf(100), 3, RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
if(money.compareTo(BigDecimal.ZERO) > 0){
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.WITHDRAW_ACCOUNT);
|
||||||
|
reduceDto.setUserId(ticketOrder.getRiderId());
|
||||||
|
reduceDto.setOrderUserId(ticketOrder.getUserId());
|
||||||
|
if(Arrays.asList(1, 3).contains(order.getOrderType())){
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
}else {
|
||||||
|
reduceDto.setOrderNo(ticketOrder.getNo());
|
||||||
|
}
|
||||||
|
reduceDto.setPrice(money);
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DELIVERY_REWARD);
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//4.配送费结算
|
||||||
|
if(Arrays.asList(1, 3).contains(order.getOrderType()) && order.getDeliveryFee().compareTo(BigDecimal.ZERO) > 0){
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.WITHDRAW_ACCOUNT);
|
||||||
|
reduceDto.setUserId(ticketOrder.getRiderId());
|
||||||
|
reduceDto.setOrderUserId(ticketOrder.getUserId());
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
reduceDto.setPrice(order.getDeliveryFee());
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DELIVERY_FLOOR_FEE);
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
//5.查询未完成订单,完成资金解冻
|
||||||
|
if(order != null && order.getOrderStatus() == 0){
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.DEFROST);
|
||||||
|
reduceDto.setOrderUserId(order.getUserId());
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.FREEZE_MONEY_THAW);
|
||||||
|
|
||||||
|
//按订单号资金解冻
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
//5.调整订单为已完成、配送完成状态
|
||||||
|
order.setOrderStatus(1);
|
||||||
|
order.setDeliveryStatus(40);
|
||||||
|
order.setUpdateTime(now);
|
||||||
|
shopOrderService.updateById(order);
|
||||||
|
}
|
||||||
|
|
||||||
private void settleRiderCommissionIfEligible(Integer ticketOrderId, Integer tenantId, boolean requirePhoto) {
|
private void settleRiderCommissionIfEligible(Integer ticketOrderId, Integer tenantId, boolean requirePhoto) {
|
||||||
if (ticketOrderId == null || tenantId == null) {
|
if (ticketOrderId == null || tenantId == null) {
|
||||||
return;
|
return;
|
||||||
@@ -845,13 +1199,13 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 送水订单提成:先入冻结金额 freeze_money(与分销订单佣金一致)
|
// 送水订单提成:直接入账可提现余额 money(配送员提成即时可用,无需冻结)
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
boolean updated = shopDealerUserService.update(
|
boolean updated = shopDealerUserService.update(
|
||||||
new LambdaUpdateWrapper<ShopDealerUser>()
|
new LambdaUpdateWrapper<ShopDealerUser>()
|
||||||
.eq(ShopDealerUser::getTenantId, tenantId)
|
.eq(ShopDealerUser::getTenantId, tenantId)
|
||||||
.eq(ShopDealerUser::getUserId, riderId)
|
.eq(ShopDealerUser::getUserId, riderId)
|
||||||
.setSql("freeze_money = IFNULL(freeze_money,0) + " + money.toPlainString())
|
.setSql("money = IFNULL(money,0) + " + money.toPlainString())
|
||||||
.setSql("total_money = IFNULL(total_money,0) + " + money.toPlainString())
|
.setSql("total_money = IFNULL(total_money,0) + " + money.toPlainString())
|
||||||
.set(ShopDealerUser::getUpdateTime, now)
|
.set(ShopDealerUser::getUpdateTime, now)
|
||||||
);
|
);
|
||||||
@@ -878,7 +1232,8 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
newDealerUser.setFreezeMoney(BigDecimal.ZERO);
|
newDealerUser.setFreezeMoney(BigDecimal.ZERO);
|
||||||
newDealerUser.setTotalMoney(BigDecimal.ZERO);
|
newDealerUser.setTotalMoney(BigDecimal.ZERO);
|
||||||
try {
|
try {
|
||||||
User sysUser = userMapper.selectByIdIgnoreTenant(riderId);
|
List<User> sysUsers = userMapper.selectByIdIgnoreTenant(riderId);
|
||||||
|
User sysUser = (sysUsers != null && !sysUsers.isEmpty()) ? sysUsers.get(0) : null;
|
||||||
if (sysUser != null) {
|
if (sysUser != null) {
|
||||||
newDealerUser.setRealName(sysUser.getRealName() != null ? sysUser.getRealName() : sysUser.getNickname());
|
newDealerUser.setRealName(sysUser.getRealName() != null ? sysUser.getRealName() : sysUser.getNickname());
|
||||||
newDealerUser.setMobile(sysUser.getPhone());
|
newDealerUser.setMobile(sysUser.getPhone());
|
||||||
@@ -895,7 +1250,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
new LambdaUpdateWrapper<ShopDealerUser>()
|
new LambdaUpdateWrapper<ShopDealerUser>()
|
||||||
.eq(ShopDealerUser::getTenantId, tenantId)
|
.eq(ShopDealerUser::getTenantId, tenantId)
|
||||||
.eq(ShopDealerUser::getUserId, riderId)
|
.eq(ShopDealerUser::getUserId, riderId)
|
||||||
.setSql("freeze_money = IFNULL(freeze_money,0) + " + money.toPlainString())
|
.setSql("money = IFNULL(money,0) + " + money.toPlainString())
|
||||||
.setSql("total_money = IFNULL(total_money,0) + " + money.toPlainString())
|
.setSql("total_money = IFNULL(total_money,0) + " + money.toPlainString())
|
||||||
.set(ShopDealerUser::getUpdateTime, now)
|
.set(ShopDealerUser::getUpdateTime, now)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.gxwebsoft.glt.service.impl;
|
package com.gxwebsoft.glt.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicket;
|
import com.gxwebsoft.glt.entity.GltUserTicket;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketLog;
|
import com.gxwebsoft.glt.entity.GltUserTicketLog;
|
||||||
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
|
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
|
||||||
@@ -7,13 +8,20 @@ import com.gxwebsoft.glt.mapper.GltUserTicketLogMapper;
|
|||||||
import com.gxwebsoft.glt.mapper.GltUserTicketMapper;
|
import com.gxwebsoft.glt.mapper.GltUserTicketMapper;
|
||||||
import com.gxwebsoft.glt.mapper.GltUserTicketReleaseMapper;
|
import com.gxwebsoft.glt.mapper.GltUserTicketReleaseMapper;
|
||||||
import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService;
|
import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import com.gxwebsoft.glt.service.GltUserTicketLogService;
|
||||||
|
import com.gxwebsoft.glt.service.GltUserTicketReleaseService;
|
||||||
|
import com.gxwebsoft.glt.service.GltUserTicketService;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 冻结水票自动释放实现:
|
* 冻结水票自动释放实现:
|
||||||
@@ -23,7 +31,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@AllArgsConstructor
|
||||||
public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoReleaseService {
|
public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoReleaseService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,6 +52,13 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel
|
|||||||
private final GltUserTicketMapper userTicketMapper;
|
private final GltUserTicketMapper userTicketMapper;
|
||||||
private final GltUserTicketLogMapper userTicketLogMapper;
|
private final GltUserTicketLogMapper userTicketLogMapper;
|
||||||
|
|
||||||
|
private GltUserTicketReleaseService gltUserTicketReleaseService;
|
||||||
|
|
||||||
|
private GltUserTicketService gltUserTicketService;
|
||||||
|
|
||||||
|
private GltUserTicketLogService gltUserTicketLogService;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public int releaseDue(LocalDateTime now, int batchSize) {
|
public int releaseDue(LocalDateTime now, int batchSize) {
|
||||||
@@ -71,12 +86,7 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
long userTicketIdLong = rel.getUserTicketId();
|
Integer userTicketId = rel.getUserTicketId();
|
||||||
if (userTicketIdLong > Integer.MAX_VALUE || userTicketIdLong < 1) {
|
|
||||||
markFailed(rel.getId(), now, "userTicketId超范围");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Integer userTicketId = (int) userTicketIdLong;
|
|
||||||
|
|
||||||
// 先释放冻结数量(条件更新,确保 frozen_qty >= qty)
|
// 先释放冻结数量(条件更新,确保 frozen_qty >= qty)
|
||||||
int updated = userTicketMapper.releaseFrozenQty(
|
int updated = userTicketMapper.releaseFrozenQty(
|
||||||
@@ -129,4 +139,96 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel
|
|||||||
releaseMapper.updateStatus(releaseId, RELEASE_STATUS_FAILED, now);
|
releaseMapper.updateStatus(releaseId, RELEASE_STATUS_FAILED, now);
|
||||||
log.warn("冻结水票释放标记失败 - releaseId={}, reason={}", releaseId, reason);
|
log.warn("冻结水票释放标记失败 - releaseId={}, reason={}", releaseId, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void releaseTask() {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
log.info("***执行水票释放任务***-执行时间:{}", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
List<GltUserTicketRelease> releaseList = releaseMapper.getThisMonthReleaseList(1000);
|
||||||
|
if(CollectionUtils.isEmpty(releaseList)){
|
||||||
|
log.info("***本轮任务无待释放水票数据***");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询用户水票信息
|
||||||
|
List<Integer> userTickIdList = releaseList.stream().map(GltUserTicketRelease::getUserTicketId).distinct().collect(Collectors.toList());
|
||||||
|
LambdaQueryWrapper<GltUserTicket> userTicketLambdaQueryWrapper = new LambdaQueryWrapper<GltUserTicket>().select(GltUserTicket::getId, GltUserTicket::getAvailableQty,
|
||||||
|
GltUserTicket::getFrozenQty, GltUserTicket::getReleasedQty).in(GltUserTicket::getId, userTickIdList);
|
||||||
|
List<GltUserTicket> userTicketList = userTicketMapper.selectList(userTicketLambdaQueryWrapper);
|
||||||
|
|
||||||
|
//创建修改用户水票集合、水票释放计划记录集合
|
||||||
|
List<GltUserTicket> updateUserTicketList = new ArrayList<>();
|
||||||
|
List<GltUserTicketLog> userTicketLogList = new ArrayList<>();
|
||||||
|
|
||||||
|
//遍历水票释放数据执行释放任务
|
||||||
|
releaseList.forEach(release ->{
|
||||||
|
//1.缺少水票ID、用户ID、租户ID记录错误记录
|
||||||
|
if(release.getUserTicketId() == null || release.getUserId() == null || release.getTenantId() == null){
|
||||||
|
release.setRemark("缺少userTicketId/userId/tenantId");
|
||||||
|
release.setStatus(2);
|
||||||
|
release.setUpdateTime(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
GltUserTicket userTicket = userTicketList.stream().filter(gltUserTicket -> release.getUserTicketId().equals(gltUserTicket.getId())).findFirst().orElse(null);
|
||||||
|
if(userTicket != null){
|
||||||
|
Integer releaseQty = release.getReleaseQty();
|
||||||
|
if(userTicket.getFrozenQty() > releaseQty){
|
||||||
|
//更改用户水票可用数量、冻结数量、已释放数量
|
||||||
|
userTicket.setAvailableQty(userTicket.getAvailableQty() + releaseQty);
|
||||||
|
userTicket.setFrozenQty(userTicket.getFrozenQty() - releaseQty);
|
||||||
|
userTicket.setReleasedQty(userTicket.getReleasedQty() + releaseQty);
|
||||||
|
userTicket.setUpdateTime(now);
|
||||||
|
updateUserTicketList.add(userTicket);
|
||||||
|
|
||||||
|
//记录水票释放计划为成功释放状态
|
||||||
|
release.setStatus(1);
|
||||||
|
release.setRemark("success");
|
||||||
|
release.setUpdateTime(now);
|
||||||
|
|
||||||
|
//生成水票释放记录
|
||||||
|
GltUserTicketLog ticketLog = new GltUserTicketLog();
|
||||||
|
ticketLog.setUserTicketId(release.getUserTicketId());
|
||||||
|
ticketLog.setChangeType(CHANGE_TYPE_RELEASE);
|
||||||
|
ticketLog.setChangeAvailable(releaseQty);
|
||||||
|
ticketLog.setChangeFrozen(-releaseQty);
|
||||||
|
ticketLog.setChangeUsed(0);
|
||||||
|
ticketLog.setAvailableAfter(userTicket.getAvailableQty());
|
||||||
|
ticketLog.setFrozenAfter(userTicket.getFrozenQty());
|
||||||
|
ticketLog.setUsedAfter(userTicket.getUsedQty());
|
||||||
|
ticketLog.setOrderId(Integer.valueOf(String.valueOf(release.getId())));
|
||||||
|
ticketLog.setUserId(release.getUserId());
|
||||||
|
ticketLog.setComments("冻结水票到期释放");
|
||||||
|
ticketLog.setTenantId(release.getTenantId());
|
||||||
|
ticketLog.setCreateTime(now);
|
||||||
|
ticketLog.setUpdateTime(now);
|
||||||
|
userTicketLogList.add(ticketLog);
|
||||||
|
}else {
|
||||||
|
release.setRemark("释放数量大于冻结数量,释放:" + releaseQty + ",冻结:" + userTicket.getFrozenQty());
|
||||||
|
release.setStatus(2);
|
||||||
|
release.setUpdateTime(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
release.setRemark("查询不到水票数据");
|
||||||
|
release.setStatus(2);
|
||||||
|
release.setUpdateTime(LocalDateTime.now());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(CollectionUtils.isNotEmpty(releaseList)){
|
||||||
|
gltUserTicketReleaseService.updateBatchById(releaseList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CollectionUtils.isNotEmpty(updateUserTicketList)){
|
||||||
|
gltUserTicketService.updateBatchById(updateUserTicketList);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CollectionUtils.isNotEmpty(userTicketLogList)){
|
||||||
|
gltUserTicketLogService.saveBatch(userTicketLogList);
|
||||||
|
}
|
||||||
|
|
||||||
|
long end = System.currentTimeMillis();
|
||||||
|
long costMs = end - start;
|
||||||
|
log.info("***执行水票释放任务完成*** 执行耗时:{} ms", costMs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import java.math.RoundingMode;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -48,7 +49,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
* <p>1) 送水套餐(formId in 水票模板 goodsId):订单号关联的水票产生了第一次送水订单,且该第一次送水订单状态=已完成(40) -> 解冻。</p>
|
* <p>1) 送水套餐(formId in 水票模板 goodsId):订单号关联的水票产生了第一次送水订单,且该第一次送水订单状态=已完成(40) -> 解冻。</p>
|
||||||
* <p>2) 非送水套餐(formId not in 水票模板 goodsId):订单已确认收货(orderStatus=1) -> 解冻。</p>
|
* <p>2) 非送水套餐(formId not in 水票模板 goodsId):订单已确认收货(orderStatus=1) -> 解冻。</p>
|
||||||
*
|
*
|
||||||
* <p>实现策略:以 ShopDealerCapital(flowType=10) 的“佣金明细”为解冻粒度,
|
* <p>实现策略:以 ShopDealerCapital(flowType=10) 的"佣金明细"为解冻粒度,
|
||||||
* 每条佣金明细对应生成一条 ShopDealerCapital(flowType=50) 作为幂等标记,并执行
|
* 每条佣金明细对应生成一条 ShopDealerCapital(flowType=50) 作为幂等标记,并执行
|
||||||
* ShopDealerUser.freezeMoney -> ShopDealerUser.money 的转移。</p>
|
* ShopDealerUser.freezeMoney -> ShopDealerUser.money 的转移。</p>
|
||||||
*/
|
*/
|
||||||
@@ -76,7 +77,7 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
if (rawRate == null || rawRate.signum() <= 0) {
|
if (rawRate == null || rawRate.signum() <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// 如果录入 >= 1,按“百分比”处理(1 => 1%)
|
// 如果录入 >= 1,按"百分比"处理(1 => 1%)
|
||||||
if (rawRate.compareTo(BigDecimal.ONE) >= 0) {
|
if (rawRate.compareTo(BigDecimal.ONE) >= 0) {
|
||||||
return rawRate.movePointLeft(2);
|
return rawRate.movePointLeft(2);
|
||||||
}
|
}
|
||||||
@@ -115,7 +116,7 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
|
|
||||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
|
|
||||||
@Scheduled(cron = "${dealer.commission.unfreeze10584.cron:0/20 * * * * ?}")
|
// @Scheduled(cron = "${dealer.commission.unfreeze10584.cron:0/50 * * * * ?}")
|
||||||
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!running.compareAndSet(false, true)) {
|
if (!running.compareAndSet(false, true)) {
|
||||||
@@ -124,46 +125,77 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// ========== 步骤1: 加载水票模板 ==========
|
||||||
Set<Integer> waterFormIds = loadWaterFormIds();
|
Set<Integer> waterFormIds = loadWaterFormIds();
|
||||||
|
log.info("【步骤1】加载水票模板 - tenantId={}, waterFormIds={}", TENANT_ID, waterFormIds);
|
||||||
if (waterFormIds.isEmpty()) {
|
if (waterFormIds.isEmpty()) {
|
||||||
// 送水/非送水的判断依赖模板 goodsId;拿不到会导致误判,宁可跳过本轮。
|
// 送水/非送水的判断依赖模板 goodsId;拿不到会导致误判,宁可跳过本轮。
|
||||||
log.warn("分销佣金解冻任务跳过:未找到水票模板 goodsId - tenantId={}", TENANT_ID);
|
log.warn("分销佣金解冻任务跳过:未找到水票模板 goodsId - tenantId={}", TENANT_ID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先按“最近确认收货”的订单扫描,避免总是卡在很早的历史订单上。
|
// ========== 步骤2: 扫描非送水订单(优先最新) ==========
|
||||||
Set<String> eligibleOrderNos = new HashSet<>();
|
Set<String> eligibleOrderNos = new HashSet<>();
|
||||||
eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(waterFormIds, true));
|
List<String> nonWaterOrders = findEligibleNonWaterOrderNos(waterFormIds, true);
|
||||||
eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds));
|
log.info("【步骤2】扫描非送水订单(最新优先)- tenantId={}, count={}, orderNos={}", TENANT_ID, nonWaterOrders.size(), nonWaterOrders.size() <= 20 ? nonWaterOrders : nonWaterOrders.subList(0, 20));
|
||||||
|
eligibleOrderNos.addAll(nonWaterOrders);
|
||||||
|
|
||||||
|
// ========== 步骤3: 扫描送水订单(第一条送水完成) ==========
|
||||||
|
Set<String> waterOrders = findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds);
|
||||||
|
log.info("【步骤3】扫描送水订单(第一条送水完成)- tenantId={}, count={}, orderNos={}", TENANT_ID, waterOrders.size(), waterOrders);
|
||||||
|
eligibleOrderNos.addAll(waterOrders);
|
||||||
|
|
||||||
if (eligibleOrderNos.isEmpty()) {
|
if (eligibleOrderNos.isEmpty()) {
|
||||||
|
log.info("【步骤4-9】无可处理订单,本轮结束 - tenantId={}", TENANT_ID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 订单太多时不打印完整列表
|
||||||
|
String orderNosSummary = eligibleOrderNos.size() <= 30 ? eligibleOrderNos.toString() : eligibleOrderNos.size() + " orders (too many to show)";
|
||||||
|
log.info("【步骤4】汇总待处理订单 - tenantId={}, totalCount={}, orderNos={}", TENANT_ID, eligibleOrderNos.size(), orderNosSummary);
|
||||||
|
|
||||||
// 配送奖励(与佣金解冻独立):按订单发放,幂等保证不会重复入账
|
// ========== 步骤5: 发放配送奖励 ==========
|
||||||
|
log.info("【步骤5】开始发放配送奖励 - tenantId={}, orderCount={}", TENANT_ID, eligibleOrderNos.size());
|
||||||
int rewarded = 0;
|
int rewarded = 0;
|
||||||
|
List<String> rewardedOrders = new ArrayList<>();
|
||||||
for (String orderNo : eligibleOrderNos) {
|
for (String orderNo : eligibleOrderNos) {
|
||||||
try {
|
try {
|
||||||
if (settleDeliveryRewardIfNeeded(orderNo)) {
|
if (settleDeliveryRewardIfNeeded(orderNo)) {
|
||||||
rewarded++;
|
rewarded++;
|
||||||
|
rewardedOrders.add(orderNo);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("发放配送奖励失败,将在下次任务重试 - tenantId={}, orderNo={}", TENANT_ID, orderNo, e);
|
log.error("发放配送奖励失败,将在下次任务重试 - tenantId={}, orderNo={}", TENANT_ID, orderNo, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.info("【步骤5】配送奖励发放完成 - tenantId={}, rewardedCount={}, rewardedOrders={}", TENANT_ID, rewarded, rewardedOrders);
|
||||||
|
|
||||||
|
// ========== 步骤6: 查询佣金明细 ==========
|
||||||
|
log.info("【步骤6】查询佣金明细(flowType=10)- tenantId={}, orderCount={}", TENANT_ID, eligibleOrderNos.size());
|
||||||
List<ShopDealerCapital> capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos);
|
List<ShopDealerCapital> capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos);
|
||||||
|
String capitalsSummary = capitals.size() <= 50
|
||||||
|
? capitals.stream().map(c -> "capitalId=" + c.getId() + ", orderNo=" + c.getOrderNo() + ", amount=" + c.getMoney()).toList().toString()
|
||||||
|
: capitals.size() + " capitals (too many to show)";
|
||||||
|
log.info("【步骤6】查询到佣金明细 - tenantId={}, count={}, capitals={}", TENANT_ID, capitals.size(), capitalsSummary);
|
||||||
|
|
||||||
if (capitals.isEmpty()) {
|
if (capitals.isEmpty()) {
|
||||||
// 若本轮没有取到佣金明细,回退再按“最早确认收货”的订单扫一轮,尽量覆盖历史遗留未解冻。
|
// ========== 步骤6.1: 兜底扫描历史订单 ==========
|
||||||
|
log.info("【步骤6.1】本轮未取到佣金明细,执行兜底扫描(最早确认收货) - tenantId={}", TENANT_ID);
|
||||||
eligibleOrderNos.clear();
|
eligibleOrderNos.clear();
|
||||||
eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(waterFormIds, false));
|
List<String> fallbackNonWater = findEligibleNonWaterOrderNos(waterFormIds, false);
|
||||||
eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds));
|
log.info("【步骤6.1】兜底-非送水订单 - tenantId={}, count={}", TENANT_ID, fallbackNonWater.size());
|
||||||
|
eligibleOrderNos.addAll(fallbackNonWater);
|
||||||
|
|
||||||
|
Set<String> fallbackWater = findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds);
|
||||||
|
log.info("【步骤6.1】兜底-送水订单 - tenantId={}, count={}", TENANT_ID, fallbackWater.size());
|
||||||
|
eligibleOrderNos.addAll(fallbackWater);
|
||||||
|
|
||||||
// 兜底扫描出来的订单也补发配送奖励(幂等)
|
// 兜底扫描出来的订单也补发配送奖励(幂等)
|
||||||
|
log.info("【步骤6.1】兜底扫描后补发配送奖励 - tenantId={}, orderCount={}", TENANT_ID, eligibleOrderNos.size());
|
||||||
for (String orderNo : eligibleOrderNos) {
|
for (String orderNo : eligibleOrderNos) {
|
||||||
try {
|
try {
|
||||||
if (settleDeliveryRewardIfNeeded(orderNo)) {
|
if (settleDeliveryRewardIfNeeded(orderNo)) {
|
||||||
rewarded++;
|
rewarded++;
|
||||||
|
rewardedOrders.add(orderNo + "(兜底)");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("发放配送奖励失败,将在下次任务重试 - tenantId={}, orderNo={}", TENANT_ID, orderNo, e);
|
log.error("发放配送奖励失败,将在下次任务重试 - tenantId={}, orderNo={}", TENANT_ID, orderNo, e);
|
||||||
@@ -171,32 +203,41 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos);
|
capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos);
|
||||||
|
log.info("【步骤6.1】兜底扫描到佣金明细 - tenantId={}, count={}", TENANT_ID, capitals.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (capitals.isEmpty()) {
|
if (capitals.isEmpty()) {
|
||||||
|
log.info("【步骤7-9】仍未查到佣金明细,本轮结束 - tenantId={}", TENANT_ID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== 步骤7: 执行佣金解冻 ==========
|
||||||
|
log.info("【步骤7】开始执行佣金解冻 - tenantId={}, totalCount={}", TENANT_ID, capitals.size());
|
||||||
int unfrozen = 0;
|
int unfrozen = 0;
|
||||||
|
List<String> unfrozenDetails = new ArrayList<>();
|
||||||
for (ShopDealerCapital cap : capitals) {
|
for (ShopDealerCapital cap : capitals) {
|
||||||
try {
|
try {
|
||||||
boolean ok = unfreezeOneCapitalIfNeeded(cap);
|
boolean ok = unfreezeOneCapitalIfNeeded(cap);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
unfrozen++;
|
unfrozen++;
|
||||||
|
unfrozenDetails.add("capitalId=" + cap.getId() + ", orderNo=" + cap.getOrderNo() + ", amount=" + cap.getMoney());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("解冻佣金失败,将在下次任务重试 - tenantId={}, capitalId={}, orderNo={}, userId={}",
|
log.error("解冻佣金失败,将在下次任务重试 - tenantId={}, capitalId={}, orderNo={}, userId={}",
|
||||||
TENANT_ID, cap != null ? cap.getId() : null, cap != null ? cap.getOrderNo() : null, cap != null ? cap.getUserId() : null, e);
|
TENANT_ID, cap != null ? cap.getId() : null, cap != null ? cap.getOrderNo() : null, cap != null ? cap.getUserId() : null, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.info("【步骤7】佣金解冻完成 - tenantId={}, unfrozenDetails={}", TENANT_ID, unfrozenDetails);
|
||||||
|
|
||||||
if (unfrozen > 0) {
|
// ========== 步骤8: 更新分销订单状态 ==========
|
||||||
log.info("分销佣金解冻完成 - tenantId={}, eligibleOrderNos={}, scannedCapitals={}, unfrozen={}",
|
log.info("【步骤8】检查并更新分销订单状态(isUnfreeze=1)- tenantId={}, unfrozenCount={}", TENANT_ID, unfrozen);
|
||||||
TENANT_ID, eligibleOrderNos.size(), capitals.size(), unfrozen);
|
|
||||||
}
|
// ========== 步骤9: 汇总报告 ==========
|
||||||
if (rewarded > 0) {
|
log.info("========================================");
|
||||||
log.info("配送奖励发放完成 - tenantId={}, eligibleOrderNos={}, rewarded={}", TENANT_ID, eligibleOrderNos.size(), rewarded);
|
log.info("【步骤9】本轮任务执行完毕 - tenantId={}", TENANT_ID);
|
||||||
}
|
log.info(" - 配送奖励: rewarded={}, orders={}", rewarded, rewardedOrders);
|
||||||
|
log.info(" - 佣金解冻: unfrozen={}, details={}", unfrozen, unfrozenDetails);
|
||||||
|
log.info("========================================");
|
||||||
} finally {
|
} finally {
|
||||||
running.set(false);
|
running.set(false);
|
||||||
}
|
}
|
||||||
@@ -204,8 +245,10 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
|
|
||||||
private boolean settleDeliveryRewardIfNeeded(String orderNo) {
|
private boolean settleDeliveryRewardIfNeeded(String orderNo) {
|
||||||
if (orderNo == null || orderNo.isBlank()) {
|
if (orderNo == null || orderNo.isBlank()) {
|
||||||
|
log.debug("【步骤5.X】配送奖励跳过:orderNo为空 - tenantId={}", TENANT_ID);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
log.debug("【步骤5.X】开始处理配送奖励 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
|
|
||||||
ShopOrder order = shopOrderService.getOne(
|
ShopOrder order = shopOrderService.getOne(
|
||||||
new LambdaQueryWrapper<ShopOrder>()
|
new LambdaQueryWrapper<ShopOrder>()
|
||||||
@@ -215,13 +258,16 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
.last("limit 1")
|
.last("limit 1")
|
||||||
);
|
);
|
||||||
if (order == null) {
|
if (order == null) {
|
||||||
|
log.debug("【步骤5.X】配送奖励跳过:订单不存在 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer riderId = order.getRiderId();
|
Integer riderId = order.getRiderId();
|
||||||
if (riderId == null || riderId <= 0) {
|
if (riderId == null || riderId <= 0) {
|
||||||
|
log.debug("【步骤5.X】配送奖励跳过:无配送员 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
log.debug("【步骤5.X】找到配送员 - tenantId={}, orderNo={}, riderId={}", TENANT_ID, orderNo, riderId);
|
||||||
|
|
||||||
// 快速幂等检查:已发放则跳过(事务内仍会二次校验避免并发重复)
|
// 快速幂等检查:已发放则跳过(事务内仍会二次校验避免并发重复)
|
||||||
boolean already = shopDealerCapitalService.count(
|
boolean already = shopDealerCapitalService.count(
|
||||||
@@ -231,11 +277,15 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
.eq(ShopDealerCapital::getOrderNo, orderNo)
|
.eq(ShopDealerCapital::getOrderNo, orderNo)
|
||||||
) > 0;
|
) > 0;
|
||||||
if (already) {
|
if (already) {
|
||||||
|
log.debug("【步骤5.X】配送奖励跳过:已发放(幂等)- tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
log.debug("【步骤5.X】进入事务 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
|
|
||||||
|
try {
|
||||||
return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
|
return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
log.debug("【步骤5.X】事务内开始 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
|
|
||||||
// 锁定配送员资金明细 marker,确保并发幂等
|
// 锁定配送员资金明细 marker,确保并发幂等
|
||||||
ShopDealerCapital existedMarker = shopDealerCapitalService.getOne(
|
ShopDealerCapital existedMarker = shopDealerCapitalService.getOne(
|
||||||
@@ -246,11 +296,13 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
.last("limit 1 for update")
|
.last("limit 1 for update")
|
||||||
);
|
);
|
||||||
if (existedMarker != null) {
|
if (existedMarker != null) {
|
||||||
|
log.debug("【步骤5.X】配送奖励跳过:事务内检测已存在 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer orderId = order.getOrderId();
|
Integer orderId = order.getOrderId();
|
||||||
if (orderId == null) {
|
if (orderId == null) {
|
||||||
|
log.debug("【步骤5.X】配送奖励跳过:orderId为空 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +312,7 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
.eq(ShopOrderGoods::getOrderId, orderId)
|
.eq(ShopOrderGoods::getOrderId, orderId)
|
||||||
);
|
);
|
||||||
if (orderGoodsList == null || orderGoodsList.isEmpty()) {
|
if (orderGoodsList == null || orderGoodsList.isEmpty()) {
|
||||||
|
log.debug("【步骤5.X】配送奖励跳过:订单商品为空 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,6 +322,7 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
.distinct()
|
.distinct()
|
||||||
.toList();
|
.toList();
|
||||||
if (goodsIds.isEmpty()) {
|
if (goodsIds.isEmpty()) {
|
||||||
|
log.debug("【步骤5.X】配送奖励跳过:商品ID列表为空 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,8 +361,10 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
|
|
||||||
reward = reward.setScale(2, RoundingMode.HALF_UP);
|
reward = reward.setScale(2, RoundingMode.HALF_UP);
|
||||||
if (reward.signum() <= 0) {
|
if (reward.signum() <= 0) {
|
||||||
|
log.debug("【步骤5.X】配送奖励跳过:计算金额为0 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
log.debug("【步骤5.X】计算配送奖励 - tenantId={}, orderNo={}, reward={}", TENANT_ID, orderNo, reward);
|
||||||
|
|
||||||
// 锁定/创建配送员分销账户
|
// 锁定/创建配送员分销账户
|
||||||
ShopDealerUser dealerUser = shopDealerUserService.getOne(
|
ShopDealerUser dealerUser = shopDealerUserService.getOne(
|
||||||
@@ -318,6 +374,7 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
.last("limit 1 for update")
|
.last("limit 1 for update")
|
||||||
);
|
);
|
||||||
if (dealerUser == null) {
|
if (dealerUser == null) {
|
||||||
|
log.info("【步骤5.X】创建配送员分销账户 - tenantId={}, orderNo={}, riderId={}", TENANT_ID, orderNo, riderId);
|
||||||
ShopDealerUser newDealerUser = new ShopDealerUser();
|
ShopDealerUser newDealerUser = new ShopDealerUser();
|
||||||
newDealerUser.setTenantId(TENANT_ID);
|
newDealerUser.setTenantId(TENANT_ID);
|
||||||
newDealerUser.setUserId(riderId);
|
newDealerUser.setUserId(riderId);
|
||||||
@@ -369,9 +426,13 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
cap.setUpdateTime(now);
|
cap.setUpdateTime(now);
|
||||||
shopDealerCapitalService.save(cap);
|
shopDealerCapitalService.save(cap);
|
||||||
|
|
||||||
log.info("配送奖励发放成功 - tenantId={}, orderNo={}, riderId={}, reward={}", TENANT_ID, orderNo, riderId, reward);
|
log.info("【步骤5.X】配送奖励发放成功 - tenantId={}, orderNo={}, riderId={}, reward={}", TENANT_ID, orderNo, riderId, reward);
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("【步骤5.X】配送奖励发放异常 - tenantId={}, orderNo={}, error={}", TENANT_ID, orderNo, e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Integer> loadWaterFormIds() {
|
private Set<Integer> loadWaterFormIds() {
|
||||||
@@ -408,10 +469,11 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> findEligibleWaterOrderNosByFirstFinishedTicketOrder(Set<Integer> waterFormIds) {
|
private Set<String> findEligibleWaterOrderNosByFirstFinishedTicketOrder(Set<Integer> waterFormIds) {
|
||||||
// 缓存减少 DB 往返:userTicketId -> 是否“第一次送水单已完成”
|
// 缓存减少 DB 往返:userTicketId -> 是否"第一次送水单已完成"
|
||||||
Map<Integer, Boolean> firstFinishedCache = new HashMap<>();
|
Map<Integer, Boolean> firstFinishedCache = new HashMap<>();
|
||||||
Map<Integer, String> userTicketOrderNoCache = new HashMap<>();
|
Map<Integer, String> userTicketOrderNoCache = new HashMap<>();
|
||||||
|
|
||||||
|
log.info("【步骤3.1】查询已完成的送水订单 - tenantId={}, limit={}", TENANT_ID, MAX_ELIGIBLE_TICKET_ORDERS_PER_RUN);
|
||||||
List<GltTicketOrder> finishedTicketOrders = gltTicketOrderService.list(
|
List<GltTicketOrder> finishedTicketOrders = gltTicketOrderService.list(
|
||||||
new LambdaQueryWrapper<GltTicketOrder>()
|
new LambdaQueryWrapper<GltTicketOrder>()
|
||||||
.eq(GltTicketOrder::getTenantId, TENANT_ID)
|
.eq(GltTicketOrder::getTenantId, TENANT_ID)
|
||||||
@@ -421,21 +483,27 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
.orderByDesc(GltTicketOrder::getId)
|
.orderByDesc(GltTicketOrder::getId)
|
||||||
.last("limit " + MAX_ELIGIBLE_TICKET_ORDERS_PER_RUN)
|
.last("limit " + MAX_ELIGIBLE_TICKET_ORDERS_PER_RUN)
|
||||||
);
|
);
|
||||||
|
log.info("【步骤3.1】查到已完成的送水订单数量 - tenantId={}, count={}", TENANT_ID, finishedTicketOrders.size());
|
||||||
|
|
||||||
Set<String> orderNos = new HashSet<>();
|
Set<String> orderNos = new HashSet<>();
|
||||||
|
int checked = 0, skippedNull = 0, skippedNotFirst = 0, skippedNoOrderNo = 0, skippedNotWater = 0;
|
||||||
for (GltTicketOrder ticketOrder : finishedTicketOrders) {
|
for (GltTicketOrder ticketOrder : finishedTicketOrders) {
|
||||||
Integer userTicketId = ticketOrder.getUserTicketId();
|
Integer userTicketId = ticketOrder.getUserTicketId();
|
||||||
|
checked++;
|
||||||
if (userTicketId == null) {
|
if (userTicketId == null) {
|
||||||
|
skippedNull++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean firstFinished = firstFinishedCache.computeIfAbsent(userTicketId, id -> isFirstTicketOrderFinished(id));
|
boolean firstFinished = firstFinishedCache.computeIfAbsent(userTicketId, id -> isFirstTicketOrderFinished(id));
|
||||||
if (!firstFinished) {
|
if (!firstFinished) {
|
||||||
|
skippedNotFirst++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String orderNo = userTicketOrderNoCache.computeIfAbsent(userTicketId, id -> findOrderNoByUserTicketId(id));
|
String orderNo = userTicketOrderNoCache.computeIfAbsent(userTicketId, id -> findOrderNoByUserTicketId(id));
|
||||||
if (orderNo == null || orderNo.isBlank()) {
|
if (orderNo == null || orderNo.isBlank()) {
|
||||||
|
skippedNoOrderNo++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,6 +517,7 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
.last("limit 1")
|
.last("limit 1")
|
||||||
);
|
);
|
||||||
if (shopOrder == null || shopOrder.getFormId() == null || !waterFormIds.contains(shopOrder.getFormId())) {
|
if (shopOrder == null || shopOrder.getFormId() == null || !waterFormIds.contains(shopOrder.getFormId())) {
|
||||||
|
skippedNotWater++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,6 +526,8 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.info("【步骤3.2】送水订单筛选完成 - tenantId={}, checked={}, skippedNull={}, skippedNotFirst={}, skippedNoOrderNo={}, skippedNotWater={}, matched={}, orderNos={}",
|
||||||
|
TENANT_ID, checked, skippedNull, skippedNotFirst, skippedNoOrderNo, skippedNotWater, orderNos.size(), orderNos);
|
||||||
return orderNos;
|
return orderNos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,7 +564,9 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
if (eligibleOrderNos == null || eligibleOrderNos.isEmpty()) {
|
if (eligibleOrderNos == null || eligibleOrderNos.isEmpty()) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
return shopDealerCapitalService.list(
|
// 先按 eligibleOrderNos 集合的顺序(最新订单优先)处理佣金
|
||||||
|
// 注意:MyBatis-Plus 的 in 查询会保持 in() 列表的顺序
|
||||||
|
List<ShopDealerCapital> capitals = shopDealerCapitalService.list(
|
||||||
new LambdaQueryWrapper<ShopDealerCapital>()
|
new LambdaQueryWrapper<ShopDealerCapital>()
|
||||||
.eq(ShopDealerCapital::getTenantId, TENANT_ID)
|
.eq(ShopDealerCapital::getTenantId, TENANT_ID)
|
||||||
.eq(ShopDealerCapital::getFlowType, 10)
|
.eq(ShopDealerCapital::getFlowType, 10)
|
||||||
@@ -502,6 +575,14 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
.orderByAsc(ShopDealerCapital::getId)
|
.orderByAsc(ShopDealerCapital::getId)
|
||||||
.last("limit " + MAX_CAPITALS_PER_RUN)
|
.last("limit " + MAX_CAPITALS_PER_RUN)
|
||||||
);
|
);
|
||||||
|
// 按 eligibleOrderNos 的顺序重新排序(最新订单优先)
|
||||||
|
List<String> orderList = List.copyOf(eligibleOrderNos);
|
||||||
|
capitals.sort((a, b) -> {
|
||||||
|
int idxA = orderList.indexOf(a.getOrderNo());
|
||||||
|
int idxB = orderList.indexOf(b.getOrderNo());
|
||||||
|
return Integer.compare(idxA, idxB);
|
||||||
|
});
|
||||||
|
return capitals;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean unfreezeOneCapitalIfNeeded(ShopDealerCapital cap) {
|
private boolean unfreezeOneCapitalIfNeeded(ShopDealerCapital cap) {
|
||||||
@@ -546,7 +627,7 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RR 隔离级别下,先锁 user 行,再用锁定读检查 marker,避免“读到旧快照”导致重复解冻。
|
// RR 隔离级别下,先锁 user 行,再用锁定读检查 marker,避免"读到旧快照"导致重复解冻。
|
||||||
ShopDealerCapital existedMarker = shopDealerCapitalService.getOne(
|
ShopDealerCapital existedMarker = shopDealerCapitalService.getOne(
|
||||||
new LambdaQueryWrapper<ShopDealerCapital>()
|
new LambdaQueryWrapper<ShopDealerCapital>()
|
||||||
.eq(ShopDealerCapital::getTenantId, TENANT_ID)
|
.eq(ShopDealerCapital::getTenantId, TENANT_ID)
|
||||||
@@ -590,7 +671,7 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
marker.setUpdateTime(now);
|
marker.setUpdateTime(now);
|
||||||
shopDealerCapitalService.save(marker);
|
shopDealerCapitalService.save(marker);
|
||||||
|
|
||||||
// 佣金全部解冻完成后,将分销订单状态置为“已解冻”(0)。
|
// 佣金全部解冻完成后,将分销订单状态置为"已解冻"(0)。
|
||||||
// 以当前任务生成的 flowType=50 marker 数量作为完成度判断,避免提前将订单置为已解冻。
|
// 以当前任务生成的 flowType=50 marker 数量作为完成度判断,避免提前将订单置为已解冻。
|
||||||
setDealerOrderUnfrozenIfCompleted(orderNo, now);
|
setDealerOrderUnfrozenIfCompleted(orderNo, now);
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,38 @@
|
|||||||
package com.gxwebsoft.glt.task;
|
package com.gxwebsoft.glt.task;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
|
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
|
||||||
import com.gxwebsoft.glt.entity.GltTicketTemplate;
|
import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum;
|
||||||
import com.gxwebsoft.glt.service.GltTicketTemplateService;
|
import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerCapital;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerOrder;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerReferee;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerSetting;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopGoods;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.common.system.mapper.UserMapper;
|
import com.gxwebsoft.common.system.mapper.UserMapper;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
import com.gxwebsoft.glt.entity.GltTicketTemplate;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerOrderService;
|
import com.gxwebsoft.glt.service.GltTicketTemplateService;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerRefereeService;
|
import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerSettingService;
|
import com.gxwebsoft.shop.entity.*;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
import com.gxwebsoft.shop.mapper.ShopDealerRefereeMapper;
|
||||||
import com.gxwebsoft.shop.service.ShopGoodsService;
|
import com.gxwebsoft.shop.mapper.ShopOrderMapper;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderService;
|
import com.gxwebsoft.shop.service.*;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
|
||||||
import com.gxwebsoft.shop.util.UpstreamUserFinder;
|
import com.gxwebsoft.shop.util.UpstreamUserFinder;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.gxwebsoft.shop.vo.ShopDealerRefereeVO;
|
||||||
|
import com.gxwebsoft.shop.vo.ShopOrderGoodsVO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户10584:分销订单结算任务
|
* 租户10584:分销订单结算任务
|
||||||
@@ -50,7 +47,6 @@ public class DealerOrderSettlement10584Task {
|
|||||||
|
|
||||||
private static final BigDecimal RATE_0_10 = new BigDecimal("0.10");
|
private static final BigDecimal RATE_0_10 = new BigDecimal("0.10");
|
||||||
private static final BigDecimal RATE_0_05 = new BigDecimal("0.05");
|
private static final BigDecimal RATE_0_05 = new BigDecimal("0.05");
|
||||||
private static final BigDecimal RATE_0_03 = new BigDecimal("0.03");
|
|
||||||
private static final BigDecimal RATE_0_02 = new BigDecimal("0.02");
|
private static final BigDecimal RATE_0_02 = new BigDecimal("0.02");
|
||||||
private static final BigDecimal RATE_0_01 = new BigDecimal("0.01");
|
private static final BigDecimal RATE_0_01 = new BigDecimal("0.01");
|
||||||
private static final BigDecimal TOTAL_DEALER_DIVIDEND_RATE = RATE_0_01;
|
private static final BigDecimal TOTAL_DEALER_DIVIDEND_RATE = RATE_0_01;
|
||||||
@@ -92,14 +88,23 @@ public class DealerOrderSettlement10584Task {
|
|||||||
@Resource
|
@Resource
|
||||||
private GltTicketTemplateService gltTicketTemplateService;
|
private GltTicketTemplateService gltTicketTemplateService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ShopOrderMapper shopOrderMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ShopDealerRefereeMapper shopDealerRefereeMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每10秒执行一次。
|
* 每10秒执行一次。
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "0/10 * * * * ?")
|
// @Scheduled(cron = "0/10 * * * * ?")
|
||||||
@IgnoreTenant("该定时任务仅处理租户10584,但需要显式按tenantId过滤,避免定时任务线程无租户上下文导致查询异常")
|
@IgnoreTenant("该定时任务仅处理租户10584,但需要显式按tenantId过滤,避免定时任务线程无租户上下文导致查询异常")
|
||||||
public void settleTenant10584Orders() {
|
public void settleTenant10584Orders() {
|
||||||
try {
|
try {
|
||||||
|
//获取水票模板对应的商品信息列表
|
||||||
Set<Integer> waterFormIds = loadWaterFormIds();
|
Set<Integer> waterFormIds = loadWaterFormIds();
|
||||||
|
|
||||||
|
//查询商品列表存在已支付未核销订单数据
|
||||||
List<ShopOrder> orders = findUnsettledPaidOrders(waterFormIds);
|
List<ShopOrder> orders = findUnsettledPaidOrders(waterFormIds);
|
||||||
if (orders.isEmpty()) {
|
if (orders.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@@ -108,7 +113,11 @@ public class DealerOrderSettlement10584Task {
|
|||||||
// Per-run caches to reduce DB chatter across orders.
|
// Per-run caches to reduce DB chatter across orders.
|
||||||
Map<Integer, Integer> level1ParentCache = new HashMap<>();
|
Map<Integer, Integer> level1ParentCache = new HashMap<>();
|
||||||
Map<Integer, Boolean> shopRoleCache = new HashMap<>();
|
Map<Integer, Boolean> shopRoleCache = new HashMap<>();
|
||||||
|
|
||||||
|
//获取系统设置分销等级
|
||||||
DealerBasicSetting dealerBasicSetting = findDealerBasicSetting();
|
DealerBasicSetting dealerBasicSetting = findDealerBasicSetting();
|
||||||
|
|
||||||
|
//获取分销员type=2第一个分销人作为平台总分红人
|
||||||
ShopDealerUser totalDealerUser = findTotalDealerUser();
|
ShopDealerUser totalDealerUser = findTotalDealerUser();
|
||||||
if (totalDealerUser == null || totalDealerUser.getUserId() == null) {
|
if (totalDealerUser == null || totalDealerUser.getUserId() == null) {
|
||||||
log.warn("未找到分红账号,订单仍可结算但不会发放分红 - tenantId={}", TENANT_ID);
|
log.warn("未找到分红账号,订单仍可结算但不会发放分红 - tenantId={}", TENANT_ID);
|
||||||
@@ -124,6 +133,7 @@ public class DealerOrderSettlement10584Task {
|
|||||||
try {
|
try {
|
||||||
transactionTemplate.executeWithoutResult(status -> {
|
transactionTemplate.executeWithoutResult(status -> {
|
||||||
// 先“认领”订单:并发/多实例下避免重复结算(update=0 表示被其他线程/实例处理)
|
// 先“认领”订单:并发/多实例下避免重复结算(update=0 表示被其他线程/实例处理)
|
||||||
|
//更新商品订单为已结算状态
|
||||||
if (!claimOrderToSettle(order.getOrderId(), waterFormIds)) {
|
if (!claimOrderToSettle(order.getOrderId(), waterFormIds)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -138,6 +148,88 @@ public class DealerOrderSettlement10584Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每10分钟执行一次。
|
||||||
|
*/
|
||||||
|
@Scheduled(cron = "0 0/10 * * * ?")
|
||||||
|
@IgnoreTenant("该定时任务仅处理租户10584,但需要显式按tenantId过滤,避免定时任务线程无租户上下文导致查询异常")
|
||||||
|
public void settleTenant10584OrdersV2() {
|
||||||
|
try {
|
||||||
|
//获取水票模板对应的商品信息列表
|
||||||
|
Set<Integer> waterFormIds = loadWaterFormIds();
|
||||||
|
|
||||||
|
//查询商品列表存在已支付未核销订单数据【isSettled = 0, payStatus= 1, orderStatus 不在列表(2, 3, 4, 5, 6, 7)】
|
||||||
|
List<ShopOrder> orders = findUnsettledPaidOrders(waterFormIds);
|
||||||
|
if (orders.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-run caches to reduce DB chatter across orders.
|
||||||
|
Map<Integer, Integer> level1ParentCache = new HashMap<>();
|
||||||
|
Map<Integer, Boolean> shopRoleCache = new HashMap<>();
|
||||||
|
|
||||||
|
//获取系统设置分销等级
|
||||||
|
DealerBasicSetting dealerBasicSetting = findDealerBasicSetting();
|
||||||
|
|
||||||
|
//获取分销员type=2第一个分销人作为平台总分红人
|
||||||
|
ShopDealerUser totalDealerUser = findTotalDealerUser();
|
||||||
|
if (totalDealerUser == null || totalDealerUser.getUserId() == null) {
|
||||||
|
log.warn("未找到分红账号,订单仍可结算但不会发放分红 - tenantId={}", TENANT_ID);
|
||||||
|
}
|
||||||
|
log.debug("租户{}分销设置 - level={}", TENANT_ID, dealerBasicSetting.level);
|
||||||
|
|
||||||
|
log.info("租户{}待结算订单数: {}, orderNos(sample)={}",
|
||||||
|
TENANT_ID,
|
||||||
|
orders.size(),
|
||||||
|
orders.stream().limit(10).map(ShopOrder::getOrderNo).toList());
|
||||||
|
|
||||||
|
for (ShopOrder order : orders) {
|
||||||
|
try {
|
||||||
|
transactionTemplate.executeWithoutResult(status -> {
|
||||||
|
// 先“认领”订单:并发/多实例下避免重复结算(update=0 表示被其他线程/实例处理)
|
||||||
|
//更新商品订单为已结算状态
|
||||||
|
if (!claimOrderToSettle(order.getOrderId(), waterFormIds)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
settleOneOrderV2(order, level1ParentCache, shopRoleCache, totalDealerUser, dealerBasicSetting.level);
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("订单结算失败,将回滚本订单并在下次任务重试 - orderId={}, orderNo={}", order.getOrderId(), order.getOrderNo(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("租户{}分销订单结算任务执行失败", TENANT_ID, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单分销金、分润金结算
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public void orderSettlement(String orderNo){
|
||||||
|
LambdaQueryWrapper<ShopOrder> orderLambdaQueryWrapper = new LambdaQueryWrapper<ShopOrder>().eq(ShopOrder::getOrderNo, orderNo).eq(ShopOrder::getIsSettled, 0).eq(ShopOrder::getPayStatus, 1);
|
||||||
|
ShopOrder order = shopOrderService.getOne(orderLambdaQueryWrapper);
|
||||||
|
if(order != null){
|
||||||
|
//获取系统设置分销等级
|
||||||
|
DealerBasicSetting dealerBasicSetting = findDealerBasicSetting();
|
||||||
|
|
||||||
|
//获取分销员type=2第一个分销人作为平台总分红人
|
||||||
|
ShopDealerUser totalDealerUser = findTotalDealerUser();
|
||||||
|
|
||||||
|
Map<Integer, Integer> level1ParentCache = new HashMap<>();
|
||||||
|
Map<Integer, Boolean> shopRoleCache = new HashMap<>();
|
||||||
|
|
||||||
|
transactionTemplate.executeWithoutResult(status -> {
|
||||||
|
// 先“认领”订单:并发/多实例下避免重复结算(update=0 表示被其他线程/实例处理)
|
||||||
|
//更新商品订单为已结算状态
|
||||||
|
if (!claimOrderToSettleV2(order.getOrderId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
settleOneOrderV2(order, level1ParentCache, shopRoleCache, totalDealerUser, dealerBasicSetting.level);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<ShopOrder> findUnsettledPaidOrders(Set<Integer> waterFormIds) {
|
private List<ShopOrder> findUnsettledPaidOrders(Set<Integer> waterFormIds) {
|
||||||
// 租户10584约定:
|
// 租户10584约定:
|
||||||
// - 普通订单:以发货为准(deliveryStatus=20)才结算;
|
// - 普通订单:以发货为准(deliveryStatus=20)才结算;
|
||||||
@@ -148,7 +240,7 @@ public class DealerOrderSettlement10584Task {
|
|||||||
.eq(ShopOrder::getPayStatus, true)
|
.eq(ShopOrder::getPayStatus, true)
|
||||||
.eq(ShopOrder::getIsSettled, 0)
|
.eq(ShopOrder::getIsSettled, 0)
|
||||||
// 退款/取消订单不结算,避免“退款后仍发放分红/分润/佣金”
|
// 退款/取消订单不结算,避免“退款后仍发放分红/分润/佣金”
|
||||||
.and(w -> w.notIn(ShopOrder::getOrderStatus, 2, 4, 5, 6, 7).or().isNull(ShopOrder::getOrderStatus));
|
.and(w -> w.notIn(ShopOrder::getOrderStatus, 2, 3, 4, 5, 6, 7).or().isNull(ShopOrder::getOrderStatus));
|
||||||
|
|
||||||
if (waterFormIds != null && !waterFormIds.isEmpty()) {
|
if (waterFormIds != null && !waterFormIds.isEmpty()) {
|
||||||
qw.and(w -> w.eq(ShopOrder::getDeliveryStatus, 20).or().in(ShopOrder::getFormId, waterFormIds));
|
qw.and(w -> w.eq(ShopOrder::getDeliveryStatus, 20).or().in(ShopOrder::getFormId, waterFormIds));
|
||||||
@@ -178,6 +270,17 @@ public class DealerOrderSettlement10584Task {
|
|||||||
return shopOrderService.update(uw);
|
return shopOrderService.update(uw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean claimOrderToSettleV2(Integer orderId) {
|
||||||
|
LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
|
||||||
|
.eq(ShopOrder::getOrderId, orderId)
|
||||||
|
.eq(ShopOrder::getTenantId, TENANT_ID)
|
||||||
|
.eq(ShopOrder::getIsSettled, 0)
|
||||||
|
// 二次防御:退款/取消订单不允许被“认领结算”
|
||||||
|
.and(w -> w.notIn(ShopOrder::getOrderStatus, 2, 3, 4, 5, 6, 7).or().isNull(ShopOrder::getOrderStatus));
|
||||||
|
uw.set(ShopOrder::getIsSettled, 1);
|
||||||
|
return shopOrderService.update(uw);
|
||||||
|
}
|
||||||
|
|
||||||
private Set<Integer> loadWaterFormIds() {
|
private Set<Integer> loadWaterFormIds() {
|
||||||
try {
|
try {
|
||||||
return gltTicketTemplateService.list(
|
return gltTicketTemplateService.list(
|
||||||
@@ -252,6 +355,45 @@ public class DealerOrderSettlement10584Task {
|
|||||||
log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), baseAmount);
|
log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), baseAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void settleOneOrderV2(ShopOrder order, Map<Integer, Integer> level1ParentCache, Map<Integer, Boolean> shopRoleCache,
|
||||||
|
ShopDealerUser totalDealerUser, int dealerLevel) {
|
||||||
|
if (order.getUserId() == null || order.getOrderNo() == null) {
|
||||||
|
throw new IllegalStateException("订单关键信息缺失,无法结算 - orderId=" + order.getOrderId());
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal totalPrice = order.getTotalPrice();
|
||||||
|
BigDecimal payPrice = order.getPayPrice();
|
||||||
|
BigDecimal rate = payPrice.divide(totalPrice, 2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
if(payPrice.compareTo(BigDecimal.ZERO) <= 0){
|
||||||
|
log.info("订单号:{},实付金额为0,无需执行分销逻辑" + order.getOrderNo());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询订单号订单所有已开启分销的商品分润信息
|
||||||
|
List<ShopOrderGoodsVO> orderGoodsVOList = shopOrderMapper.getOrderGoodsInfo(order.getOrderNo());
|
||||||
|
if(CollectionUtils.isNotEmpty(orderGoodsVOList)){
|
||||||
|
// 1) 直推/间推(直接增加冻结账户余额)
|
||||||
|
DealerRefereeCommissionV2 dealerRefereeCommission = settleDealerRefereeCommissionV2(order, rate, orderGoodsVOList, dealerLevel);
|
||||||
|
|
||||||
|
// 2) 门店分润上级:从下单用户开始逐级向上找,命中 ShopDealerUser.type=1 的最近两级(直推门店/间推门店)【只统计数据,不对分销账户进行处理,
|
||||||
|
// 已日结形式,统计分销记录表:shop_dealer_order 做对应一级二级管理津贴结算】
|
||||||
|
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommissionV2(order, rate, orderGoodsVOList, level1ParentCache, shopRoleCache);
|
||||||
|
|
||||||
|
// 3) 分红:固定比率,每个订单都分 TODO 总分红未开发,还按原逻辑走
|
||||||
|
int goodsQty = orderGoodsVOList.stream().mapToInt(ShopOrderGoodsVO::getTotalNum).sum();
|
||||||
|
TotalDealerCommission totalDealerCommission = settleTotalDealerCommissionV2(order, goodsQty, totalDealerUser);
|
||||||
|
|
||||||
|
// 4) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准)
|
||||||
|
createDealerOrderRecordV2(order, dealerRefereeCommission, shopRoleCommission, totalDealerCommission);
|
||||||
|
|
||||||
|
log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), payPrice);
|
||||||
|
}else {
|
||||||
|
log.error("订单号:{},未找到下单分销商品数据!", order.getOrderNo());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private DealerRefereeCommission settleDealerRefereeCommission(
|
private DealerRefereeCommission settleDealerRefereeCommission(
|
||||||
ShopOrder order,
|
ShopOrder order,
|
||||||
BigDecimal baseAmount,
|
BigDecimal baseAmount,
|
||||||
@@ -329,6 +471,95 @@ public class DealerOrderSettlement10584Task {
|
|||||||
return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney);
|
return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分销员分销霍金数据
|
||||||
|
* @param order
|
||||||
|
* @param orderGoodsVOList
|
||||||
|
* @param dealerLevel
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private DealerRefereeCommissionV2 settleDealerRefereeCommissionV2(ShopOrder order, BigDecimal rate, List<ShopOrderGoodsVO> orderGoodsVOList, int dealerLevel) {
|
||||||
|
Integer directDealerId = null;
|
||||||
|
Integer simpleDealerId = null;
|
||||||
|
AtomicReference<BigDecimal> directMoney = new AtomicReference<>(BigDecimal.ZERO);
|
||||||
|
AtomicReference<BigDecimal> simpleMoney = new AtomicReference<>(BigDecimal.ZERO);
|
||||||
|
|
||||||
|
|
||||||
|
ShopDealerRefereeVO dealerRefereeVO = shopDealerRefereeMapper.getDealerIdByUserId(order.getUserId());
|
||||||
|
if(dealerRefereeVO == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (dealerLevel == 1) {
|
||||||
|
Integer directUserId = dealerRefereeVO.getDirectUserId();
|
||||||
|
Integer directUserType = dealerRefereeVO.getDirectUserType();
|
||||||
|
if(directUserId != null && directUserType != null && directUserType == 0){
|
||||||
|
directDealerId = dealerRefereeVO.getDirectUserId();
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
Integer directUserId = dealerRefereeVO.getDirectUserId();
|
||||||
|
Integer directUserType = dealerRefereeVO.getDirectUserType();
|
||||||
|
Integer simpleUserId = dealerRefereeVO.getSimpleUserId();
|
||||||
|
Integer simpleUserType = dealerRefereeVO.getSimpleUserType();
|
||||||
|
if(directUserId != null && directUserType != null && directUserType == 0){
|
||||||
|
directDealerId = directUserId;
|
||||||
|
}
|
||||||
|
if(simpleUserId != null && simpleUserType != null && simpleUserType == 0){
|
||||||
|
simpleDealerId = simpleUserId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(directDealerId != null || simpleDealerId != null){
|
||||||
|
Integer finalDirectDealerId = directDealerId;
|
||||||
|
Integer finalSimpleDealerId = simpleDealerId;
|
||||||
|
orderGoodsVOList.forEach(orderGoodsVO -> {
|
||||||
|
//获取商品分润比例/金额
|
||||||
|
BigDecimal firstMoney = orderGoodsVO.getFirstMoney();
|
||||||
|
BigDecimal secondMoney = orderGoodsVO.getSecondMoney();
|
||||||
|
|
||||||
|
//按实付比例计算单项应参与分润金额
|
||||||
|
BigDecimal itemRatePrice = orderGoodsVO.getPrice().multiply(BigDecimal.valueOf(orderGoodsVO.getTotalNum())).multiply(rate);
|
||||||
|
|
||||||
|
//一级分销员存在(type = 0)且单项实付金额大于0及商品设置了一级分销比例/金额
|
||||||
|
if(finalDirectDealerId != null && itemRatePrice.compareTo(BigDecimal.ZERO) > 0 && firstMoney.compareTo(BigDecimal.ZERO) > 0){
|
||||||
|
BigDecimal one = calcMoneyByCommissionType(itemRatePrice, firstMoney, orderGoodsVO.getTotalNum(), 2, orderGoodsVO.getCommissionType());
|
||||||
|
directMoney.accumulateAndGet(one, BigDecimal::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
//一级分销员存在(type = 0)且单项实付金额大于0及商品设置了一级分销比例/金额
|
||||||
|
if(finalSimpleDealerId != null && itemRatePrice.compareTo(BigDecimal.ZERO) > 0 && secondMoney.compareTo(BigDecimal.ZERO) > 0 ){
|
||||||
|
BigDecimal two = calcMoneyByCommissionType(itemRatePrice, secondMoney, orderGoodsVO.getTotalNum(), 2, orderGoodsVO.getCommissionType());
|
||||||
|
simpleMoney.accumulateAndGet(two, BigDecimal::add);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//一级分销员账户增加冻结金额
|
||||||
|
if (directDealerId != null && directMoney.get().compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.FREEZE_ACCOUNT);
|
||||||
|
reduceDto.setUserId(directDealerId);
|
||||||
|
reduceDto.setOrderUserId(order.getUserId());
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
reduceDto.setPrice(directMoney.get());
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DISTRIBUTION_INCOME);
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
//二级分销员账户增加冻结金额
|
||||||
|
if (simpleDealerId != null && simpleMoney.get().compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.FREEZE_ACCOUNT);
|
||||||
|
reduceDto.setUserId(simpleDealerId);
|
||||||
|
reduceDto.setOrderUserId(order.getUserId());
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
reduceDto.setPrice(simpleMoney.get());
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DISTRIBUTION_INCOME);
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
}
|
||||||
|
return new DealerRefereeCommissionV2(directDealerId, directMoney.get(), simpleDealerId, simpleMoney.get());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private Integer getDealerRefereeId(Integer userId) {
|
private Integer getDealerRefereeId(Integer userId) {
|
||||||
return getDealerRefereeId(userId, 1);
|
return getDealerRefereeId(userId, 1);
|
||||||
}
|
}
|
||||||
@@ -412,6 +643,54 @@ public class DealerOrderSettlement10584Task {
|
|||||||
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
|
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ShopRoleCommission settleShopRoleRefereeCommissionV2(ShopOrder order, BigDecimal rate, List<ShopOrderGoodsVO> orderGoodsVOList, Map<Integer, Integer> level1ParentCache, Map<Integer, Boolean> shopRoleCache) {
|
||||||
|
List<Integer> shopRoleReferees = findFirstTwoShopRoleReferees(order.getUserId(), level1ParentCache, shopRoleCache);
|
||||||
|
log.info("门店分润命中结果(type=1门店角色取前两级) - orderNo={}, buyerUserId={}, shopRoleReferees={}",
|
||||||
|
order.getOrderNo(), order.getUserId(), shopRoleReferees);
|
||||||
|
if (shopRoleReferees.isEmpty()) {
|
||||||
|
return ShopRoleCommission.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CollectionUtils.isNotEmpty(shopRoleReferees)){
|
||||||
|
Integer storeDirectUserId;
|
||||||
|
Integer storeSimpleUserId = null;
|
||||||
|
AtomicReference<BigDecimal> storeDirectMoney = new AtomicReference<>(BigDecimal.ZERO);
|
||||||
|
AtomicReference<BigDecimal> storeSimpleMoney = new AtomicReference<>(BigDecimal.ZERO);
|
||||||
|
|
||||||
|
if(shopRoleReferees.size() == 1){
|
||||||
|
storeDirectUserId = shopRoleReferees.get(0);
|
||||||
|
}else {
|
||||||
|
storeDirectUserId = shopRoleReferees.get(0);
|
||||||
|
storeSimpleUserId = shopRoleReferees.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Integer finalStoreDirectUserId = storeDirectUserId;
|
||||||
|
Integer finalStoreSimpleUserId = storeSimpleUserId;
|
||||||
|
orderGoodsVOList.forEach(orderGoodsVO ->{
|
||||||
|
//获取商品对应服务商管理费分润比例/金额
|
||||||
|
BigDecimal firstMoney = orderGoodsVO.getFirstDividend();
|
||||||
|
BigDecimal secondMoney = orderGoodsVO.getSecondDividend();
|
||||||
|
|
||||||
|
//按实付比例计算单项应参与分润金额
|
||||||
|
BigDecimal itemRatePrice = orderGoodsVO.getPrice().multiply(BigDecimal.valueOf(orderGoodsVO.getTotalNum())).multiply(rate);
|
||||||
|
|
||||||
|
if(finalStoreDirectUserId != null && itemRatePrice.compareTo(BigDecimal.ZERO) > 0){
|
||||||
|
BigDecimal one = calcMoneyByCommissionType(itemRatePrice, firstMoney, orderGoodsVO.getTotalNum(), 2, orderGoodsVO.getCommissionType());
|
||||||
|
storeDirectMoney.accumulateAndGet(one, BigDecimal::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(finalStoreSimpleUserId != null && itemRatePrice.compareTo(BigDecimal.ZERO) > 0){
|
||||||
|
BigDecimal two = calcMoneyByCommissionType(itemRatePrice, secondMoney, orderGoodsVO.getTotalNum(), 2, orderGoodsVO.getCommissionType());
|
||||||
|
storeSimpleMoney.accumulateAndGet(two, BigDecimal::add);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return new ShopRoleCommission(storeDirectUserId, storeDirectMoney.get(), storeSimpleUserId, storeSimpleMoney.get());
|
||||||
|
}else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private TotalDealerCommission settleTotalDealerCommission(
|
private TotalDealerCommission settleTotalDealerCommission(
|
||||||
ShopOrder order,
|
ShopOrder order,
|
||||||
BigDecimal baseAmount,
|
BigDecimal baseAmount,
|
||||||
@@ -438,6 +717,32 @@ public class DealerOrderSettlement10584Task {
|
|||||||
return new TotalDealerCommission(totalDealerUser.getUserId(), money);
|
return new TotalDealerCommission(totalDealerUser.getUserId(), money);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TotalDealerCommission settleTotalDealerCommissionV2(ShopOrder order, int goodsQty, ShopDealerUser totalDealerUser) {
|
||||||
|
if (totalDealerUser == null || totalDealerUser.getUserId() == null) {
|
||||||
|
return TotalDealerCommission.empty();
|
||||||
|
}
|
||||||
|
BigDecimal rate = safePositive(totalDealerUser.getRate());
|
||||||
|
if (rate.signum() <= 0) {
|
||||||
|
rate = TOTAL_DEALER_DIVIDEND_RATE;
|
||||||
|
}
|
||||||
|
BigDecimal money = calcMoneyByCommissionType(order.getPayPrice(), rate, goodsQty, DIVIDEND_SCALE, 20);
|
||||||
|
|
||||||
|
//一级分销员账户增加冻结金额
|
||||||
|
if (money.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
|
||||||
|
reduceDto.setTypeEnum(ShopDealerTypeEnum.FREEZE_ACCOUNT);
|
||||||
|
reduceDto.setUserId(totalDealerUser.getUserId());
|
||||||
|
reduceDto.setOrderUserId(order.getUserId());
|
||||||
|
reduceDto.setOrderNo(order.getOrderNo());
|
||||||
|
reduceDto.setPrice(money);
|
||||||
|
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DIVIDEND_INCOME);
|
||||||
|
shopDealerUserService.reduceBalance(reduceDto);
|
||||||
|
|
||||||
|
return new TotalDealerCommission(totalDealerUser.getUserId(), money);
|
||||||
|
}
|
||||||
|
return TotalDealerCommission.empty();
|
||||||
|
}
|
||||||
|
|
||||||
private ShopDealerUser findTotalDealerUser() {
|
private ShopDealerUser findTotalDealerUser() {
|
||||||
return shopDealerUserService.getOne(
|
return shopDealerUserService.getOne(
|
||||||
new LambdaQueryWrapper<ShopDealerUser>()
|
new LambdaQueryWrapper<ShopDealerUser>()
|
||||||
@@ -620,7 +925,8 @@ public class DealerOrderSettlement10584Task {
|
|||||||
newDealerUser.setTotalMoney(BigDecimal.ZERO);
|
newDealerUser.setTotalMoney(BigDecimal.ZERO);
|
||||||
// 尽量补齐基础信息,避免表字段 NOT NULL 导致插入失败(插入失败会让门店分佣“找到了人但入不了账”)。
|
// 尽量补齐基础信息,避免表字段 NOT NULL 导致插入失败(插入失败会让门店分佣“找到了人但入不了账”)。
|
||||||
try {
|
try {
|
||||||
User sysUser = userMapper.selectByIdIgnoreTenant(dealerUserId);
|
List<User> sysUsers = userMapper.selectByIdIgnoreTenant(dealerUserId);
|
||||||
|
User sysUser = (sysUsers != null && !sysUsers.isEmpty()) ? sysUsers.get(0) : null;
|
||||||
if (sysUser != null) {
|
if (sysUser != null) {
|
||||||
newDealerUser.setRealName(sysUser.getRealName() != null ? sysUser.getRealName() : sysUser.getNickname());
|
newDealerUser.setRealName(sysUser.getRealName() != null ? sysUser.getRealName() : sysUser.getNickname());
|
||||||
newDealerUser.setMobile(sysUser.getPhone());
|
newDealerUser.setMobile(sysUser.getPhone());
|
||||||
@@ -755,6 +1061,92 @@ public class DealerOrderSettlement10584Task {
|
|||||||
order.getOrderNo(), dealerOrder.getFirstUserId(), dealerOrder.getSecondUserId(), dealerOrder.getFirstDividendUser(), dealerOrder.getSecondDividendUser());
|
order.getOrderNo(), dealerOrder.getFirstUserId(), dealerOrder.getSecondUserId(), dealerOrder.getFirstDividendUser(), dealerOrder.getSecondDividendUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录订单分销业务
|
||||||
|
* @param order 商品订单
|
||||||
|
* @param dealerRefereeCommission 一级、二级分销员分销数据
|
||||||
|
* @param shopRoleCommission 门店/服务商一级、二级管理津贴数据
|
||||||
|
*/
|
||||||
|
private void createDealerOrderRecordV2(ShopOrder order, DealerRefereeCommissionV2 dealerRefereeCommission, ShopRoleCommission shopRoleCommission, TotalDealerCommission totalDealerCommission) {
|
||||||
|
// 幂等:同一订单只写一条(依赖 order_no + tenant_id 作为业务唯一)
|
||||||
|
ShopDealerOrder existed = shopDealerOrderService.getOne(
|
||||||
|
new LambdaQueryWrapper<ShopDealerOrder>()
|
||||||
|
.eq(ShopDealerOrder::getTenantId, TENANT_ID)
|
||||||
|
.eq(ShopDealerOrder::getOrderNo, order.getOrderNo())
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
if (existed != null) {
|
||||||
|
// 允许“补发”门店分润时回填分润字段,避免订单已结算但分润字段一直为空,影响排查/对账。
|
||||||
|
LambdaUpdateWrapper<ShopDealerOrder> uw = new LambdaUpdateWrapper<ShopDealerOrder>()
|
||||||
|
.eq(ShopDealerOrder::getTenantId, TENANT_ID)
|
||||||
|
.eq(ShopDealerOrder::getOrderNo, order.getOrderNo());
|
||||||
|
boolean needUpdate = false;
|
||||||
|
if (shopRoleCommission != null && shopRoleCommission.storeDirectUserId != null) {
|
||||||
|
Integer existedUser = existed.getFirstDividendUser();
|
||||||
|
boolean needSetUser = existedUser == null;
|
||||||
|
boolean needSetMoney = existed.getFirstDividend() == null || existed.getFirstDividend().signum() == 0;
|
||||||
|
if (needSetUser) {
|
||||||
|
uw.set(ShopDealerOrder::getFirstDividendUser, shopRoleCommission.storeDirectUserId);
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
boolean sameUser = existedUser == null || Objects.equals(existedUser, shopRoleCommission.storeDirectUserId);
|
||||||
|
if (sameUser && needSetMoney && shopRoleCommission.storeDirectMoney != null && shopRoleCommission.storeDirectMoney.signum() > 0) {
|
||||||
|
uw.set(ShopDealerOrder::getFirstDividend, shopRoleCommission.storeDirectMoney);
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shopRoleCommission != null && shopRoleCommission.storeSimpleUserId != null) {
|
||||||
|
Integer existedUser = existed.getSecondDividendUser();
|
||||||
|
boolean needSetUser = existedUser == null;
|
||||||
|
boolean needSetMoney = existed.getSecondDividend() == null || existed.getSecondDividend().signum() == 0;
|
||||||
|
if (needSetUser) {
|
||||||
|
uw.set(ShopDealerOrder::getSecondDividendUser, shopRoleCommission.storeSimpleUserId);
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
boolean sameUser = existedUser == null || Objects.equals(existedUser, shopRoleCommission.storeSimpleUserId);
|
||||||
|
if (sameUser && needSetMoney && shopRoleCommission.storeSimpleMoney != null && shopRoleCommission.storeSimpleMoney.signum() > 0) {
|
||||||
|
uw.set(ShopDealerOrder::getSecondDividend, shopRoleCommission.storeSimpleMoney);
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (needUpdate) {
|
||||||
|
shopDealerOrderService.update(uw);
|
||||||
|
log.info("ShopDealerOrder已存在,回填门店分润字段 - orderNo={}, firstDividendUser={}, secondDividendUser={}",
|
||||||
|
order.getOrderNo(), shopRoleCommission.storeDirectUserId, shopRoleCommission.storeSimpleUserId);
|
||||||
|
} else {
|
||||||
|
log.info("ShopDealerOrder已存在,跳过写入 - orderNo={}", order.getOrderNo());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}else {
|
||||||
|
|
||||||
|
ShopDealerOrder dealerOrder = new ShopDealerOrder();
|
||||||
|
dealerOrder.setUserId(order.getUserId()); // 买家用户ID
|
||||||
|
dealerOrder.setOrderNo(order.getOrderNo());
|
||||||
|
dealerOrder.setOrderPrice(order.getTotalPrice());
|
||||||
|
dealerOrder.setPayPrice(order.getPayPrice());
|
||||||
|
|
||||||
|
//一级、二级分销员分销佣金统计
|
||||||
|
dealerOrder.setFirstUserId(dealerRefereeCommission != null ? dealerRefereeCommission.directDealerId : null);
|
||||||
|
dealerOrder.setFirstMoney(dealerRefereeCommission != null ? dealerRefereeCommission.directMoney : BigDecimal.ZERO);
|
||||||
|
dealerOrder.setSecondUserId(dealerRefereeCommission != null ? dealerRefereeCommission.simpleDealerId : null);
|
||||||
|
dealerOrder.setSecondMoney(dealerRefereeCommission != null ? dealerRefereeCommission.simpleMoney : BigDecimal.ZERO);
|
||||||
|
|
||||||
|
//门店(角色shop)两级分润单独落字段(详细以 ShopDealerCapital 为准)
|
||||||
|
dealerOrder.setFirstDividendUser(shopRoleCommission != null ? shopRoleCommission.storeDirectUserId : null);
|
||||||
|
dealerOrder.setFirstDividend(shopRoleCommission != null ? shopRoleCommission.storeDirectMoney : BigDecimal.ZERO);
|
||||||
|
dealerOrder.setSecondDividendUser(shopRoleCommission != null ? shopRoleCommission.storeSimpleUserId : null);
|
||||||
|
dealerOrder.setSecondDividend(shopRoleCommission != null ? shopRoleCommission.storeSimpleMoney : BigDecimal.ZERO);
|
||||||
|
|
||||||
|
dealerOrder.setIsSettled(1);
|
||||||
|
dealerOrder.setSettleTime(LocalDateTime.now());
|
||||||
|
dealerOrder.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
|
||||||
|
dealerOrder.setTenantId(TENANT_ID);
|
||||||
|
dealerOrder.setComments(buildCommissionTraceCommentV2(dealerRefereeCommission, shopRoleCommission, totalDealerCommission));
|
||||||
|
|
||||||
|
shopDealerOrderService.save(dealerOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String buildCommissionTraceComment(
|
private String buildCommissionTraceComment(
|
||||||
DealerRefereeCommission dealerRefereeCommission,
|
DealerRefereeCommission dealerRefereeCommission,
|
||||||
ShopRoleCommission shopRoleCommission,
|
ShopRoleCommission shopRoleCommission,
|
||||||
@@ -769,6 +1161,30 @@ public class DealerOrderSettlement10584Task {
|
|||||||
+ ",totalDealer=" + totalDealerCommission.userId + ":" + totalDealerCommission.money;
|
+ ",totalDealer=" + totalDealerCommission.userId + ":" + totalDealerCommission.money;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String buildCommissionTraceCommentV2(
|
||||||
|
DealerRefereeCommissionV2 dealerRefereeCommission,
|
||||||
|
ShopRoleCommission shopRoleCommission,
|
||||||
|
TotalDealerCommission totalDealerCommission
|
||||||
|
) {
|
||||||
|
// 轻量“过程”留痕,方便排查;详细分佣以 ShopDealerCapital 为准。
|
||||||
|
Integer direct = dealerRefereeCommission != null ? dealerRefereeCommission.directDealerId : null;
|
||||||
|
BigDecimal directMoney = dealerRefereeCommission != null ? dealerRefereeCommission.directMoney : BigDecimal.ZERO;
|
||||||
|
Integer simpleDealerId = dealerRefereeCommission != null ? dealerRefereeCommission.simpleDealerId : null;
|
||||||
|
BigDecimal simpleMoney = dealerRefereeCommission != null ? dealerRefereeCommission.simpleMoney : BigDecimal.ZERO;
|
||||||
|
Integer storeDirectUserId = shopRoleCommission != null ? shopRoleCommission.storeDirectUserId : null;
|
||||||
|
BigDecimal storeDirectMoney = shopRoleCommission != null ? shopRoleCommission.storeDirectMoney : BigDecimal.ZERO;
|
||||||
|
Integer storeSimpleUserId = shopRoleCommission != null ? shopRoleCommission.storeSimpleUserId : null;
|
||||||
|
BigDecimal storeSimpleMoney = shopRoleCommission != null ? shopRoleCommission.storeSimpleMoney : BigDecimal.ZERO;
|
||||||
|
Integer userId = totalDealerCommission != null ? totalDealerCommission.userId : null;
|
||||||
|
BigDecimal money = totalDealerCommission != null ? totalDealerCommission.money : BigDecimal.ZERO;
|
||||||
|
|
||||||
|
return "direct=" + direct + ":" + directMoney
|
||||||
|
+ ",simple=" + simpleDealerId + ":" + simpleMoney
|
||||||
|
+ ",dividend1=" + storeDirectUserId + ":" + storeDirectMoney
|
||||||
|
+ ",dividend2=" + storeSimpleUserId + ":" + storeSimpleMoney
|
||||||
|
+ ",totalDealer=" + userId + ":" + money;
|
||||||
|
}
|
||||||
|
|
||||||
private BigDecimal getOrderBaseAmount(ShopOrder order) {
|
private BigDecimal getOrderBaseAmount(ShopOrder order) {
|
||||||
if (order == null) {
|
if (order == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -961,6 +1377,25 @@ public class DealerOrderSettlement10584Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class DealerRefereeCommissionV2 {
|
||||||
|
private final Integer directDealerId;
|
||||||
|
private final BigDecimal directMoney;
|
||||||
|
private final Integer simpleDealerId;
|
||||||
|
private final BigDecimal simpleMoney;
|
||||||
|
|
||||||
|
private DealerRefereeCommissionV2(
|
||||||
|
Integer directDealerId,
|
||||||
|
BigDecimal directMoney,
|
||||||
|
Integer simpleDealerId,
|
||||||
|
BigDecimal simpleMoney
|
||||||
|
) {
|
||||||
|
this.directDealerId = directDealerId;
|
||||||
|
this.directMoney = directMoney != null ? directMoney : BigDecimal.ZERO;
|
||||||
|
this.simpleDealerId = simpleDealerId;
|
||||||
|
this.simpleMoney = simpleMoney != null ? simpleMoney : BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class ShopRoleCommission {
|
private static class ShopRoleCommission {
|
||||||
private final Integer storeDirectUserId;
|
private final Integer storeDirectUserId;
|
||||||
private final BigDecimal storeDirectMoney;
|
private final BigDecimal storeDirectMoney;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class GltTicketIssue10584Task {
|
|||||||
|
|
||||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
|
|
||||||
@Scheduled(cron = "${glt.ticket.issue10584.cron:0/15 * * * * ?}")
|
// @Scheduled(cron = "${glt.ticket.issue10584.cron:0/15 * * * * ?}")
|
||||||
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!running.compareAndSet(false, true)) {
|
if (!running.compareAndSet(false, true)) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class GltTicketOrderAutoConfirm10584Task {
|
|||||||
|
|
||||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
|
|
||||||
@Scheduled(cron = "${glt.ticket-order.auto-confirm10584.cron:0/33 * * * * ?}")
|
// @Scheduled(cron = "${glt.ticket-order.auto-confirm10584.cron:0/33 * * * * ?}")
|
||||||
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!running.compareAndSet(false, true)) {
|
if (!running.compareAndSet(false, true)) {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class GltTicketOrderAutoDispatch10584Task {
|
|||||||
@Value("${glt.ticket.dispatch10584.batchSize:50}")
|
@Value("${glt.ticket.dispatch10584.batchSize:50}")
|
||||||
private int batchSize;
|
private int batchSize;
|
||||||
|
|
||||||
@Scheduled(cron = "${glt.ticket.dispatch10584.cron:0/20 * * * * ?}")
|
// @Scheduled(cron = "${glt.ticket.dispatch10584.cron:0/20 * * * * ?}")
|
||||||
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!running.compareAndSet(false, true)) {
|
if (!running.compareAndSet(false, true)) {
|
||||||
|
|||||||
@@ -4,18 +4,19 @@ import com.gxwebsoft.common.core.annotation.IgnoreTenant;
|
|||||||
import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService;
|
import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 冻结水票自动释放任务:
|
* 冻结水票自动释放任务:每月1日凌晨1-3点每2分钟执行一次任务
|
||||||
* - 扫描 glt_user_ticket_release 中到期且待释放(status=0)的记录
|
* - 扫描 glt_user_ticket_release 中本月到期且待释放(status=0)的记录
|
||||||
* - 释放成功:frozen -> available,并将 release.status 置为 1
|
* - 释放成功:
|
||||||
|
* 1.修改水票释放任务为已释放状态 glt_user_ticket_release
|
||||||
|
* 2.修改个人水票中:可用数量、冻结数量、已释放数量 glt_user_ticket
|
||||||
|
* 3.同步生产水票释放记录数据 glt_user_ticket_log
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -25,25 +26,17 @@ public class GltUserTicketAutoReleaseTask {
|
|||||||
|
|
||||||
private final GltUserTicketAutoReleaseService autoReleaseService;
|
private final GltUserTicketAutoReleaseService autoReleaseService;
|
||||||
|
|
||||||
@Value("${glt.ticket.auto-release.batch-size:200}")
|
|
||||||
private int batchSize;
|
|
||||||
|
|
||||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
|
|
||||||
@Scheduled(cron = "${glt.ticket.auto-release.cron:0 */10 * * * ?}")
|
@Scheduled(cron = "${glt.ticket.auto-release.cron:0 */2 1-3 1 * ?}")
|
||||||
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;释放记录自带 tenantId,更新时会校验 tenantId")
|
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;释放记录自带 tenantId,更新时会校验 tenantId")
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!running.compareAndSet(false, true)) {
|
if (!running.compareAndSet(false, true)) {
|
||||||
log.warn("冻结水票自动释放任务仍在执行中,本轮跳过");
|
log.warn("冻结水票自动释放任务仍在执行中,本轮跳过");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LocalDateTime now = LocalDateTime.now();
|
autoReleaseService.releaseTask();
|
||||||
int released = autoReleaseService.releaseDue(now, Math.max(batchSize, 1));
|
|
||||||
if (released > 0) {
|
|
||||||
log.info("冻结水票自动释放完成 - released={}, now={}", released, now);
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
running.set(false);
|
running.set(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ public class PaymentConstants {
|
|||||||
*/
|
*/
|
||||||
public static class Environment {
|
public static class Environment {
|
||||||
/** 开发环境 */
|
/** 开发环境 */
|
||||||
public static final String DEV = "dev";
|
public static final String DEV = "local";
|
||||||
/** 测试环境 */
|
/** 测试环境 */
|
||||||
public static final String TEST = "test";
|
public static final String TEST = "test";
|
||||||
/** 生产环境 */
|
/** 生产环境 */
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ public class WxPayConfigService {
|
|||||||
log.info("从数据库获取支付配置成功,租户ID: {},将缓存配置", tenantId);
|
log.info("从数据库获取支付配置成功,租户ID: {},将缓存配置", tenantId);
|
||||||
|
|
||||||
// 开发环境下,如果apiclientKey为空,设置默认值
|
// 开发环境下,如果apiclientKey为空,设置默认值
|
||||||
if ("dev".equals(activeProfile) &&
|
if ("local".equals(activeProfile) &&
|
||||||
(payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty())) {
|
(payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty())) {
|
||||||
log.warn("开发环境:数据库配置中apiclientKey为空,使用默认值,租户ID: {}", tenantId);
|
log.warn("开发环境:数据库配置中apiclientKey为空,使用默认值,租户ID: {}", tenantId);
|
||||||
payment.setApiclientKey("apiclient_key.pem");
|
payment.setApiclientKey("apiclient_key.pem");
|
||||||
@@ -167,7 +167,7 @@ public class WxPayConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 数据库也没有配置
|
// 数据库也没有配置
|
||||||
if (!"dev".equals(activeProfile)) {
|
if (!"local".equals(activeProfile)) {
|
||||||
throw PaymentException.systemError("微信支付配置未找到,租户ID: " + tenantId + ",请检查数据库配置", null);
|
throw PaymentException.systemError("微信支付配置未找到,租户ID: " + tenantId + ",请检查数据库配置", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ public class WxPayConfigService {
|
|||||||
* 获取证书文件路径
|
* 获取证书文件路径
|
||||||
*/
|
*/
|
||||||
private String getCertificatePath(Integer tenantId, Payment payment) throws PaymentException {
|
private String getCertificatePath(Integer tenantId, Payment payment) throws PaymentException {
|
||||||
if ("dev".equals(activeProfile)) {
|
if ("local".equals(activeProfile)) {
|
||||||
return getDevCertificatePath(tenantId);
|
return getDevCertificatePath(tenantId);
|
||||||
} else {
|
} else {
|
||||||
return getProdCertificatePath(payment);
|
return getProdCertificatePath(payment);
|
||||||
@@ -237,7 +237,7 @@ public class WxPayConfigService {
|
|||||||
*/
|
*/
|
||||||
private Config createWxPayConfig(Payment payment, String certificatePath) throws PaymentException {
|
private Config createWxPayConfig(Payment payment, String certificatePath) throws PaymentException {
|
||||||
try {
|
try {
|
||||||
if ("dev".equals(activeProfile) && payment == null) {
|
if ("local".equals(activeProfile) && payment == null) {
|
||||||
// 开发环境测试配置
|
// 开发环境测试配置
|
||||||
return createDevTestConfig(certificatePath);
|
return createDevTestConfig(certificatePath);
|
||||||
} else if (payment != null) {
|
} else if (payment != null) {
|
||||||
@@ -343,7 +343,7 @@ public class WxPayConfigService {
|
|||||||
// 开发环境下,如果apiclientKey为空,给一个警告但不抛异常
|
// 开发环境下,如果apiclientKey为空,给一个警告但不抛异常
|
||||||
// 生产环境必须有apiclientKey
|
// 生产环境必须有apiclientKey
|
||||||
if (payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty()) {
|
if (payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty()) {
|
||||||
if ("dev".equals(activeProfile)) {
|
if ("local".equals(activeProfile)) {
|
||||||
log.warn("开发环境:证书文件名(apiclientKey)未配置,将使用默认值");
|
log.warn("开发环境:证书文件名(apiclientKey)未配置,将使用默认值");
|
||||||
} else {
|
} else {
|
||||||
throw PaymentException.systemError("证书文件名(apiclientKey)未配置", null);
|
throw PaymentException.systemError("证书文件名(apiclientKey)未配置", null);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class WxPayConstants {
|
|||||||
public static final int AMOUNT_MULTIPLIER = 100;
|
public static final int AMOUNT_MULTIPLIER = 100;
|
||||||
|
|
||||||
/** 开发环境标识 */
|
/** 开发环境标识 */
|
||||||
public static final String PROFILE_DEV = "dev";
|
public static final String PROFILE_DEV = "local";
|
||||||
/** 生产环境标识 */
|
/** 生产环境标识 */
|
||||||
public static final String PROFILE_PROD = "prod";
|
public static final String PROFILE_PROD = "prod";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package com.gxwebsoft.shop.controller;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.shop.service.ShopActiveImageService;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopActiveImage;
|
||||||
|
import com.gxwebsoft.shop.param.ShopActiveImageParam;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推广码底图控制器
|
||||||
|
*
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-04-27 18:02:18
|
||||||
|
*/
|
||||||
|
@Tag(name = "推广码底图管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/shop/shop-active-image")
|
||||||
|
public class ShopActiveImageController extends BaseController {
|
||||||
|
@Resource
|
||||||
|
private ShopActiveImageService shopActiveImageService;
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopActiveImage:list')")
|
||||||
|
@Operation(summary = "分页查询推广码底图")
|
||||||
|
@GetMapping("/page")
|
||||||
|
public ApiResult<PageResult<ShopActiveImage>> page(ShopActiveImageParam param) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopActiveImageService.pageRel(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopActiveImage:list')")
|
||||||
|
@Operation(summary = "查询全部推广码底图")
|
||||||
|
@GetMapping()
|
||||||
|
public ApiResult<List<ShopActiveImage>> list(ShopActiveImageParam param) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopActiveImageService.listRel(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopActiveImage:list')")
|
||||||
|
@Operation(summary = "根据id查询推广码底图")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ApiResult<ShopActiveImage> get(@PathVariable("id") Integer id) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopActiveImageService.getByIdRel(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopActiveImage:save')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "添加推广码底图")
|
||||||
|
@PostMapping()
|
||||||
|
public ApiResult<?> save(@RequestBody ShopActiveImage shopActiveImage) {
|
||||||
|
shopActiveImage.setCreator(String.valueOf(getLoginUserId()));
|
||||||
|
shopActiveImage.setCreateTime(LocalDateTime.now());
|
||||||
|
if (shopActiveImageService.save(shopActiveImage)) {
|
||||||
|
return success("添加成功");
|
||||||
|
}
|
||||||
|
return fail("添加失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopActiveImage:update')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "修改推广码底图")
|
||||||
|
@PutMapping()
|
||||||
|
public ApiResult<?> update(@RequestBody ShopActiveImage shopActiveImage) {
|
||||||
|
shopActiveImage.setUpdater(String.valueOf(getLoginUserId()));
|
||||||
|
shopActiveImage.setUpdateTime(LocalDateTime.now());
|
||||||
|
if (shopActiveImageService.updateById(shopActiveImage)) {
|
||||||
|
return success("修改成功");
|
||||||
|
}
|
||||||
|
return fail("修改失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopActiveImage:remove')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "删除推广码底图")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||||||
|
if (shopActiveImageService.removeById(id)) {
|
||||||
|
return success("删除成功");
|
||||||
|
}
|
||||||
|
return fail("删除失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopActiveImage:save')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "批量添加推广码底图")
|
||||||
|
@PostMapping("/batch")
|
||||||
|
public ApiResult<?> saveBatch(@RequestBody List<ShopActiveImage> list) {
|
||||||
|
if (shopActiveImageService.saveBatch(list)) {
|
||||||
|
return success("添加成功");
|
||||||
|
}
|
||||||
|
return fail("添加失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopActiveImage:update')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "批量修改推广码底图")
|
||||||
|
@PutMapping("/batch")
|
||||||
|
public ApiResult<?> removeBatch(@RequestBody BatchParam<ShopActiveImage> batchParam) {
|
||||||
|
if (batchParam.update(shopActiveImageService, "id")) {
|
||||||
|
return success("修改成功");
|
||||||
|
}
|
||||||
|
return fail("修改失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopActiveImage:remove')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "批量删除推广码底图")
|
||||||
|
@DeleteMapping("/batch")
|
||||||
|
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
||||||
|
if (shopActiveImageService.removeByIds(ids)) {
|
||||||
|
return success("删除成功");
|
||||||
|
}
|
||||||
|
return fail("删除失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
package com.gxwebsoft.shop.controller;
|
package com.gxwebsoft.shop.controller;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import com.gxwebsoft.shop.dto.ShopDealerCapitalWaterDto;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerCapital;
|
import com.gxwebsoft.shop.entity.ShopDealerCapital;
|
||||||
import com.gxwebsoft.shop.param.ShopDealerCapitalParam;
|
import com.gxwebsoft.shop.param.ShopDealerCapitalParam;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.shop.vo.ShopDealerCapitalWaterVO;
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springdoc.api.annotations.ParameterObject;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -34,11 +36,19 @@ public class ShopDealerCapitalController extends BaseController {
|
|||||||
@PreAuthorize("hasAuthority('shop:shopDealerCapital:list')")
|
@PreAuthorize("hasAuthority('shop:shopDealerCapital:list')")
|
||||||
@Operation(summary = "分页查询分销商资金明细表")
|
@Operation(summary = "分页查询分销商资金明细表")
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
public ApiResult<PageResult<ShopDealerCapital>> page(ShopDealerCapitalParam param) {
|
public ApiResult<PageResult<ShopDealerCapital>> page(@ParameterObject ShopDealerCapitalParam param) {
|
||||||
// 使用关联查询
|
// 使用关联查询
|
||||||
return success(shopDealerCapitalService.pageRel(param));
|
return success(shopDealerCapitalService.pageRel(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopDealerCapital:list')")
|
||||||
|
@Operation(summary = "分页查询分销商个人流水")
|
||||||
|
@GetMapping("/myCapitalWater")
|
||||||
|
public ApiResult<PageResult<ShopDealerCapitalWaterVO>> myCapitalWater(ShopDealerCapitalWaterDto waterDto) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopDealerCapitalService.myCapitalWater(waterDto));
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopDealerCapital:list')")
|
@PreAuthorize("hasAuthority('shop:shopDealerCapital:list')")
|
||||||
@Operation(summary = "查询全部分销商资金明细表")
|
@Operation(summary = "查询全部分销商资金明细表")
|
||||||
@GetMapping()
|
@GetMapping()
|
||||||
|
|||||||
@@ -3,19 +3,18 @@ package com.gxwebsoft.shop.controller;
|
|||||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
|
||||||
import com.gxwebsoft.shop.service.ShopDealerOrderService;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerOrder;
|
|
||||||
import com.gxwebsoft.shop.param.ShopDealerOrderParam;
|
|
||||||
import com.gxwebsoft.shop.param.ShopDealerOrderImportParam;
|
|
||||||
import com.gxwebsoft.common.core.utils.JSONUtil;
|
import com.gxwebsoft.common.core.utils.JSONUtil;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopDealerOrder;
|
||||||
|
import com.gxwebsoft.shop.param.ShopDealerOrderImportParam;
|
||||||
|
import com.gxwebsoft.shop.param.ShopDealerOrderParam;
|
||||||
|
import com.gxwebsoft.shop.service.ShopDealerOrderService;
|
||||||
|
import com.gxwebsoft.shop.task.OrderSettlementTask;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@@ -24,7 +23,9 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分销商订单记录表控制器
|
* 分销商订单记录表控制器
|
||||||
@@ -39,6 +40,9 @@ public class ShopDealerOrderController extends BaseController {
|
|||||||
@Resource
|
@Resource
|
||||||
private ShopDealerOrderService shopDealerOrderService;
|
private ShopDealerOrderService shopDealerOrderService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private OrderSettlementTask orderSettlementTask;
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopDealerOrder:list')")
|
@PreAuthorize("hasAuthority('shop:shopDealerOrder:list')")
|
||||||
@Operation(summary = "分页查询分销商订单记录表")
|
@Operation(summary = "分页查询分销商订单记录表")
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@@ -63,6 +67,13 @@ public class ShopDealerOrderController extends BaseController {
|
|||||||
return success(shopDealerOrderService.getByIdRel(id));
|
return success(shopDealerOrderService.getByIdRel(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopDealerOrder:list')")
|
||||||
|
@Operation(summary = "查询个人今日收益")
|
||||||
|
@GetMapping("/todayRevenue")
|
||||||
|
public ApiResult<BigDecimal> todayRevenue() {
|
||||||
|
return success(shopDealerOrderService.todayRevenue());
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopDealerOrder:save')")
|
@PreAuthorize("hasAuthority('shop:shopDealerOrder:save')")
|
||||||
@OperationLog
|
@OperationLog
|
||||||
@Operation(summary = "添加分销商订单记录表")
|
@Operation(summary = "添加分销商订单记录表")
|
||||||
@@ -168,4 +179,29 @@ public class ShopDealerOrderController extends BaseController {
|
|||||||
}
|
}
|
||||||
return fail("导入失败", null);
|
return fail("导入失败", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopDealerOrder:update')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "手动触发单条订单佣金解冻")
|
||||||
|
// @PostMapping("/unfreeze")
|
||||||
|
public ApiResult<String> manualUnfreeze(@RequestBody Map<String, Object> body) {
|
||||||
|
String orderNo = (String) body.get("orderNo");
|
||||||
|
if (orderNo == null || orderNo.isBlank()) {
|
||||||
|
return fail("订单编号不能为空", null);
|
||||||
|
}
|
||||||
|
Integer tenantId = getTenantId();
|
||||||
|
try {
|
||||||
|
String detail = shopDealerOrderService.manualUnfreeze(orderNo, tenantId);
|
||||||
|
return success("解冻执行完成", detail);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return fail(e.getMessage(),null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "店、服务商结算任务:每天一点,每10分钟执行一次结算任务")
|
||||||
|
@PutMapping("/teamSettlement")
|
||||||
|
public ApiResult<?> teamSettlement() {
|
||||||
|
orderSettlementTask.teamSettlement();
|
||||||
|
return success("success");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
package com.gxwebsoft.shop.controller;
|
package com.gxwebsoft.shop.controller;
|
||||||
|
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
|
||||||
import com.gxwebsoft.common.core.Constants;
|
import com.gxwebsoft.common.core.Constants;
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
import com.gxwebsoft.common.core.exception.BusinessException;
|
import com.gxwebsoft.common.core.exception.BusinessException;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerRefereeService;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerReferee;
|
import com.gxwebsoft.shop.entity.ShopDealerReferee;
|
||||||
import com.gxwebsoft.shop.param.ShopDealerRefereeParam;
|
import com.gxwebsoft.shop.param.ShopDealerRefereeParam;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.shop.service.ShopDealerRefereeService;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springdoc.api.annotations.ParameterObject;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@@ -41,6 +41,13 @@ public class ShopDealerRefereeController extends BaseController {
|
|||||||
return success(shopDealerRefereeService.pageRel(param));
|
return success(shopDealerRefereeService.pageRel(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopDealerReferee:list')")
|
||||||
|
@Operation(summary = "分页查询分销商推荐关系表")
|
||||||
|
@GetMapping("/appPage")
|
||||||
|
public ApiResult<PageResult<ShopDealerReferee>> appPage(@ParameterObject ShopDealerRefereeParam param) {
|
||||||
|
return success(shopDealerRefereeService.appPage(param));
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopDealerReferee:list')")
|
@PreAuthorize("hasAuthority('shop:shopDealerReferee:list')")
|
||||||
@Operation(summary = "查询全部分销商推荐关系表")
|
@Operation(summary = "查询全部分销商推荐关系表")
|
||||||
@GetMapping()
|
@GetMapping()
|
||||||
|
|||||||
@@ -4,18 +4,19 @@ import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
|||||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.gxwebsoft.common.core.utils.JSONUtil;
|
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
|
||||||
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
|
||||||
import com.gxwebsoft.shop.param.ShopDealerUserParam;
|
|
||||||
import com.gxwebsoft.shop.param.ShopDealerUserImportParam;
|
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import com.gxwebsoft.common.core.utils.JSONUtil;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import com.gxwebsoft.shop.dto.ShopDealerRefundDto;
|
||||||
|
import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
||||||
|
import com.gxwebsoft.shop.param.ShopDealerUserImportParam;
|
||||||
|
import com.gxwebsoft.shop.param.ShopDealerUserParam;
|
||||||
|
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@@ -78,6 +79,13 @@ public class ShopDealerUserController extends BaseController {
|
|||||||
return fail("添加失败");
|
return fail("添加失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopDealerUser:update')")
|
||||||
|
@Operation(summary = "开启/关闭分销商用户核销权限")
|
||||||
|
@PutMapping("/verifyEnable")
|
||||||
|
public ApiResult<Boolean> verifyEnable(@RequestParam("id") Integer id) {
|
||||||
|
return success(shopDealerUserService.verifyEnable(id));
|
||||||
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopDealerUser:update')")
|
@PreAuthorize("hasAuthority('shop:shopDealerUser:update')")
|
||||||
@Operation(summary = "修改分销商用户记录表")
|
@Operation(summary = "修改分销商用户记录表")
|
||||||
@PutMapping()
|
@PutMapping()
|
||||||
@@ -178,4 +186,18 @@ public class ShopDealerUserController extends BaseController {
|
|||||||
return fail("导入失败", null);
|
return fail("导入失败", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "分销结算")
|
||||||
|
@PostMapping("/dealerCapital")
|
||||||
|
@Deprecated
|
||||||
|
public ApiResult<?> dealerCapital(@RequestBody ShopDealerUserReduceDto reduceDto) {
|
||||||
|
return success(shopDealerUserService.reduceBalance(reduceDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "分销退单")
|
||||||
|
@PostMapping("/refundOrder")
|
||||||
|
@Deprecated
|
||||||
|
public ApiResult<?> refundOrder(@RequestBody ShopDealerRefundDto refundDto) {
|
||||||
|
return success(shopDealerUserService.refundOrder(refundDto));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.gxwebsoft.shop.controller;
|
|||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.system.redis.OrderNoUtils;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerWithdrawService;
|
import com.gxwebsoft.shop.service.ShopDealerWithdrawService;
|
||||||
@@ -43,6 +44,8 @@ public class ShopDealerWithdrawController extends BaseController {
|
|||||||
private ShopDealerUserService shopDealerUserService;
|
private ShopDealerUserService shopDealerUserService;
|
||||||
@Resource
|
@Resource
|
||||||
private WxTransferService wxTransferService;
|
private WxTransferService wxTransferService;
|
||||||
|
@Resource
|
||||||
|
private OrderNoUtils orderNoUtils;
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:list')")
|
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:list')")
|
||||||
@Operation(summary = "分页查询分销商提现明细表")
|
@Operation(summary = "分页查询分销商提现明细表")
|
||||||
@@ -89,6 +92,8 @@ public class ShopDealerWithdrawController extends BaseController {
|
|||||||
return fail("tenantId为空,无法发起提现");
|
return fail("tenantId为空,无法发起提现");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String orderNo = orderNoUtils.generate("WD");
|
||||||
|
shopDealerWithdraw.setOrderNo(orderNo);
|
||||||
shopDealerWithdraw.setTenantId(tenantId);
|
shopDealerWithdraw.setTenantId(tenantId);
|
||||||
shopDealerWithdraw.setUserId(loginUser.getUserId());
|
shopDealerWithdraw.setUserId(loginUser.getUserId());
|
||||||
|
|
||||||
@@ -251,7 +256,8 @@ public class ShopDealerWithdrawController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 使用提现记录ID构造单号,保持幂等;微信要求 5-32 且仅字母/数字
|
// 使用提现记录ID构造单号,保持幂等;微信要求 5-32 且仅字母/数字
|
||||||
String outBillNo = String.format("WD%03d", db.getId());
|
String outBillNo = db.getOrderNo();
|
||||||
|
|
||||||
String remark = "分销商提现";
|
String remark = "分销商提现";
|
||||||
String userName = db.getRealName();
|
String userName = db.getRealName();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package com.gxwebsoft.shop.controller;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopFlashSaleActivity;
|
||||||
|
import com.gxwebsoft.shop.param.ShopFlashSaleActivityParam;
|
||||||
|
import com.gxwebsoft.shop.service.ShopFlashSaleActivityService;
|
||||||
|
import com.gxwebsoft.shop.vo.ShopFlashSaleActivityVO;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameters;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 秒杀活动控制器
|
||||||
|
*
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-04-22 17:18:18
|
||||||
|
*/
|
||||||
|
@Tag(name = "秒杀活动管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/shop/shop-flash-sale-activity")
|
||||||
|
public class ShopFlashSaleActivityController extends BaseController {
|
||||||
|
@Resource
|
||||||
|
private ShopFlashSaleActivityService shopFlashSaleActivityService;
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:list')")
|
||||||
|
@Operation(summary = "后台分页查询秒杀活动")
|
||||||
|
@GetMapping("/page")
|
||||||
|
public ApiResult<PageResult<ShopFlashSaleActivityVO>> page(ShopFlashSaleActivityParam param) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopFlashSaleActivityService.pageRel(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "个人获取秒杀活动数据")
|
||||||
|
@GetMapping("/getMyActive")
|
||||||
|
public ApiResult<List<ShopFlashSaleActivityVO>> getMyActive(@RequestParam("tenantId") Integer tenantId, Integer popFlag) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopFlashSaleActivityService.getMyActive(tenantId, popFlag));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:list')")
|
||||||
|
@Operation(summary = "查询全部秒杀活动")
|
||||||
|
@GetMapping()
|
||||||
|
public ApiResult<List<ShopFlashSaleActivity>> list(ShopFlashSaleActivityParam param) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopFlashSaleActivityService.listRel(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:list')")
|
||||||
|
@Operation(summary = "根据id查询秒杀活动")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ApiResult<ShopFlashSaleActivityVO> get(@PathVariable("id") Integer id) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopFlashSaleActivityService.getInfoById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:save')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "添加秒杀活动")
|
||||||
|
@PostMapping()
|
||||||
|
public ApiResult<?> save(@RequestBody ShopFlashSaleActivity shopFlashSaleActivity) {
|
||||||
|
// 记录当前登录用户id
|
||||||
|
User loginUser = getLoginUser();
|
||||||
|
if (loginUser != null) {
|
||||||
|
shopFlashSaleActivity.setCreator(loginUser.getUserId().toString());
|
||||||
|
}
|
||||||
|
if (shopFlashSaleActivityService.save(shopFlashSaleActivity)) {
|
||||||
|
return success("添加成功");
|
||||||
|
}
|
||||||
|
return fail("添加失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:update')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "修改秒杀活动")
|
||||||
|
@PutMapping()
|
||||||
|
public ApiResult<?> update(@RequestBody ShopFlashSaleActivity shopFlashSaleActivity) {
|
||||||
|
shopFlashSaleActivity.setUpdater(String.valueOf(getLoginUserId()));
|
||||||
|
if (shopFlashSaleActivityService.updateById(shopFlashSaleActivity)) {
|
||||||
|
return success("修改成功");
|
||||||
|
}
|
||||||
|
return fail("修改失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "开启/关闭秒杀活动状态")
|
||||||
|
@PutMapping("/updateStatus")
|
||||||
|
public ApiResult<?> updateStatus(@RequestParam("id") Integer id) {
|
||||||
|
return success(shopFlashSaleActivityService.updateStatus(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "修改秒杀活动排序")
|
||||||
|
@PutMapping("/updateSortNumber")
|
||||||
|
@Parameters({
|
||||||
|
@Parameter(name = "id", description = "活动ID", required = true, example = "1"),
|
||||||
|
@Parameter(name = "sortNumber", description = "排序", required = true, example = "2")
|
||||||
|
})
|
||||||
|
public ApiResult<?> updateSortNumber(@RequestParam("id") Integer id, @RequestParam("sortNumber") Integer sortNumber) {
|
||||||
|
return success(shopFlashSaleActivityService.updateSortNumber(id, sortNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:remove')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "删除秒杀活动")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||||||
|
if (shopFlashSaleActivityService.removeById(id)) {
|
||||||
|
return success("删除成功");
|
||||||
|
}
|
||||||
|
return fail("删除失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:save')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "批量添加秒杀活动")
|
||||||
|
@PostMapping("/batch")
|
||||||
|
public ApiResult<?> saveBatch(@RequestBody List<ShopFlashSaleActivity> list) {
|
||||||
|
if (shopFlashSaleActivityService.saveBatch(list)) {
|
||||||
|
return success("添加成功");
|
||||||
|
}
|
||||||
|
return fail("添加失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:update')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "批量修改秒杀活动")
|
||||||
|
@PutMapping("/batch")
|
||||||
|
public ApiResult<?> removeBatch(@RequestBody BatchParam<ShopFlashSaleActivity> batchParam) {
|
||||||
|
if (batchParam.update(shopFlashSaleActivityService, "id")) {
|
||||||
|
return success("修改成功");
|
||||||
|
}
|
||||||
|
return fail("修改失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:remove')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "批量删除秒杀活动")
|
||||||
|
@DeleteMapping("/batch")
|
||||||
|
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
|
||||||
|
if (shopFlashSaleActivityService.removeByIds(ids)) {
|
||||||
|
return success("删除成功");
|
||||||
|
}
|
||||||
|
return fail("删除失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ import com.gxwebsoft.shop.entity.ShopGoodsCategory;
|
|||||||
import com.gxwebsoft.shop.param.ShopGoodsCategoryParam;
|
import com.gxwebsoft.shop.param.ShopGoodsCategoryParam;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.core.web.PageParam;
|
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.gxwebsoft.shop.controller;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopGoodsProfit;
|
||||||
|
import com.gxwebsoft.shop.param.ShopGoodsProfitParam;
|
||||||
|
import com.gxwebsoft.shop.service.ShopGoodsProfitService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品分润设定控制器
|
||||||
|
*
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-05-20 14:33:00
|
||||||
|
*/
|
||||||
|
@Tag(name = "商品分润设定管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/shop/shop-goods-profit")
|
||||||
|
public class ShopGoodsProfitController extends BaseController {
|
||||||
|
@Resource
|
||||||
|
private ShopGoodsProfitService shopGoodsProfitService;
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopGoodsProfit:list')")
|
||||||
|
@Operation(summary = "分页查询商品分润设定")
|
||||||
|
@GetMapping("/page")
|
||||||
|
public ApiResult<PageResult<ShopGoodsProfit>> page(ShopGoodsProfitParam param) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopGoodsProfitService.pageRel(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopGoodsProfit:list')")
|
||||||
|
@Operation(summary = "查询全部商品分润设定")
|
||||||
|
@GetMapping()
|
||||||
|
public ApiResult<List<ShopGoodsProfit>> list(ShopGoodsProfitParam param) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopGoodsProfitService.listRel(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopGoodsProfit:list')")
|
||||||
|
@Operation(summary = "根据id查询商品分润设定")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ApiResult<ShopGoodsProfit> get(@PathVariable("id") Integer id) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopGoodsProfitService.getByIdRel(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopGoodsProfit:save')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "添加商品分润设定")
|
||||||
|
@PostMapping()
|
||||||
|
public ApiResult<?> save(@RequestBody ShopGoodsProfit shopGoodsProfit) {
|
||||||
|
// 记录当前登录用户id
|
||||||
|
User loginUser = getLoginUser();
|
||||||
|
if (loginUser != null) {
|
||||||
|
shopGoodsProfit.setUserId(loginUser.getUserId());
|
||||||
|
}
|
||||||
|
if (shopGoodsProfitService.save(shopGoodsProfit)) {
|
||||||
|
return success("添加成功");
|
||||||
|
}
|
||||||
|
return fail("添加失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopGoodsProfit:update')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "修改商品分润设定")
|
||||||
|
@PutMapping()
|
||||||
|
public ApiResult<?> update(@RequestBody ShopGoodsProfit shopGoodsProfit) {
|
||||||
|
if (shopGoodsProfitService.updateById(shopGoodsProfit)) {
|
||||||
|
return success("修改成功");
|
||||||
|
}
|
||||||
|
return fail("修改失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopGoodsProfit:remove')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "删除商品分润设定")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||||||
|
if (shopGoodsProfitService.removeById(id)) {
|
||||||
|
return success("删除成功");
|
||||||
|
}
|
||||||
|
return fail("删除失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,42 +5,45 @@ import cn.hutool.core.util.NumberUtil;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
|
||||||
import com.gxwebsoft.common.core.config.CertificateProperties;
|
import com.gxwebsoft.common.core.config.CertificateProperties;
|
||||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||||
import com.gxwebsoft.common.core.utils.CertificateLoader;
|
import com.gxwebsoft.common.core.utils.CertificateLoader;
|
||||||
|
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||||
import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
|
import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
|
||||||
import com.gxwebsoft.common.core.utils.WechatPayConfigValidator;
|
import com.gxwebsoft.common.core.utils.WechatPayConfigValidator;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
import com.gxwebsoft.common.core.web.BaseController;
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.core.web.BatchParam;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import com.gxwebsoft.common.system.entity.Payment;
|
import com.gxwebsoft.common.system.entity.Payment;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrderDelivery;
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
import com.gxwebsoft.shop.entity.ShopUserAddress;
|
import com.gxwebsoft.common.system.redis.OrderNoUtils;
|
||||||
import com.gxwebsoft.shop.service.*;
|
import com.gxwebsoft.glt.service.GltTicketIssueService;
|
||||||
import com.gxwebsoft.glt.service.GltTicketRevokeService;
|
import com.gxwebsoft.glt.service.GltTicketRevokeService;
|
||||||
import com.gxwebsoft.shop.service.impl.KuaiDi100Impl;
|
import com.gxwebsoft.glt.task.DealerOrderSettlement10584Task;
|
||||||
import com.gxwebsoft.shop.task.OrderAutoCancelTask;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
|
||||||
import com.gxwebsoft.shop.param.ShopOrderParam;
|
|
||||||
import com.gxwebsoft.shop.dto.OrderCreateRequest;
|
|
||||||
import com.gxwebsoft.shop.dto.OrderPrepayRequest;
|
|
||||||
import com.gxwebsoft.shop.dto.UpdatePaymentStatusRequest;
|
|
||||||
import com.gxwebsoft.payment.service.PaymentService;
|
|
||||||
import com.gxwebsoft.payment.dto.PaymentResponse;
|
import com.gxwebsoft.payment.dto.PaymentResponse;
|
||||||
import com.gxwebsoft.payment.enums.PaymentType;
|
import com.gxwebsoft.payment.enums.PaymentType;
|
||||||
import com.gxwebsoft.common.core.web.ApiResult;
|
import com.gxwebsoft.payment.service.PaymentService;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.shop.dto.*;
|
||||||
import com.gxwebsoft.common.core.web.BatchParam;
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
import com.gxwebsoft.shop.entity.ShopOrderDelivery;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopUserAddress;
|
||||||
|
import com.gxwebsoft.shop.param.ShopOrderParam;
|
||||||
|
import com.gxwebsoft.shop.service.*;
|
||||||
|
import com.gxwebsoft.shop.service.impl.KuaiDi100Impl;
|
||||||
|
import com.gxwebsoft.shop.task.OrderAutoCancelTask;
|
||||||
|
import com.gxwebsoft.shop.vo.ShopOrderMyVerifyVO;
|
||||||
|
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||||
import com.wechat.pay.java.core.notification.NotificationConfig;
|
import com.wechat.pay.java.core.notification.NotificationConfig;
|
||||||
import com.wechat.pay.java.core.notification.NotificationParser;
|
import com.wechat.pay.java.core.notification.NotificationParser;
|
||||||
import com.wechat.pay.java.core.notification.RequestParam;
|
import com.wechat.pay.java.core.notification.RequestParam;
|
||||||
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
import com.wechat.pay.java.service.payments.model.Transaction;
|
||||||
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springdoc.api.annotations.ParameterObject;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@@ -53,6 +56,7 @@ import java.time.LocalDateTime;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订单控制器
|
* 订单控制器
|
||||||
@@ -65,6 +69,8 @@ import java.util.Objects;
|
|||||||
@RequestMapping("/api/shop/shop-order")
|
@RequestMapping("/api/shop/shop-order")
|
||||||
public class ShopOrderController extends BaseController {
|
public class ShopOrderController extends BaseController {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ShopOrderController.class);
|
private static final Logger logger = LoggerFactory.getLogger(ShopOrderController.class);
|
||||||
|
/** 按商户号缓存 NotificationConfig,避免每次回调都重新拉取平台证书 */
|
||||||
|
private final ConcurrentHashMap<String, NotificationConfig> notifyConfigCache = new ConcurrentHashMap<>();
|
||||||
@Resource
|
@Resource
|
||||||
private ShopOrderService shopOrderService;
|
private ShopOrderService shopOrderService;
|
||||||
@Resource
|
@Resource
|
||||||
@@ -107,6 +113,12 @@ public class ShopOrderController extends BaseController {
|
|||||||
private GltTicketRevokeService gltTicketRevokeService;
|
private GltTicketRevokeService gltTicketRevokeService;
|
||||||
@Resource
|
@Resource
|
||||||
private ShopDealerCommissionRollbackService shopDealerCommissionRollbackService;
|
private ShopDealerCommissionRollbackService shopDealerCommissionRollbackService;
|
||||||
|
@Resource
|
||||||
|
private GltTicketIssueService gltTicketIssueService;
|
||||||
|
@Resource
|
||||||
|
private OrderNoUtils orderNoUtils;
|
||||||
|
@Resource
|
||||||
|
private DealerOrderSettlement10584Task dealerOrderSettlement;
|
||||||
|
|
||||||
@Operation(summary = "分页查询订单")
|
@Operation(summary = "分页查询订单")
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@@ -130,14 +142,20 @@ public class ShopOrderController extends BaseController {
|
|||||||
return success(shopOrderService.getByIdRel(id));
|
return success(shopOrderService.getByIdRel(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "添加订单")
|
// @PreAuthorize("hasAuthority('shop:shopOrder:list')")
|
||||||
|
@Operation(summary = "我已核销订单")
|
||||||
|
@GetMapping("/myVerifyOrder")
|
||||||
|
public ApiResult<ShopOrderMyVerifyVO> myVerifyOrder(@ParameterObject ShopOrderMyVerifyDto myVerifyDto) {
|
||||||
|
return success(shopOrderService.myVerifyOrder(myVerifyDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "添加订单【单商品添加】")
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
public ApiResult<?> save(@RequestBody OrderCreateRequest request) {
|
public ApiResult<?> save(@RequestBody OrderCreateRequest request) {
|
||||||
User loginUser = getLoginUser();
|
User loginUser = getLoginUser();
|
||||||
if (loginUser == null) {
|
if (loginUser == null) {
|
||||||
return fail("用户未登录");
|
return fail("用户未登录");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<String, String> wxOrderInfo = orderBusinessService.createOrder(request, loginUser);
|
Map<String, String> wxOrderInfo = orderBusinessService.createOrder(request, loginUser);
|
||||||
return success("下单成功", wxOrderInfo);
|
return success("下单成功", wxOrderInfo);
|
||||||
@@ -438,14 +456,15 @@ public class ShopOrderController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopOrder:refund')")
|
@PreAuthorize("hasAuthority('shop:shopOrder:refund')")
|
||||||
@Operation(summary = "订单退款操作(申请退款/同意退款)", description = "orderStatus=4 申请退款;orderStatus=6 同意退款并发起原路退款")
|
@Operation(summary = "订单退款操作(申请退款/同意退款/拒绝退款)", description = "orderStatus=4 申请退款;orderStatus=5 拒绝退款;orderStatus=6 同意退款并发起原路退款;orderStatus=7 客户端申请退款")
|
||||||
@PutMapping("/refund")
|
@PutMapping("/refund")
|
||||||
public ApiResult<?> refund(@RequestBody ShopOrder req) {
|
public ApiResult<?> refund(@RequestBody ShopOrder req) {
|
||||||
if (req == null || req.getOrderId() == null || req.getOrderStatus() == null) {
|
if (req == null || req.getOrderId() == null || req.getOrderStatus() == null) {
|
||||||
return fail("orderId 和 orderStatus 不能为空");
|
return fail("orderId 和 orderStatus 不能为空");
|
||||||
}
|
}
|
||||||
if (!Objects.equals(req.getOrderStatus(), 4) && !Objects.equals(req.getOrderStatus(), 6) && !Objects.equals(req.getOrderStatus(), 7)) {
|
if (!Objects.equals(req.getOrderStatus(), 4) && !Objects.equals(req.getOrderStatus(), 5)
|
||||||
return fail("orderStatus 仅支持 4(申请退款) 或 6(同意退款) 或 7(客户端申请退款)");
|
&& !Objects.equals(req.getOrderStatus(), 6) && !Objects.equals(req.getOrderStatus(), 7)) {
|
||||||
|
return fail("orderStatus 仅支持 4(申请退款)、5(拒绝退款)、6(同意退款)、7(客户端申请退款)");
|
||||||
}
|
}
|
||||||
|
|
||||||
ShopOrder current = shopOrderService.getById(req.getOrderId());
|
ShopOrder current = shopOrderService.getById(req.getOrderId());
|
||||||
@@ -490,12 +509,12 @@ public class ShopOrderController extends BaseController {
|
|||||||
if (!Boolean.TRUE.equals(current.getPayStatus())) {
|
if (!Boolean.TRUE.equals(current.getPayStatus())) {
|
||||||
return fail("订单未支付,无法退款");
|
return fail("订单未支付,无法退款");
|
||||||
}
|
}
|
||||||
if (StrUtil.isNotBlank(current.getRefundOrder())) {
|
if (StrUtil.isNotBlank(current.getRefundOrder()) || current.getOrderStatus() == 6) {
|
||||||
logger.warn("订单已经退款过,订单号: {}, 退款单号: {}", current.getOrderNo(), current.getRefundOrder());
|
logger.warn("订单已经退款过,订单号: {}, 退款单号: {}", current.getOrderNo(), current.getRefundOrder() != null ? current.getRefundOrder() : "");
|
||||||
return fail("订单已退款,请勿重复操作");
|
return fail("订单已退款,请勿重复操作");
|
||||||
}
|
}
|
||||||
|
|
||||||
String refundNo = "RF" + IdUtil.getSnowflakeNextId();
|
String refundNo = orderNoUtils.generate("RF");
|
||||||
|
|
||||||
BigDecimal refundAmount = req.getRefundMoney();
|
BigDecimal refundAmount = req.getRefundMoney();
|
||||||
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
@@ -569,7 +588,7 @@ public class ShopOrderController extends BaseController {
|
|||||||
rollbackOrder.setOrderNo(current.getOrderNo());
|
rollbackOrder.setOrderNo(current.getOrderNo());
|
||||||
rollbackOrder.setPayPrice(current.getPayPrice());
|
rollbackOrder.setPayPrice(current.getPayPrice());
|
||||||
rollbackOrder.setTotalPrice(current.getTotalPrice());
|
rollbackOrder.setTotalPrice(current.getTotalPrice());
|
||||||
boolean rollbackOk = shopDealerCommissionRollbackService.rollbackOnOrderRefund(rollbackOrder, refundAmount);
|
boolean rollbackOk = shopDealerCommissionRollbackService.rollbackOnOrderRefundV2(rollbackOrder, refundAmount);
|
||||||
if (!rollbackOk) {
|
if (!rollbackOk) {
|
||||||
logger.error("退款成功但回退分红/分润/佣金失败 - tenantId={}, orderId={}, orderNo={}",
|
logger.error("退款成功但回退分红/分润/佣金失败 - tenantId={}, orderId={}, orderNo={}",
|
||||||
tenantId, current.getOrderId(), current.getOrderNo());
|
tenantId, current.getOrderId(), current.getOrderNo());
|
||||||
@@ -760,7 +779,7 @@ public class ShopOrderController extends BaseController {
|
|||||||
@Schema(description = "异步通知11")
|
@Schema(description = "异步通知11")
|
||||||
@PostMapping("/notify/{tenantId}")
|
@PostMapping("/notify/{tenantId}")
|
||||||
public String wxNotify(@RequestHeader Map<String, String> header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) {
|
public String wxNotify(@RequestHeader Map<String, String> header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) {
|
||||||
logger.info("异步通知*************** = " + tenantId);
|
logger.info("异步通知*************** = " + body + ",租户:" +tenantId);
|
||||||
|
|
||||||
// 获取支付配置信息用于解密
|
// 获取支付配置信息用于解密
|
||||||
String key = "Payment:1:".concat(tenantId.toString());
|
String key = "Payment:1:".concat(tenantId.toString());
|
||||||
@@ -791,10 +810,14 @@ public class ShopOrderController extends BaseController {
|
|||||||
.body(body)
|
.body(body)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 创建通知配置 - 使用与下单方法相同的证书配置逻辑
|
// 创建通知配置 - 使用与下单方法相同的证书配置逻辑(按 mchId 缓存,避免重复拉取平台证书)
|
||||||
NotificationConfig config;
|
NotificationConfig config;
|
||||||
|
final String mchId = payment.getMchId();
|
||||||
|
config = notifyConfigCache.get(mchId);
|
||||||
|
if (config == null) {
|
||||||
try {
|
try {
|
||||||
if (active.equals("dev")) {
|
NotificationConfig newConfig;
|
||||||
|
if (active.equals("local")) {
|
||||||
// 开发环境 - 构建包含租户号的私钥路径
|
// 开发环境 - 构建包含租户号的私钥路径
|
||||||
String tenantCertPath = "dev/wechat/" + tenantId;
|
String tenantCertPath = "dev/wechat/" + tenantId;
|
||||||
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
|
||||||
@@ -819,8 +842,8 @@ public class ShopOrderController extends BaseController {
|
|||||||
logger.info("商户证书序列号: {}", payment.getMerchantSerialNumber());
|
logger.info("商户证书序列号: {}", payment.getMerchantSerialNumber());
|
||||||
|
|
||||||
// 使用自动证书配置
|
// 使用自动证书配置
|
||||||
config = new RSAAutoCertificateConfig.Builder()
|
newConfig = new RSAAutoCertificateConfig.Builder()
|
||||||
.merchantId(payment.getMchId())
|
.merchantId(mchId)
|
||||||
.privateKeyFromPath(privateKey)
|
.privateKeyFromPath(privateKey)
|
||||||
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
||||||
.apiV3Key(apiV3Key)
|
.apiV3Key(apiV3Key)
|
||||||
@@ -857,8 +880,8 @@ public class ShopOrderController extends BaseController {
|
|||||||
String apiV3Key = payment.getApiKey();
|
String apiV3Key = payment.getApiKey();
|
||||||
|
|
||||||
// 使用自动证书配置
|
// 使用自动证书配置
|
||||||
config = new RSAAutoCertificateConfig.Builder()
|
newConfig = new RSAAutoCertificateConfig.Builder()
|
||||||
.merchantId(payment.getMchId())
|
.merchantId(mchId)
|
||||||
.privateKeyFromPath(privateKey)
|
.privateKeyFromPath(privateKey)
|
||||||
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
||||||
.apiV3Key(apiV3Key)
|
.apiV3Key(apiV3Key)
|
||||||
@@ -866,8 +889,11 @@ public class ShopOrderController extends BaseController {
|
|||||||
|
|
||||||
logger.info("✅ 生产环境使用自动证书配置创建通知解析器成功");
|
logger.info("✅ 生产环境使用自动证书配置创建通知解析器成功");
|
||||||
}
|
}
|
||||||
|
// 放入缓存
|
||||||
|
notifyConfigCache.putIfAbsent(mchId, newConfig);
|
||||||
|
config = notifyConfigCache.get(mchId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("❌ 创建通知配置失败 - 租户ID: {}, 商户号: {}", tenantId, payment.getMchId(), e);
|
logger.error("❌ 创建通知配置失败 - 租户ID: {}, 商户号: {}", tenantId, mchId, e);
|
||||||
logger.error("🔍 错误详情: {}", e.getMessage());
|
logger.error("🔍 错误详情: {}", e.getMessage());
|
||||||
logger.error("💡 请检查:");
|
logger.error("💡 请检查:");
|
||||||
logger.error("1. 证书文件是否存在且路径正确");
|
logger.error("1. 证书文件是否存在且路径正确");
|
||||||
@@ -876,6 +902,9 @@ public class ShopOrderController extends BaseController {
|
|||||||
logger.error("4. 网络连接是否正常");
|
logger.error("4. 网络连接是否正常");
|
||||||
throw new RuntimeException("微信支付通知配置失败: " + e.getMessage(), e);
|
throw new RuntimeException("微信支付通知配置失败: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("✅ 使用缓存的通知配置 - 商户号: {}", mchId);
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化 NotificationParser
|
// 初始化 NotificationParser
|
||||||
NotificationParser parser = new NotificationParser(config);
|
NotificationParser parser = new NotificationParser(config);
|
||||||
@@ -886,9 +915,10 @@ public class ShopOrderController extends BaseController {
|
|||||||
logger.info("开始解析微信支付异步通知...");
|
logger.info("开始解析微信支付异步通知...");
|
||||||
Transaction transaction = parser.parse(requestParam, Transaction.class);
|
Transaction transaction = parser.parse(requestParam, Transaction.class);
|
||||||
logger.info("✅ 异步通知解析成功 - 交易状态: {}, 商户订单号: {}",
|
logger.info("✅ 异步通知解析成功 - 交易状态: {}, 商户订单号: {}",
|
||||||
transaction.getTradeStateDesc(), transaction.getOutTradeNo());
|
transaction.getTradeState(), transaction.getOutTradeNo());
|
||||||
|
|
||||||
if (StrUtil.equals("支付成功", transaction.getTradeStateDesc())) {
|
// 使用枚举值判断支付状态,避免依赖状态描述字符串
|
||||||
|
if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
|
||||||
final String outTradeNo = transaction.getOutTradeNo();
|
final String outTradeNo = transaction.getOutTradeNo();
|
||||||
final String transactionId = transaction.getTransactionId();
|
final String transactionId = transaction.getTransactionId();
|
||||||
final Integer total = transaction.getAmount().getTotal();
|
final Integer total = transaction.getAmount().getTotal();
|
||||||
@@ -920,6 +950,10 @@ public class ShopOrderController extends BaseController {
|
|||||||
System.out.println("实际付款金额 = " + order.getPayPrice());
|
System.out.println("实际付款金额 = " + order.getPayPrice());
|
||||||
// 更新订单状态并处理支付成功后的业务逻辑(包括累加商品销量)
|
// 更新订单状态并处理支付成功后的业务逻辑(包括累加商品销量)
|
||||||
shopOrderService.updateByOutTradeNo(order);
|
shopOrderService.updateByOutTradeNo(order);
|
||||||
|
|
||||||
|
//支付成功执行异步任务
|
||||||
|
gltTicketIssueService.paySuccessTask(order.getOrderNo(), tenantId);
|
||||||
|
|
||||||
return "SUCCESS";
|
return "SUCCESS";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -940,6 +974,33 @@ public class ShopOrderController extends BaseController {
|
|||||||
return "fail";
|
return "fail";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "核销订单", description = "核销员扫码核销用户订单")
|
||||||
|
@PutMapping("/verifyOrder")
|
||||||
|
public ApiResult<Boolean> verifyOrder(@RequestBody VerifyShopOrderDto verifyDto){
|
||||||
|
return success(shopOrderService.verifyOrder(verifyDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "支付成功任务", description = "用户支付成功后执行任务")
|
||||||
|
@PutMapping("/paySuccessTask")
|
||||||
|
public ApiResult<Boolean> paySuccessTask(@RequestBody PaySuccessTaskDto taskDto){
|
||||||
|
gltTicketIssueService.paySuccessTask(taskDto.getOrderNo(), taskDto.getTenantId());
|
||||||
|
return success(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "支付成功发送水票", description = "支付成功发送水票")
|
||||||
|
@PutMapping("/suerTicketRelease")
|
||||||
|
public ApiResult<Boolean> suerTicketRelease(@RequestBody PaySuccessTaskDto taskDto){
|
||||||
|
gltTicketIssueService.suerTicketRelease(taskDto.getOrderNo(), taskDto.getTenantId());
|
||||||
|
return success(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "支付成功结算", description = "支付成功结算")
|
||||||
|
@PutMapping("/orderSettlement")
|
||||||
|
public ApiResult<Boolean> orderSettlement(@RequestBody PaySuccessTaskDto taskDto){
|
||||||
|
dealerOrderSettlement.orderSettlement(taskDto.getOrderNo());
|
||||||
|
return success(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "更新订单支付状态", description = "用户支付成功后主动同步订单状态")
|
@Operation(summary = "更新订单支付状态", description = "用户支付成功后主动同步订单状态")
|
||||||
@PutMapping("/payment-status")
|
@PutMapping("/payment-status")
|
||||||
public ApiResult<?> updateOrderPaymentStatus(@RequestBody UpdatePaymentStatusRequest request) {
|
public ApiResult<?> updateOrderPaymentStatus(@RequestBody UpdatePaymentStatusRequest request) {
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.gxwebsoft.shop.controller;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopSurchargeConfig;
|
||||||
|
import com.gxwebsoft.shop.param.ShopSurchargeConfigParam;
|
||||||
|
import com.gxwebsoft.shop.service.ShopSurchargeConfigService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 步梯费用设置控制器
|
||||||
|
*
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-04-28 16:30:00
|
||||||
|
*/
|
||||||
|
@Tag(name = "步梯费用设置管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/shop/shop-surcharge-config")
|
||||||
|
public class ShopSurchargeConfigController extends BaseController {
|
||||||
|
@Resource
|
||||||
|
private ShopSurchargeConfigService shopSurchargeConfigService;
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:list')")
|
||||||
|
@Operation(summary = "分页查询步梯费用设置")
|
||||||
|
@GetMapping("/page")
|
||||||
|
public ApiResult<PageResult<ShopSurchargeConfig>> page(ShopSurchargeConfigParam param) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopSurchargeConfigService.pageRel(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:list')")
|
||||||
|
@Operation(summary = "根据id查询步梯费用设置")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ApiResult<ShopSurchargeConfig> get(@PathVariable("id") Integer id) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopSurchargeConfigService.getByIdRel(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:list')")
|
||||||
|
@Operation(summary = "根据类型查询步梯费用设置")
|
||||||
|
@GetMapping("/getInfoByType")
|
||||||
|
public ApiResult<ShopSurchargeConfig> getInfoByType(@RequestParam Integer type) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopSurchargeConfigService.getInfoByType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:save')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "添加步梯费用设置")
|
||||||
|
@PostMapping()
|
||||||
|
public ApiResult<Integer> save(@RequestBody ShopSurchargeConfig shopSurchargeConfig) {
|
||||||
|
return success(shopSurchargeConfigService.saveInfo(shopSurchargeConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:update')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "修改步梯费用设置")
|
||||||
|
@PutMapping()
|
||||||
|
public ApiResult<Boolean> update(@RequestBody ShopSurchargeConfig shopSurchargeConfig) {
|
||||||
|
return success(shopSurchargeConfigService.updateInfo(shopSurchargeConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:update')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "修改步梯费用设置")
|
||||||
|
@PutMapping("/updateStatus")
|
||||||
|
public ApiResult<Boolean> updateStatus(@RequestParam("id") Integer id) {
|
||||||
|
return success(shopSurchargeConfigService.updateStatus(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:remove')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "删除步梯费用设置")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||||||
|
if (shopSurchargeConfigService.removeById(id)) {
|
||||||
|
return success("删除成功");
|
||||||
|
}
|
||||||
|
return fail("删除失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -79,11 +79,8 @@
|
|||||||
@OperationLog
|
@OperationLog
|
||||||
@Operation(summary = "修改收货地址")
|
@Operation(summary = "修改收货地址")
|
||||||
@PutMapping()
|
@PutMapping()
|
||||||
public ApiResult<?> update(@RequestBody ShopUserAddress shopUserAddress) {
|
public ApiResult<Boolean> update(@RequestBody ShopUserAddress shopUserAddress) {
|
||||||
if (shopUserAddressService.updateById(shopUserAddress)) {
|
return success(shopUserAddressService.updateInfo(shopUserAddress));
|
||||||
return success("修改成功");
|
|
||||||
}
|
|
||||||
return fail("修改失败");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopUserAddress:remove')")
|
@PreAuthorize("hasAuthority('shop:shopUserAddress:remove')")
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package com.gxwebsoft.shop.controller;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopVerifyUser;
|
||||||
|
import com.gxwebsoft.shop.param.ShopVerifyUserParam;
|
||||||
|
import com.gxwebsoft.shop.service.ShopVerifyUserService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单核销人管理控制器
|
||||||
|
*
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-05-25 17:13:01
|
||||||
|
*/
|
||||||
|
@Tag(name = "订单核销人管理管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/shop/shop-verify-user")
|
||||||
|
public class ShopVerifyUserController extends BaseController {
|
||||||
|
@Resource
|
||||||
|
private ShopVerifyUserService shopVerifyUserService;
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopVerifyUser:list')")
|
||||||
|
@Operation(summary = "分页查询订单核销人管理")
|
||||||
|
@GetMapping("/page")
|
||||||
|
public ApiResult<PageResult<ShopVerifyUser>> page(ShopVerifyUserParam param) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopVerifyUserService.pageRel(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopVerifyUser:list')")
|
||||||
|
@Operation(summary = "查询全部订单核销人管理")
|
||||||
|
@GetMapping()
|
||||||
|
public ApiResult<List<ShopVerifyUser>> list(ShopVerifyUserParam param) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopVerifyUserService.listRel(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopVerifyUser:list')")
|
||||||
|
@Operation(summary = "根据id查询订单核销人管理")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ApiResult<ShopVerifyUser> get(@PathVariable("id") Integer id) {
|
||||||
|
// 使用关联查询
|
||||||
|
return success(shopVerifyUserService.getByIdRel(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopVerifyUser:list')")
|
||||||
|
@Operation(summary = "根据用户ID查询订单核销人管理")
|
||||||
|
@GetMapping("/getByUserId")
|
||||||
|
public ApiResult<ShopVerifyUser> getByUserId(@RequestParam("userId") Integer userId) {
|
||||||
|
return success(shopVerifyUserService.getInfo(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopVerifyUser:save')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "添加订单核销人管理")
|
||||||
|
@PostMapping()
|
||||||
|
public ApiResult<Boolean> save(@RequestBody ShopVerifyUser shopVerifyUser) {
|
||||||
|
return success(shopVerifyUserService.saveInfo(shopVerifyUser));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopVerifyUser:update')")
|
||||||
|
@Operation(summary = "修改订单核销人管理")
|
||||||
|
@PutMapping()
|
||||||
|
public ApiResult<?> update(@RequestBody ShopVerifyUser shopVerifyUser) {
|
||||||
|
if (shopVerifyUserService.updateById(shopVerifyUser)) {
|
||||||
|
return success("修改成功");
|
||||||
|
}
|
||||||
|
return fail("修改失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "修改订单核销人开启状态")
|
||||||
|
@PutMapping("/updateStatus")
|
||||||
|
public ApiResult<Boolean> updateStatus(@RequestParam("id") Integer id) {
|
||||||
|
return success(shopVerifyUserService.updateStatus(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "修改订单核销人核销权限")
|
||||||
|
@PutMapping("/updateVerifyFlag")
|
||||||
|
public ApiResult<Boolean> updateVerifyFlag(@RequestParam("id") Integer id) {
|
||||||
|
return success(shopVerifyUserService.updateVerifyFlag(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @PreAuthorize("hasAuthority('shop:shopVerifyUser:remove')")
|
||||||
|
@OperationLog
|
||||||
|
@Operation(summary = "删除订单核销人管理")
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||||||
|
if (shopVerifyUserService.removeById(id)) {
|
||||||
|
return success("删除成功");
|
||||||
|
}
|
||||||
|
return fail("删除失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -27,6 +27,9 @@ public class OrderCreateRequest {
|
|||||||
@Max(value = 2, message = "订单类型值无效")
|
@Max(value = 2, message = "订单类型值无效")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
|
@Schema(description = "订单类型 1-及时自配送 2-自提 3-预约自配送 4-发快递 5-配送【系统自动识别电子围栏内转及时配送,电子围栏外发快递】")
|
||||||
|
private Integer orderType;
|
||||||
|
|
||||||
@Size(max = 60, message = "备注长度不能超过60个字符")
|
@Size(max = 60, message = "备注长度不能超过60个字符")
|
||||||
@Schema(description = "订单标题")
|
@Schema(description = "订单标题")
|
||||||
private String title;
|
private String title;
|
||||||
@@ -88,6 +91,12 @@ public class OrderCreateRequest {
|
|||||||
@Schema(description = "发货店铺")
|
@Schema(description = "发货店铺")
|
||||||
private String expressMerchantName;
|
private String expressMerchantName;
|
||||||
|
|
||||||
|
@Schema(description = "配送方式 0-电梯 1-步梯")
|
||||||
|
private Integer deliveryMethod;
|
||||||
|
|
||||||
|
@Schema(description = "楼层")
|
||||||
|
private Integer deliveryFloor;
|
||||||
|
|
||||||
@Schema(description = "订单总额")
|
@Schema(description = "订单总额")
|
||||||
@NotNull(message = "订单总额不能为空")
|
@NotNull(message = "订单总额不能为空")
|
||||||
@DecimalMin(value = "0.01", message = "订单总额必须大于0")
|
@DecimalMin(value = "0.01", message = "订单总额必须大于0")
|
||||||
@@ -147,6 +156,9 @@ public class OrderCreateRequest {
|
|||||||
@NotNull(message = "租户ID不能为空")
|
@NotNull(message = "租户ID不能为空")
|
||||||
private Integer tenantId;
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "秒杀活动ID")
|
||||||
|
private Integer activityId;
|
||||||
|
|
||||||
@Schema(description = "订单商品列表")
|
@Schema(description = "订单商品列表")
|
||||||
@Valid
|
@Valid
|
||||||
@NotEmpty(message = "订单商品列表不能为空")
|
@NotEmpty(message = "订单商品列表不能为空")
|
||||||
@@ -158,6 +170,9 @@ public class OrderCreateRequest {
|
|||||||
@Data
|
@Data
|
||||||
@Schema(name = "OrderGoodsItem", description = "订单商品项")
|
@Schema(name = "OrderGoodsItem", description = "订单商品项")
|
||||||
public static class OrderGoodsItem {
|
public static class OrderGoodsItem {
|
||||||
|
@Schema(description = "秒杀活动ID")
|
||||||
|
private Integer activityId;
|
||||||
|
|
||||||
@Schema(description = "商品ID", required = true)
|
@Schema(description = "商品ID", required = true)
|
||||||
@NotNull(message = "商品ID不能为空")
|
@NotNull(message = "商品ID不能为空")
|
||||||
private Integer goodsId;
|
private Integer goodsId;
|
||||||
|
|||||||
25
src/main/java/com/gxwebsoft/shop/dto/PaySuccessTaskDto.java
Normal file
25
src/main/java/com/gxwebsoft/shop/dto/PaySuccessTaskDto.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package com.gxwebsoft.shop.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付成功执行请求类
|
||||||
|
*
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-05-04
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "PaySuccessTaskDto", description = "支付成功执行请求类")
|
||||||
|
public class PaySuccessTaskDto {
|
||||||
|
|
||||||
|
@Schema(description = "订单号")
|
||||||
|
@NotBlank(message = "订单号不能为空")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID", example = "10584")
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.gxwebsoft.shop.dto;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.web.BaseParam;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询个人流水
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "ShopDealerCapitalWaterDto", description = "查询个人流水")
|
||||||
|
public class ShopDealerCapitalWaterDto extends BaseParam {
|
||||||
|
|
||||||
|
@Schema(description = "用户ID")
|
||||||
|
@NotEmpty(message = "用户ID不能为空!")
|
||||||
|
private Integer userId;
|
||||||
|
|
||||||
|
@Schema(description = "起始时间", example = "2026-05-01")
|
||||||
|
private String dateStart;
|
||||||
|
|
||||||
|
@Schema(description = "结束时间", example = "2026-05-31")
|
||||||
|
private String dateEnd;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.gxwebsoft.shop.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分销订单退款
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-05-13
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "ShopDealerUserReduceDto", description = "分销订单退款")
|
||||||
|
public class ShopDealerRefundDto {
|
||||||
|
|
||||||
|
@Schema(description = "订单号")
|
||||||
|
@NotNull(message = "变订单号不可为空!")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "退款金额")
|
||||||
|
private BigDecimal refundAmount;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.gxwebsoft.shop.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分销订单结算
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-05-13
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "ShopDealerSettlementDto", description = "分销订单结算")
|
||||||
|
public class ShopDealerSettlementDto {
|
||||||
|
|
||||||
|
@Schema(description = "变动类型 1-操作冻结账户余额 2-操作提现账户余额【直接结算】 3-解冻 4-退款")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
@Schema(description = "资金流动类型 (10分销收入 11团队管理津贴收入 12分红收入 13现场推广收入 20提现支出 30转账支出 40转账收入 50佣金解冻 60配送奖励 70佣金退回【退单】)")
|
||||||
|
private Integer flowType;
|
||||||
|
|
||||||
|
@Schema(description = "描述")
|
||||||
|
private String comments;
|
||||||
|
|
||||||
|
@Schema(description = "商城ID")
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "明细列表")
|
||||||
|
private List<ShopDealerSettlementItemDto> itemList;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.gxwebsoft.shop.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分销订单退款
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-05-13
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "ShopDealerSettlementItemDto", description = "分销订单退款")
|
||||||
|
public class ShopDealerSettlementItemDto {
|
||||||
|
|
||||||
|
@Schema(description = "订单号")
|
||||||
|
private String no;
|
||||||
|
|
||||||
|
@Schema(description = "分销商用户ID")
|
||||||
|
private Integer userId;
|
||||||
|
|
||||||
|
@Schema(description = "变更金额")
|
||||||
|
private BigDecimal money;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.gxwebsoft.shop.dto;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum;
|
||||||
|
import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新分销用户资金
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-05-06
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "ShopDealerUserReduceDto", description = "更新分销用户资金请求")
|
||||||
|
public class ShopDealerUserReduceDto {
|
||||||
|
|
||||||
|
@Schema(description = "变动类型 1-操作冻结账户余额 2-操作提现账户余额【直接结算】 3-解冻")
|
||||||
|
@NotNull(message = "变更类型不可为空!")
|
||||||
|
private ShopDealerTypeEnum typeEnum;
|
||||||
|
|
||||||
|
@Schema(description = "用户ID")
|
||||||
|
private Integer userId;
|
||||||
|
|
||||||
|
@Schema(description = "订单主体用户ID")
|
||||||
|
private Integer orderUserId;
|
||||||
|
|
||||||
|
@Schema(description = "订单号")
|
||||||
|
@NotEmpty(message = "订单号不能为空!")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
@Schema(description = "变更金额")
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
@Schema(description = "业务类型")
|
||||||
|
@NotEmpty(message = "业务类型不能为空!")
|
||||||
|
private ShopDealerCapitalUpdateEnum updateEnum;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.gxwebsoft.shop.dto;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.web.BaseParam;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询个人核销记录
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "ShopOrderMyVerifyDto", description = "查询个人核销记录")
|
||||||
|
public class ShopOrderMyVerifyDto extends BaseParam {
|
||||||
|
|
||||||
|
@Schema(description = "用户ID")
|
||||||
|
private Integer userId;
|
||||||
|
|
||||||
|
@Schema(description = "订单号")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
|
@Schema(description = "开始时间【核销时间】", example = "2026-05-20")
|
||||||
|
private String dateStart;
|
||||||
|
|
||||||
|
@Schema(description = "结束时间【核销时间】", example = "2026-05-25")
|
||||||
|
private String dateEnd;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ public class UserOrderStats implements Serializable {
|
|||||||
@Schema(description = "待核销(statusFilter=2)")
|
@Schema(description = "待核销(statusFilter=2)")
|
||||||
private Long waitVerify;
|
private Long waitVerify;
|
||||||
|
|
||||||
@Schema(description = "待收货(statusFilter=3)")
|
@Schema(description = "待收货/使用(statusFilter=3)")
|
||||||
private Long waitReceive;
|
private Long waitReceive;
|
||||||
|
|
||||||
@Schema(description = "待评价(statusFilter=4)")
|
@Schema(description = "待评价(statusFilter=4)")
|
||||||
|
|||||||
25
src/main/java/com/gxwebsoft/shop/dto/VerifyShopOrderDto.java
Normal file
25
src/main/java/com/gxwebsoft/shop/dto/VerifyShopOrderDto.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package com.gxwebsoft.shop.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新订单支付状态请求DTO
|
||||||
|
*
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-05-04
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "VerifyShopOrderDto", description = "更新订单支付状态请求")
|
||||||
|
public class VerifyShopOrderDto {
|
||||||
|
|
||||||
|
@Schema(description = "核销码")
|
||||||
|
@NotBlank(message = "核销码不能为空")
|
||||||
|
private String verifyCode;
|
||||||
|
|
||||||
|
@Schema(description = "核销码类型 1-普通核销【无推广佣金】 2-推广结算【有订单佣金】", example = "2")
|
||||||
|
private Integer verifyType;
|
||||||
|
|
||||||
|
}
|
||||||
81
src/main/java/com/gxwebsoft/shop/entity/ShopActiveImage.java
Normal file
81
src/main/java/com/gxwebsoft/shop/entity/ShopActiveImage.java
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package com.gxwebsoft.shop.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推广码底图
|
||||||
|
*
|
||||||
|
* @author xm
|
||||||
|
* @since 2026-04-27 18:02:17
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Schema(name = "ShopActiveImage对象", description = "推广码底图")
|
||||||
|
@TableName("shop_active_image")
|
||||||
|
public class ShopActiveImage implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "主键ID")
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "类型 0-推广底图 1-其他")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
@Schema(description = "图片地址,多个以‘,’隔开")
|
||||||
|
private String imgUrl;
|
||||||
|
|
||||||
|
@Schema(description = "分享底图")
|
||||||
|
private String shareImg;
|
||||||
|
|
||||||
|
@Schema(description = "图片地址集合")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private List<String> imgUrlList;
|
||||||
|
|
||||||
|
@Schema(description = "启用状态 0-启用 1-禁用")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "排序")
|
||||||
|
private Integer sortNumber;
|
||||||
|
|
||||||
|
@Schema(description = "跳转商品ID")
|
||||||
|
private Integer goodsId;
|
||||||
|
|
||||||
|
@Schema(description = "跳转商品名称")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String goodsName;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
@NotNull(message = "租户ID不能为空!")
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
private String creator;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "更新人")
|
||||||
|
private String updater;
|
||||||
|
|
||||||
|
@Schema(description = "修改时间")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "是否删除 0-未删 1-已删")
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package com.gxwebsoft.shop.entity;
|
package com.gxwebsoft.shop.entity;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
@@ -20,6 +21,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Schema(name = "ShopDealerCapital对象", description = "分销商资金明细表")
|
@Schema(name = "ShopDealerCapital对象", description = "分销商资金明细表")
|
||||||
|
@TableName("shop_dealer_capital")
|
||||||
public class ShopDealerCapital implements Serializable {
|
public class ShopDealerCapital implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@@ -27,26 +29,41 @@ public class ShopDealerCapital implements Serializable {
|
|||||||
@TableId(value = "id", type = IdType.AUTO)
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
private Integer id;
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "订单编号")
|
||||||
|
private String no;
|
||||||
|
|
||||||
@Schema(description = "分销商用户ID")
|
@Schema(description = "分销商用户ID")
|
||||||
private Integer userId;
|
private Integer userId;
|
||||||
|
|
||||||
|
@Schema(description = "变动类型 1-操作冻结账户余额 2-操作提现账户余额【直接结算】 3-解冻 4-退款")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
@Schema(description = "分销商昵称")
|
@Schema(description = "分销商昵称")
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String nickName;
|
private String nickName;
|
||||||
|
|
||||||
@Schema(description = "订单编号")
|
@Schema(description = "关联订单编号")
|
||||||
private String orderNo;
|
private String orderNo;
|
||||||
|
|
||||||
@Schema(description = "订单状态")
|
@Schema(description = "订单状态")
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private Integer orderStatus;
|
private Integer orderStatus;
|
||||||
|
|
||||||
@Schema(description = "资金流动类型 (10佣金收入 20提现支出 30转账支出 40转账收入 50佣金解冻 60配送奖励)")
|
@Schema(description = "资金流动类型 (10分销收入 11团队管理津贴收入 12分红收入 13现场推广收入 14现场推广分佣 20提现支出 30转账支出 40转账收入 50佣金解冻 60配送奖励 61配送提成 62配送步梯费 70佣金退回【退单】)")
|
||||||
private Integer flowType;
|
private Integer flowType;
|
||||||
|
|
||||||
@Schema(description = "金额")
|
@Schema(description = "变更金额")
|
||||||
private BigDecimal money;
|
private BigDecimal money;
|
||||||
|
|
||||||
|
@Schema(description = "变更后金额")
|
||||||
|
private BigDecimal moneyAfter;
|
||||||
|
|
||||||
|
@Schema(description = "变更冻结金额")
|
||||||
|
private BigDecimal freezeMoney;
|
||||||
|
|
||||||
|
@Schema(description = "变更冻结后金额")
|
||||||
|
private BigDecimal freezeMoneyAfter;
|
||||||
|
|
||||||
@Schema(description = "描述")
|
@Schema(description = "描述")
|
||||||
private String comments;
|
private String comments;
|
||||||
|
|
||||||
@@ -60,9 +77,15 @@ public class ShopDealerCapital implements Serializable {
|
|||||||
@Schema(description = "结算月份")
|
@Schema(description = "结算月份")
|
||||||
private String month;
|
private String month;
|
||||||
|
|
||||||
|
@Schema(description = "结算标识 0-未结算 1-已结算")
|
||||||
|
private Integer paymentFlag;
|
||||||
|
|
||||||
@Schema(description = "商城ID")
|
@Schema(description = "商城ID")
|
||||||
private Integer tenantId;
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
private Integer creator;
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
@Schema(description = "创建时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
@@ -71,4 +94,8 @@ public class ShopDealerCapital implements Serializable {
|
|||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime updateTime;
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "是否删除 0-未删 1-已删")
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
|
|||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
@@ -23,6 +25,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Schema(name = "ShopDealerOrder对象", description = "分销商订单记录表")
|
@Schema(name = "ShopDealerOrder对象", description = "分销商订单记录表")
|
||||||
|
@TableName("shop_dealer_order")
|
||||||
public class ShopDealerOrder implements Serializable {
|
public class ShopDealerOrder implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@@ -66,6 +69,9 @@ public class ShopDealerOrder implements Serializable {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String firstNickname;
|
private String firstNickname;
|
||||||
|
|
||||||
|
@Schema(description = "分销佣金(一级)")
|
||||||
|
private BigDecimal firstMoney;
|
||||||
|
|
||||||
@Schema(description = "分销商用户id(二级)")
|
@Schema(description = "分销商用户id(二级)")
|
||||||
private Integer secondUserId;
|
private Integer secondUserId;
|
||||||
|
|
||||||
@@ -73,42 +79,57 @@ public class ShopDealerOrder implements Serializable {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String secondNickname;
|
private String secondNickname;
|
||||||
|
|
||||||
@Schema(description = "分销商用户id(三级)")
|
|
||||||
private Integer thirdUserId;
|
|
||||||
|
|
||||||
@Schema(description = "分销商用户昵称(三级)")
|
|
||||||
@TableField(exist = false)
|
|
||||||
private String thirdNickname;
|
|
||||||
|
|
||||||
@Schema(description = "分销佣金(一级)")
|
|
||||||
private BigDecimal firstMoney;
|
|
||||||
|
|
||||||
@Schema(description = "分销佣金(二级)")
|
@Schema(description = "分销佣金(二级)")
|
||||||
private BigDecimal secondMoney;
|
private BigDecimal secondMoney;
|
||||||
|
|
||||||
@Schema(description = "分销佣金(三级)")
|
@Schema(description = "分销商用户id(弃用)")
|
||||||
|
private Integer thirdUserId;
|
||||||
|
|
||||||
|
@Schema(description = "分销商用户昵称(弃用)")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String thirdNickname;
|
||||||
|
|
||||||
|
@Schema(description = "分销佣金(弃用)")
|
||||||
private BigDecimal thirdMoney;
|
private BigDecimal thirdMoney;
|
||||||
|
|
||||||
@Schema(description = "门店(一级)")
|
@Schema(description = "一级服务商/门店")
|
||||||
private Integer firstDividendUser;
|
private Integer firstDividendUser;
|
||||||
|
|
||||||
@Schema(description = "门店名称(一级)")
|
@Schema(description = "一级服务商/门店名称")
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String firstDividendUserName;
|
private String firstDividendUserName;
|
||||||
|
|
||||||
@Schema(description = "分红(一级)")
|
@Schema(description = "一级服务商/门店管理津贴")
|
||||||
private BigDecimal firstDividend;
|
private BigDecimal firstDividend;
|
||||||
|
|
||||||
@Schema(description = "门店(二级)")
|
@Schema(description = "一级服务商/门店结算标识 0-否 1-是")
|
||||||
|
private Integer firstDividendFlag;
|
||||||
|
|
||||||
|
@Schema(description = "一级服务商/门店结算单号")
|
||||||
|
private String firstDividendNo;
|
||||||
|
|
||||||
|
@Schema(description = "一级服务商/门店结算时间")
|
||||||
|
private LocalDateTime firstDividendTime;
|
||||||
|
|
||||||
|
@Schema(description = "二级服务商/门店")
|
||||||
private Integer secondDividendUser;
|
private Integer secondDividendUser;
|
||||||
|
|
||||||
@Schema(description = "门店名称(二级)")
|
@Schema(description = "二级服务商/门店名称")
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String secondDividendUserName;
|
private String secondDividendUserName;
|
||||||
|
|
||||||
@Schema(description = "分红(二级)")
|
@Schema(description = "二级服务商/门店管理津贴")
|
||||||
private BigDecimal secondDividend;
|
private BigDecimal secondDividend;
|
||||||
|
|
||||||
|
@Schema(description = "二级服务商/门店结算标识 0-否 1-是")
|
||||||
|
private Integer secondDividendFlag;
|
||||||
|
|
||||||
|
@Schema(description = "二级服务商/门店结算单号")
|
||||||
|
private String secondDividendNo;
|
||||||
|
|
||||||
|
@Schema(description = "二级服务商/门店结算时间")
|
||||||
|
private LocalDateTime secondDividendTime;
|
||||||
|
|
||||||
@Schema(description = "佣金比例")
|
@Schema(description = "佣金比例")
|
||||||
private BigDecimal rate;
|
private BigDecimal rate;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.gxwebsoft.shop.entity;
|
|||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@@ -87,4 +89,16 @@ public class ShopDealerReferee implements Serializable {
|
|||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime updateTime;
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "团队成员")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer teamNum;
|
||||||
|
|
||||||
|
@Schema(description = "订单数")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Integer orderNum;
|
||||||
|
|
||||||
|
@Schema(description = "订单金额")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private BigDecimal orderAmount;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package com.gxwebsoft.shop.entity;
|
package com.gxwebsoft.shop.entity;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
@@ -20,6 +21,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Schema(name = "ShopDealerUser对象", description = "分销商用户记录表")
|
@Schema(name = "ShopDealerUser对象", description = "分销商用户记录表")
|
||||||
|
@TableName("shop_dealer_user")
|
||||||
public class ShopDealerUser implements Serializable {
|
public class ShopDealerUser implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@@ -102,6 +104,9 @@ public class ShopDealerUser implements Serializable {
|
|||||||
@Schema(description = "排序号")
|
@Schema(description = "排序号")
|
||||||
private Integer sortNumber;
|
private Integer sortNumber;
|
||||||
|
|
||||||
|
@Schema(description = "核销权限是否开启 0-未开启 1-已开启")
|
||||||
|
private Boolean verifyFlag;
|
||||||
|
|
||||||
@Schema(description = "是否删除")
|
@Schema(description = "是否删除")
|
||||||
private Integer isDelete;
|
private Integer isDelete;
|
||||||
|
|
||||||
@@ -112,8 +117,18 @@ public class ShopDealerUser implements Serializable {
|
|||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "更新人")
|
||||||
|
private Integer updater;
|
||||||
|
|
||||||
@Schema(description = "修改时间")
|
@Schema(description = "修改时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime updateTime;
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "分销商等级:0-普通用户 1-超级管理员 2-合伙人(总店) 3-合伙人(分店)")
|
||||||
|
private Integer dealerLevel;
|
||||||
|
|
||||||
|
@Schema(description = "删除 0-未删 1-已删")
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ public class ShopDealerWithdraw implements Serializable {
|
|||||||
@TableId(value = "id", type = IdType.AUTO)
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
private Integer id;
|
private Integer id;
|
||||||
|
|
||||||
|
@Schema(description = "订单号")
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
@Schema(description = "分销商用户ID")
|
@Schema(description = "分销商用户ID")
|
||||||
private Integer userId;
|
private Integer userId;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user