diff --git a/.workbuddy/expert-history.json b/.workbuddy/expert-history.json
new file mode 100644
index 0000000..c0f2131
--- /dev/null
+++ b/.workbuddy/expert-history.json
@@ -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
+}
\ No newline at end of file
diff --git a/.workbuddy/memory/2026-04-12.md b/.workbuddy/memory/2026-04-12.md
new file mode 100644
index 0000000..8db737d
--- /dev/null
+++ b/.workbuddy/memory/2026-04-12.md
@@ -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`
+
+**状态**:已修复
diff --git a/.workbuddy/memory/2026-04-13.md b/.workbuddy/memory/2026-04-13.md
new file mode 100644
index 0000000..34250a1
--- /dev/null
+++ b/.workbuddy/memory/2026-04-13.md
@@ -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`
+
+**状态**:已修复
diff --git a/.workbuddy/memory/2026-04-16.md b/.workbuddy/memory/2026-04-16.md
new file mode 100644
index 0000000..deb77d4
--- /dev/null
+++ b/.workbuddy/memory/2026-04-16.md
@@ -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 的 `` 块中没有 `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,避免重复初始化。
diff --git a/.workbuddy/memory/2026-04-18.md b/.workbuddy/memory/2026-04-18.md
new file mode 100644
index 0000000..d8ab728
--- /dev/null
+++ b/.workbuddy/memory/2026-04-18.md
@@ -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
diff --git a/.workbuddy/memory/2026-04-21.md b/.workbuddy/memory/2026-04-21.md
new file mode 100644
index 0000000..7bb7332
--- /dev/null
+++ b/.workbuddy/memory/2026-04-21.md
@@ -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`;新增 `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
+
diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md
new file mode 100644
index 0000000..6343d2e
--- /dev/null
+++ b/.workbuddy/memory/MEMORY.md
@@ -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() 不为空,否则整个任务跳过
diff --git a/.workbuddy/settings.local.json b/.workbuddy/settings.local.json
new file mode 100644
index 0000000..7a6d5dd
--- /dev/null
+++ b/.workbuddy/settings.local.json
@@ -0,0 +1,5 @@
+{
+ "enabledPlugins": {
+ "modern-webapp@cb_teams_marketplace": true
+ }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 67b484f..85b1581 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,10 +4,10 @@
4.0.0
com.gxwebsoft
- glt-api
+ mp-api
1.0
- glt-api
+ mp-api
WebSoftApi project for Spring Boot
diff --git a/sql/glt_ticket_order_delivery_fields.sql b/sql/glt_ticket_order_delivery_fields.sql
new file mode 100644
index 0000000..bbbac31
--- /dev/null
+++ b/sql/glt_ticket_order_delivery_fields.sql
@@ -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;
diff --git a/src/main/java/com/gxwebsoft/cms/entity/CmsNavigation.java b/src/main/java/com/gxwebsoft/cms/entity/CmsNavigation.java
index 449cf54..1170bc8 100644
--- a/src/main/java/com/gxwebsoft/cms/entity/CmsNavigation.java
+++ b/src/main/java/com/gxwebsoft/cms/entity/CmsNavigation.java
@@ -227,6 +227,10 @@ public class CmsNavigation implements Serializable {
@TableField(exist = false)
private String text;
+ @Schema(description = "栏目文章")
+ @TableField(exist = false)
+ private List articles;
+
public String getCategoryName() {
return this.title;
}
diff --git a/src/main/java/com/gxwebsoft/cms/service/impl/CmsNavigationServiceImpl.java b/src/main/java/com/gxwebsoft/cms/service/impl/CmsNavigationServiceImpl.java
index 7a541ba..8cf2af8 100644
--- a/src/main/java/com/gxwebsoft/cms/service/impl/CmsNavigationServiceImpl.java
+++ b/src/main/java/com/gxwebsoft/cms/service/impl/CmsNavigationServiceImpl.java
@@ -4,9 +4,11 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.CmsModel;
import com.gxwebsoft.cms.mapper.CmsNavigationMapper;
+import com.gxwebsoft.cms.service.CmsArticleService;
import com.gxwebsoft.cms.service.CmsDesignService;
import com.gxwebsoft.cms.service.CmsModelService;
import com.gxwebsoft.cms.service.CmsNavigationService;
@@ -38,6 +40,8 @@ public class CmsNavigationServiceImpl extends ServiceImpl pageRel(CmsNavigationParam param) {
@@ -53,6 +57,11 @@ public class CmsNavigationServiceImpl extends ServiceImpl page = new PageParam<>();
page.setDefaultOrder("sort_number asc, position asc,navigation_id asc");
+ list.forEach(item -> {
+ // 关联信息
+ final List articleList = cmsArticleService.list(new LambdaQueryWrapper().eq(CmsArticle::getCategoryId, item.getNavigationId()).last("limit 5"));
+ item.setArticles(articleList);
+ });
return page.sortRecords(list);
}
diff --git a/src/main/java/com/gxwebsoft/common/core/config/CertificateProperties.java b/src/main/java/com/gxwebsoft/common/core/config/CertificateProperties.java
index a13c9d3..e669ee9 100644
--- a/src/main/java/com/gxwebsoft/common/core/config/CertificateProperties.java
+++ b/src/main/java/com/gxwebsoft/common/core/config/CertificateProperties.java
@@ -32,7 +32,7 @@ public class CertificateProperties {
/**
* 开发环境证书路径前缀
*/
- private String devCertPath = "dev";
+ private String devCertPath = "local";
/**
* 微信支付证书配置
diff --git a/src/main/java/com/gxwebsoft/common/core/config/ConfigProperties.java b/src/main/java/com/gxwebsoft/common/core/config/ConfigProperties.java
index d500177..dfee5d3 100644
--- a/src/main/java/com/gxwebsoft/common/core/config/ConfigProperties.java
+++ b/src/main/java/com/gxwebsoft/common/core/config/ConfigProperties.java
@@ -60,7 +60,7 @@ public class ConfigProperties {
/**
* token过期时间, 单位秒
*/
- private Long tokenExpireTime = 60 * 60 * 365 * 24L;
+ private Long tokenExpireTime = 60 * 60 * 30 * 24L;
/**
* token快要过期自动刷新时间, 单位分钟
diff --git a/src/main/java/com/gxwebsoft/common/core/config/MybatisPlusConfig.java b/src/main/java/com/gxwebsoft/common/core/config/MybatisPlusConfig.java
index b3655bc..c5e5f34 100644
--- a/src/main/java/com/gxwebsoft/common/core/config/MybatisPlusConfig.java
+++ b/src/main/java/com/gxwebsoft/common/core/config/MybatisPlusConfig.java
@@ -80,6 +80,7 @@ public class MybatisPlusConfig {
@Override
public boolean ignoreTable(String tableName) {
+// TenantContext.setIgnoreTenant(Boolean.TRUE);
// 如果当前上下文设置了忽略租户隔离,则忽略所有表的租户隔离
if (TenantContext.isIgnoreTenant()) {
return true;
@@ -91,18 +92,18 @@ public class MybatisPlusConfig {
"sys_dictionary",
"sys_dictionary_data",
"apps_test_data",
- "cms_lang"
-// "hjm_car",
-// "hjm_fence"
-// "cms_website"
-// "sys_user"
-// "cms_domain"
-// "shop_order_goods",
-// "shop_goods"
-// "shop_users",
-// "shop_order" // 移除shop_order,改为通过注解控制
-// "shop_order_info",
-// "booking_user_invoice"
+ "cms_lang",
+ "shop_goods",
+ "shop_order" ,
+ "shop_order_goods",
+ "glt_ticket_template",
+ "glt_user_ticket",
+ "glt_user_ticket_release",
+ "glt_user_ticket_log",
+ "shop_dealer_user",
+ "shop_dealer_order",
+ "shop_dealer_referee",
+ "shop_store_rider"
).contains(tableName);
}
};
diff --git a/src/main/java/com/gxwebsoft/common/core/config/SwaggerConfig.java b/src/main/java/com/gxwebsoft/common/core/config/SwaggerConfig.java
index 75b99c8..cb74c29 100644
--- a/src/main/java/com/gxwebsoft/common/core/config/SwaggerConfig.java
+++ b/src/main/java/com/gxwebsoft/common/core/config/SwaggerConfig.java
@@ -87,14 +87,15 @@ public class SwaggerConfig {
}
/**
- * OA 模块分组
+ * Glt 模块分组
*/
@Bean
- public GroupedOpenApi oaApi() {
+ public GroupedOpenApi gltApi() {
return GroupedOpenApi.builder()
- .group("oa")
- .pathsToMatch("/api/oa/**")
- .packagesToScan("com.gxwebsoft.oa")
+ .group("glt")
+ // 订单等用户侧接口在 shop 包内,但路径使用 /api/user/**(前端统一 user 侧 API 前缀)
+ .pathsToMatch("/api/glt/**", "/api/user/**")
+ .packagesToScan("com.gxwebsoft.glt")
.build();
}
diff --git a/src/main/java/com/gxwebsoft/common/core/constants/BaseConstants.java b/src/main/java/com/gxwebsoft/common/core/constants/BaseConstants.java
index 66cf4c0..e389908 100644
--- a/src/main/java/com/gxwebsoft/common/core/constants/BaseConstants.java
+++ b/src/main/java/com/gxwebsoft/common/core/constants/BaseConstants.java
@@ -2,4 +2,7 @@ package com.gxwebsoft.common.core.constants;
public class BaseConstants {
public static final String[] STATUS = {"未定义","显示","隐藏"};
+
+ public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
+
}
diff --git a/src/main/java/com/gxwebsoft/common/core/controller/DatabaseFixController.java b/src/main/java/com/gxwebsoft/common/core/controller/DatabaseFixController.java
index 166e9cd..fdd0ec1 100644
--- a/src/main/java/com/gxwebsoft/common/core/controller/DatabaseFixController.java
+++ b/src/main/java/com/gxwebsoft/common/core/controller/DatabaseFixController.java
@@ -30,7 +30,7 @@ import java.util.Map;
@Tag(name = "数据库修复工具")
@RestController
@RequestMapping("/api/database-fix")
-// @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
+// @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "local")
public class DatabaseFixController extends BaseController {
@Autowired
diff --git a/src/main/java/com/gxwebsoft/common/core/controller/DevEnvironmentController.java b/src/main/java/com/gxwebsoft/common/core/controller/DevEnvironmentController.java
index 8e30a34..c2f5894 100644
--- a/src/main/java/com/gxwebsoft/common/core/controller/DevEnvironmentController.java
+++ b/src/main/java/com/gxwebsoft/common/core/controller/DevEnvironmentController.java
@@ -28,7 +28,7 @@ import java.util.Map;
@Tag(name = "开发环境管理")
@RestController
@RequestMapping("/api/dev")
-// @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "dev")
+// @ConditionalOnProperty(name = "spring.profiles.active", havingValue = "local")
public class DevEnvironmentController extends BaseController {
@Autowired
diff --git a/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerCapitalUpdateEnum.java b/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerCapitalUpdateEnum.java
new file mode 100644
index 0000000..7729632
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerCapitalUpdateEnum.java
@@ -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;
+}
diff --git a/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerTypeEnum.java b/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerTypeEnum.java
new file mode 100644
index 0000000..55fce65
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/common/core/enums/ShopDealerTypeEnum.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/gxwebsoft/common/core/enums/UserTypeEnum.java b/src/main/java/com/gxwebsoft/common/core/enums/UserTypeEnum.java
new file mode 100644
index 0000000..824b7dc
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/common/core/enums/UserTypeEnum.java
@@ -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());
+ }
+
+
+}
diff --git a/src/main/java/com/gxwebsoft/common/core/exception/ErrorCode.java b/src/main/java/com/gxwebsoft/common/core/exception/ErrorCode.java
new file mode 100644
index 0000000..7ce2521
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/common/core/exception/ErrorCode.java
@@ -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;
+ }
+
+}
diff --git a/src/main/java/com/gxwebsoft/common/core/exception/enums/GlobalErrorCodeConstants.java b/src/main/java/com/gxwebsoft/common/core/exception/enums/GlobalErrorCodeConstants.java
new file mode 100644
index 0000000..6f7e49f
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/common/core/exception/enums/GlobalErrorCodeConstants.java
@@ -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, "门店财务账单不存在");
+}
diff --git a/src/main/java/com/gxwebsoft/common/core/service/EnvironmentAwarePaymentService.java b/src/main/java/com/gxwebsoft/common/core/service/EnvironmentAwarePaymentService.java
index 2155cc9..25829f5 100644
--- a/src/main/java/com/gxwebsoft/common/core/service/EnvironmentAwarePaymentService.java
+++ b/src/main/java/com/gxwebsoft/common/core/service/EnvironmentAwarePaymentService.java
@@ -1,5 +1,6 @@
package com.gxwebsoft.common.core.service;
+import cn.hutool.core.util.StrUtil;
import com.gxwebsoft.common.system.entity.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,6 +27,9 @@ public class EnvironmentAwarePaymentService {
@Value("${config.server-url:}")
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}")
private String devNotifyUrl;
@@ -66,15 +70,15 @@ public class EnvironmentAwarePaymentService {
* 根据当前环境获取回调地址
*/
private String getEnvironmentNotifyUrl() {
- if ("dev".equals(activeProfile) || "test".equals(activeProfile)) {
+ if ("local".equals(activeProfile) || "test".equals(activeProfile)) {
// 开发/测试环境使用本地回调地址
return devNotifyUrl;
} else if ("prod".equals(activeProfile)) {
// 生产环境使用生产回调地址
return prodNotifyUrl;
} else {
- // 默认使用配置的服务器地址
- return serverUrl + "/shop/shop-order/notify";
+ // 默认使用 API 网关地址(支付回调需要公网可访问的 API 地址)
+ return (StrUtil.isNotBlank(apiUrl) ? apiUrl : serverUrl) + "/shop/shop-order/notify";
}
}
@@ -131,7 +135,7 @@ public class EnvironmentAwarePaymentService {
* 是否为开发环境
*/
public boolean isDevelopmentEnvironment() {
- return "dev".equals(activeProfile) || "test".equals(activeProfile);
+ return "local".equals(activeProfile) || "test".equals(activeProfile);
}
/**
diff --git a/src/main/java/com/gxwebsoft/common/core/utils/LoginUserUtil.java b/src/main/java/com/gxwebsoft/common/core/utils/LoginUserUtil.java
new file mode 100644
index 0000000..1f5fe8d
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/common/core/utils/LoginUserUtil.java
@@ -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;
+ }
+}
diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java b/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java
index 75b0a22..3f0f432 100644
--- a/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java
+++ b/src/main/java/com/gxwebsoft/common/core/utils/WechatCertAutoConfig.java
@@ -84,7 +84,7 @@ public class WechatCertAutoConfig {
String apiV3Key = "0kF5OlPr482EZwtn9zGufUcqa7ovgxRL";
// 根据环境选择证书路径
- if ("dev".equals(activeProfile)) {
+ if ("local".equals(activeProfile)) {
// 开发环境:使用配置文件upload-path拼接证书路径
String uploadPath = configProperties.getUploadPath(); // 配置文件路径
String tenantId = "10550"; // 租户ID
diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WechatPayCertificateDiagnostic.java b/src/main/java/com/gxwebsoft/common/core/utils/WechatPayCertificateDiagnostic.java
index ebe8189..81e2e9e 100644
--- a/src/main/java/com/gxwebsoft/common/core/utils/WechatPayCertificateDiagnostic.java
+++ b/src/main/java/com/gxwebsoft/common/core/utils/WechatPayCertificateDiagnostic.java
@@ -105,7 +105,7 @@ public class WechatPayCertificateDiagnostic {
* 检查证书文件
*/
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 privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
@@ -152,7 +152,7 @@ public class WechatPayCertificateDiagnostic {
*/
private void validateCertificateContent(Payment payment, Integer tenantId, String environment, DiagnosticResult result) {
try {
- if ("dev".equals(environment)) {
+ if ("local".equals(environment)) {
String tenantCertPath = "dev/wechat/" + tenantId;
String apiclientCertPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getApiclientCertFile();
diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WechatPayCertificateFixer.java b/src/main/java/com/gxwebsoft/common/core/utils/WechatPayCertificateFixer.java
index af6e357..b29918e 100644
--- a/src/main/java/com/gxwebsoft/common/core/utils/WechatPayCertificateFixer.java
+++ b/src/main/java/com/gxwebsoft/common/core/utils/WechatPayCertificateFixer.java
@@ -103,7 +103,7 @@ public class WechatPayCertificateFixer {
* 修复证书文件问题
*/
private void fixCertificateFiles(Payment payment, Integer tenantId, String environment, FixResult result) {
- if ("dev".equals(environment)) {
+ if ("local".equals(environment)) {
fixDevCertificateFiles(tenantId, result);
} else {
fixProdCertificateFiles(payment, result);
@@ -169,7 +169,7 @@ public class WechatPayCertificateFixer {
}
// 在开发环境中,尝试从证书文件中提取序列号进行验证
- if ("dev".equals(environment)) {
+ if ("local".equals(environment)) {
try {
String tenantCertPath = "dev/wechat/" + tenantId;
String apiclientCertPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getApiclientCertFile();
diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WechatPayConfigValidator.java b/src/main/java/com/gxwebsoft/common/core/utils/WechatPayConfigValidator.java
index 23326d2..eb26d62 100644
--- a/src/main/java/com/gxwebsoft/common/core/utils/WechatPayConfigValidator.java
+++ b/src/main/java/com/gxwebsoft/common/core/utils/WechatPayConfigValidator.java
@@ -115,7 +115,7 @@ public class WechatPayConfigValidator {
* 验证证书文件
*/
private void validateCertificateFiles(Integer tenantId, ValidationResult result) {
- if ("dev".equals(activeProfile)) {
+ if ("local".equals(activeProfile)) {
// 开发环境证书验证
String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
@@ -195,7 +195,7 @@ public class WechatPayConfigValidator {
// 证书文件检查
report.append("当前环境: ").append(activeProfile).append("\n");
- if ("dev".equals(activeProfile)) {
+ if ("local".equals(activeProfile)) {
String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
boolean certExists = certificateLoader.certificateExists(privateKeyPath);
diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WechatPayDiagnostic.java b/src/main/java/com/gxwebsoft/common/core/utils/WechatPayDiagnostic.java
index 620d506..a623586 100644
--- a/src/main/java/com/gxwebsoft/common/core/utils/WechatPayDiagnostic.java
+++ b/src/main/java/com/gxwebsoft/common/core/utils/WechatPayDiagnostic.java
@@ -78,7 +78,7 @@ public class WechatPayDiagnostic {
}
// 生产环境检查证书文件
- if (!"dev".equals(environment)) {
+ if (!"local".equals(environment)) {
if (payment.getApiclientCert() != null) {
log.info("商户证书文件配置: {}", payment.getApiclientCert());
}
diff --git a/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java b/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java
index 9ed476a..533bb03 100644
--- a/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java
+++ b/src/main/java/com/gxwebsoft/common/system/controller/WxLoginController.java
@@ -40,6 +40,7 @@ import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
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);
- if(website.getRunning().equals(2)){
+ if(website != null && Integer.valueOf(2).equals(website.getRunning())){
map.put("check_path",false);
map.put("env_version","trial");
}
@@ -725,26 +726,43 @@ public class WxLoginController extends BaseController {
/**
* 从scene参数中提取租户ID
- * scene格式可能是: uid_33103 或其他包含用户ID的格式
+ * scene格式: uid_userId_tenantId(优先)或 uid_userId(兼容旧格式)
*/
private Integer extractTenantIdFromScene(String scene) {
try {
System.out.println("解析scene参数: " + scene);
- // 如果scene包含uid_前缀,提取用户ID
if (scene != null && scene.startsWith("uid_")) {
- String userIdStr = scene.substring(4); // 去掉"uid_"前缀
- Integer userId = Integer.parseInt(userIdStr);
- System.out.println("userId = " + userId);
+ String content = scene.substring(4); // 去掉"uid_"前缀
- // 根据用户ID查询用户信息,获取租户ID
- User user = userService.getByIdIgnoreTenant(userId);
- System.out.println("user = " + user);
- if (user != null) {
- System.out.println("从用户ID " + userId + " 获取到租户ID: " + user.getTenantId());
- return user.getTenantId();
- } else {
- System.err.println("未找到用户ID: " + userId);
+ // 优先解析 uid_userId_tenantId 格式
+ String[] parts = content.split("_");
+ if (parts.length >= 2) {
+ try {
+ Integer tenantId = Integer.parseInt(parts[1]);
+ 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 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 {
+ System.err.println("未找到用户ID: " + userId);
+ }
+ } catch (Exception ex) {
+ System.err.println("查询用户异常: " + ex.getMessage());
+ }
}
}
diff --git a/src/main/java/com/gxwebsoft/common/system/entity/LoginRecord.java b/src/main/java/com/gxwebsoft/common/system/entity/LoginRecord.java
index b516166..a03573a 100644
--- a/src/main/java/com/gxwebsoft/common/system/entity/LoginRecord.java
+++ b/src/main/java/com/gxwebsoft/common/system/entity/LoginRecord.java
@@ -58,11 +58,11 @@ public class LoginRecord implements Serializable {
private Integer tenantId;
@Schema(description = "操作时间")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
@Schema(description = "修改时间")
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime;
@Schema(description = "用户id")
diff --git a/src/main/java/com/gxwebsoft/common/system/entity/User.java b/src/main/java/com/gxwebsoft/common/system/entity/User.java
index ca83722..f4dee75 100644
--- a/src/main/java/com/gxwebsoft/common/system/entity/User.java
+++ b/src/main/java/com/gxwebsoft/common/system/entity/User.java
@@ -262,6 +262,9 @@ public class User implements UserDetails {
@Schema(description = "微信unionid")
private String unionid;
+ @Schema(description = "核销权限是否开启 0-未开启 1-已开启")
+ private Integer verifyFlag;
+
@Schema(description = "关联用户ID")
@TableField(exist = false)
private Integer sysUserId;
diff --git a/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java b/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java
index 41c8be0..47f9746 100644
--- a/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java
+++ b/src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java
@@ -49,6 +49,9 @@ public interface UserMapper extends BaseMapper {
@InterceptorIgnore(tenantLine = "true")
List getOne(@Param("param") UserParam param);
+ @InterceptorIgnore(tenantLine = "true")
+ User getById(@Param("userId") Integer userId);
+
List selectListStatisticsRel(@Param("param") UserParam param);
@InterceptorIgnore(tenantLine = "true")
@@ -60,11 +63,18 @@ public interface UserMapper extends BaseMapper {
* @return User
*/
@InterceptorIgnore(tenantLine = "true")
- User selectByIdIgnoreTenant(@Param("userId") Integer userId);
+ List selectByIdIgnoreTenant(@Param("userId") Integer userId);
@InterceptorIgnore(tenantLine = "true")
List pageAdminByPhone(@Param("param") UserParam param);
@InterceptorIgnore(tenantLine = "true")
List listByAlert();
+
+ /**
+ * 批量查询用户信息
+ * @param userIdList 用户ID集合
+ * @return
+ */
+ List selectByUserIdList(@Param("userIdList") List userIdList);
}
diff --git a/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml b/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml
index c16fae8..d9a1921 100644
--- a/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml
+++ b/src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml
@@ -260,5 +260,25 @@
WHERE a.user_id = #{userId}
AND a.deleted = 0
+
+
diff --git a/src/main/java/com/gxwebsoft/common/system/redis/OrderNoUtils.java b/src/main/java/com/gxwebsoft/common/system/redis/OrderNoUtils.java
new file mode 100644
index 0000000..887f1ca
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/common/system/redis/OrderNoUtils.java
@@ -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;
+ }
+
+}
diff --git a/src/main/java/com/gxwebsoft/common/system/service/UserService.java b/src/main/java/com/gxwebsoft/common/system/service/UserService.java
index 2230d94..427c471 100644
--- a/src/main/java/com/gxwebsoft/common/system/service/UserService.java
+++ b/src/main/java/com/gxwebsoft/common/system/service/UserService.java
@@ -117,6 +117,11 @@ public interface UserService extends IService, UserDetailsService {
*/
User getByIdIgnoreTenant(Integer userId);
+ /**
+ * 根据用户ID查询用户列表(忽略租户隔离)
+ */
+ List listByIdIgnoreTenant(Integer userId);
+
List pageAdminByPhone(UserParam param);
List listByAlert();
diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java
index f72ab1d..e48d7d8 100644
--- a/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java
+++ b/src/main/java/com/gxwebsoft/common/system/service/impl/SettingServiceImpl.java
@@ -143,7 +143,7 @@ public class SettingServiceImpl extends ServiceImpl impl
final String apiV3key = jsonObject.getString("wechatApiKey");
if(config == null){
// 根据环境选择不同的证书路径配置
- if ("dev".equals(activeProfile)) {
+ if ("local".equals(activeProfile)) {
// 开发环境:使用配置文件的upload-path拼接证书路径 - 租户ID 10550
System.out.println("=== 开发环境:使用配置文件upload-path拼接证书路径 ===");
String uploadPath = pathConfig.getUploadPath(); // 获取配置的upload-path
diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/UserServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/UserServiceImpl.java
index b321832..c120579 100644
--- a/src/main/java/com/gxwebsoft/common/system/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/gxwebsoft/common/system/service/impl/UserServiceImpl.java
@@ -2,6 +2,9 @@ package com.gxwebsoft.common.system.service.impl;
import cn.hutool.core.util.ObjectUtil;
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.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -224,6 +227,15 @@ public class UserServiceImpl extends ServiceImpl implements Us
if (userId == null) {
return null;
}
+ List users = baseMapper.selectByIdIgnoreTenant(userId);
+ return users != null && !users.isEmpty() ? users.get(0) : null;
+ }
+
+ @Override
+ public List listByIdIgnoreTenant(Integer userId) {
+ if (userId == null) {
+ return Collections.emptyList();
+ }
return baseMapper.selectByIdIgnoreTenant(userId);
}
diff --git a/src/main/java/com/gxwebsoft/glt/controller/GltTicketOrderController.java b/src/main/java/com/gxwebsoft/glt/controller/GltTicketOrderController.java
index 7fa519e..4c1dc4c 100644
--- a/src/main/java/com/gxwebsoft/glt/controller/GltTicketOrderController.java
+++ b/src/main/java/com/gxwebsoft/glt/controller/GltTicketOrderController.java
@@ -1,15 +1,20 @@
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.exception.BusinessException;
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.core.exception.BusinessException;
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.param.GltTicketOrderDeliveredParam;
import com.gxwebsoft.glt.param.GltTicketOrderParam;
+import com.gxwebsoft.glt.service.GltSubscribeMessageService;
import com.gxwebsoft.glt.service.GltTicketOrderService;
import com.gxwebsoft.shop.entity.ShopStoreRider;
import com.gxwebsoft.shop.entity.ShopUserAddress;
@@ -19,12 +24,14 @@ import com.gxwebsoft.shop.service.ShopUserAddressService;
import cn.hutool.core.util.StrUtil;
import io.swagger.v3.oas.annotations.Operation;
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.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
+import java.util.stream.Collectors;
/**
* 送水订单控制器
@@ -32,6 +39,7 @@ import java.util.List;
* @author 科技小王子
* @since 2026-02-05 18:50:21
*/
+@Slf4j
@Tag(name = "送水订单管理")
@RestController
@RequestMapping("/api/glt/glt-ticket-order")
@@ -44,10 +52,14 @@ public class GltTicketOrderController extends BaseController {
private ShopStoreFenceService shopStoreFenceService;
@Resource
private ShopStoreRiderService shopStoreRiderService;
+ @Resource
+ private GltSubscribeMessageService gltSubscribeMessageService;
+ @Resource
+ private UserMapper userMapper;
@Operation(summary = "分页查询送水订单")
@GetMapping("/page")
- public ApiResult> page(GltTicketOrderParam param) {
+ public ApiResult> page(@ParameterObject GltTicketOrderParam param) {
// 使用关联查询
return success(gltTicketOrderService.pageRel(param));
}
@@ -170,6 +182,71 @@ public class GltTicketOrderController extends BaseController {
return success("下单成功");
}
+ /**
+ * 通知所有在线配送员有新订单
+ */
+ private void notifyRidersOfNewOrder(GltTicketOrder order, Integer tenantId) {
+ if (order == null || tenantId == null) {
+ return;
+ }
+
+ // 查询所有启用且在线的配送员
+ List onlineRiders = shopStoreRiderService.list(
+ new LambdaQueryWrapper()
+ .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 riderUserIds = onlineRiders.stream()
+ .map(ShopStoreRider::getUserId)
+ .filter(id -> id != null && id > 0)
+ .collect(Collectors.toList());
+
+ if (riderUserIds.isEmpty()) {
+ return;
+ }
+
+ // 批量查询配送员的 openId
+ List riders = userMapper.selectList(
+ new LambdaQueryWrapper()
+ .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()")
@Operation(summary = "配送员接单")
@PostMapping("/{id}/accept")
@@ -184,6 +261,13 @@ public class GltTicketOrderController extends BaseController {
return success("接单成功");
}
+ @PreAuthorize("isAuthenticated()")
+ @Operation(summary = "配送员转单")
+ @PostMapping("/transferOrder")
+ public ApiResult transferOrder(@RequestBody GltTransferOrderDto orderDto) {
+ return success(gltTicketOrderService.transferOrder(orderDto));
+ }
+
@PreAuthorize("isAuthenticated()")
@Operation(summary = "配送员开始配送")
@PostMapping("/{id}/start")
@@ -215,6 +299,13 @@ public class GltTicketOrderController extends BaseController {
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()")
@Operation(summary = "用户确认收货")
@PostMapping("/{id}/confirm-receive")
@@ -253,16 +344,14 @@ public class GltTicketOrderController extends BaseController {
if (addr == null) {
return null;
}
- if (StrUtil.isNotBlank(addr.getFullAddress())) {
- return addr.getFullAddress();
- }
// 兼容旧数据:fullAddress 为空时,拼接省市区 + 详细地址
return StrUtil.blankToDefault(
StrUtil.join("",
StrUtil.nullToEmpty(addr.getProvince()),
StrUtil.nullToEmpty(addr.getCity()),
StrUtil.nullToEmpty(addr.getRegion()),
- StrUtil.nullToEmpty(addr.getAddress())
+ StrUtil.nullToEmpty(addr.getAddress()),
+ StrUtil.nullToEmpty(addr.getFullAddress())
),
addr.getAddress()
);
@@ -300,23 +389,14 @@ public class GltTicketOrderController extends BaseController {
}
if (gltTicketOrderService.updateById(gltTicketOrder)) {
- // 后台指派配送员(直接改 riderId)时,同步商城订单为“已发货”(deliveryStatus=20)
- if (gltTicketOrder != null
- && gltTicketOrder.getId() != null
- && gltTicketOrder.getRiderId() != null
- && gltTicketOrder.getRiderId() > 0) {
- gltTicketOrderService.markShopOrderShippedAfterRiderAssigned(
- gltTicketOrder.getId(),
- tenantId,
- gltTicketOrder.getRiderId()
- );
- }
+ gltTicketOrderService.markShopOrderShippedAfterRiderAssigned(gltTicketOrder.getId(), tenantId, gltTicketOrder.getRiderId());
+
// 后台直接改“已完成”(deliveryStatus=40)时,同步商城订单为“已完成”(orderStatus=1)
if (gltTicketOrder != null
&& gltTicketOrder.getId() != null
&& gltTicketOrder.getDeliveryStatus() != null
&& gltTicketOrder.getDeliveryStatus() == GltTicketOrderService.DELIVERY_STATUS_FINISHED) {
- gltTicketOrderService.markShopOrderCompletedAfterTicketFinished(gltTicketOrder.getId(), tenantId);
+ gltTicketOrderService.markShopOrderCompletedAfterTicketFinished(gltTicketOrder.getId());
}
return success("修改成功");
}
diff --git a/src/main/java/com/gxwebsoft/glt/controller/GltTicketTemplateController.java b/src/main/java/com/gxwebsoft/glt/controller/GltTicketTemplateController.java
index fb07151..488e7b9 100644
--- a/src/main/java/com/gxwebsoft/glt/controller/GltTicketTemplateController.java
+++ b/src/main/java/com/gxwebsoft/glt/controller/GltTicketTemplateController.java
@@ -79,6 +79,7 @@ public class GltTicketTemplateController extends BaseController {
User loginUser = getLoginUser();
if (loginUser != null) {
gltTicketTemplate.setUserId(loginUser.getUserId());
+ gltTicketTemplate.setTenantId(loginUser.getTenantId());
}
if (gltTicketTemplateService.save(gltTicketTemplate)) {
return success("添加成功");
diff --git a/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketReleaseController.java b/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketReleaseController.java
index 44198ba..eae605d 100644
--- a/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketReleaseController.java
+++ b/src/main/java/com/gxwebsoft/glt/controller/GltUserTicketReleaseController.java
@@ -1,15 +1,15 @@
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.BatchParam;
+import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
-import com.gxwebsoft.glt.service.GltUserTicketReleaseService;
import com.gxwebsoft.glt.entity.GltUserTicketRelease;
import com.gxwebsoft.glt.param.GltUserTicketReleaseParam;
-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.glt.service.GltUserTicketReleaseService;
+import com.gxwebsoft.glt.service.impl.GltUserTicketAutoReleaseServiceImpl;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -31,6 +31,9 @@ public class GltUserTicketReleaseController extends BaseController {
@Resource
private GltUserTicketReleaseService gltUserTicketReleaseService;
+ @Resource
+ private GltUserTicketAutoReleaseServiceImpl gltUserTicketAutoReleaseService;
+
@PreAuthorize("hasAuthority('glt:gltUserTicketRelease:list')")
@Operation(summary = "分页查询水票释放")
@GetMapping("/page")
@@ -126,4 +129,11 @@ public class GltUserTicketReleaseController extends BaseController {
return fail("删除失败");
}
+ @Operation(summary = "水票释放测试")
+ @PostMapping("/releaseTask")
+ public ApiResult> releaseTask() {
+ gltUserTicketAutoReleaseService.releaseTask();
+ return success(true);
+ }
+
}
diff --git a/src/main/java/com/gxwebsoft/glt/dto/GltTransferOrderDto.java b/src/main/java/com/gxwebsoft/glt/dto/GltTransferOrderDto.java
new file mode 100644
index 0000000..b653b98
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/glt/dto/GltTransferOrderDto.java
@@ -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;
+
+}
+
diff --git a/src/main/java/com/gxwebsoft/glt/dto/NoticeRiderNewOrderDto.java b/src/main/java/com/gxwebsoft/glt/dto/NoticeRiderNewOrderDto.java
new file mode 100644
index 0000000..49d5c31
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/glt/dto/NoticeRiderNewOrderDto.java
@@ -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;
+
+}
+
diff --git a/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java b/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java
index 17bb743..7db3ba1 100644
--- a/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java
+++ b/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java
@@ -1,9 +1,6 @@
package com.gxwebsoft.glt.entity;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -22,17 +19,20 @@ import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "GltTicketOrder对象", description = "送水订单")
+@TableName("glt_ticket_order")
public class GltTicketOrder implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
+ @Schema(description = "订单编号")
+ private String no;
+
@Schema(description = "用户水票ID")
private Integer userTicketId;
- @Schema(description = "订单编号")
- @TableField(exist = false)
+ @Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "订单状态")
@@ -46,6 +46,10 @@ public class GltTicketOrder implements Serializable {
@TableField(exist = false)
private String storeName;
+ @Schema(description = "品名")
+ @TableField(exist = false)
+ private String goodsName;
+
@Schema(description = "门店地址")
@TableField(exist = false)
private String storeAddress;
@@ -191,6 +195,19 @@ public class GltTicketOrder implements Serializable {
@TableField(exist = false)
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 = "排序(数字越小越靠前)")
private Integer sortNumber;
@@ -200,6 +217,9 @@ public class GltTicketOrder implements Serializable {
@Schema(description = "状态, 0正常, 1冻结")
private Integer status;
+ @Schema(description = "水票标识 0-非 1-是")
+ private Integer waterTicketFlag;
+
@Schema(description = "是否删除, 0否, 1是")
@TableLogic
private Integer deleted;
diff --git a/src/main/java/com/gxwebsoft/glt/entity/GltTicketTemplate.java b/src/main/java/com/gxwebsoft/glt/entity/GltTicketTemplate.java
index 95338a9..7fa64b1 100644
--- a/src/main/java/com/gxwebsoft/glt/entity/GltTicketTemplate.java
+++ b/src/main/java/com/gxwebsoft/glt/entity/GltTicketTemplate.java
@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable;
+
+import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -19,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "GltTicketTemplate对象", description = "水票")
+@TableName("glt_ticket_template")
public class GltTicketTemplate implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/gxwebsoft/glt/entity/GltUserTicket.java b/src/main/java/com/gxwebsoft/glt/entity/GltUserTicket.java
index 4a6e8a4..49d89c4 100644
--- a/src/main/java/com/gxwebsoft/glt/entity/GltUserTicket.java
+++ b/src/main/java/com/gxwebsoft/glt/entity/GltUserTicket.java
@@ -1,12 +1,9 @@
package com.gxwebsoft.glt.entity;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
-import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -22,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "GltUserTicket对象", description = "我的水票")
+@TableName("glt_user_ticket")
public class GltUserTicket implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketLog.java b/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketLog.java
index 4c83b91..9ae12b6 100644
--- a/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketLog.java
+++ b/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketLog.java
@@ -1,10 +1,8 @@
package com.gxwebsoft.glt.entity;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.*;
+
import java.time.LocalDateTime;
-import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -20,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "GltUserTicketLog对象", description = "消费日志")
+@TableName("glt_user_ticket_log")
public class GltUserTicketLog implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketRelease.java b/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketRelease.java
index 26131d9..0c3f0b2 100644
--- a/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketRelease.java
+++ b/src/main/java/com/gxwebsoft/glt/entity/GltUserTicketRelease.java
@@ -1,10 +1,8 @@
package com.gxwebsoft.glt.entity;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.*;
+
import java.time.LocalDateTime;
-import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -20,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "GltUserTicketRelease对象", description = "水票释放")
+@TableName("glt_user_ticket_release")
public class GltUserTicketRelease implements Serializable {
private static final long serialVersionUID = 1L;
@@ -27,7 +26,7 @@ public class GltUserTicketRelease implements Serializable {
private Long id;
@Schema(description = "水票ID")
- private Long userTicketId;
+ private Integer userTicketId;
@Schema(description = "用户ID")
private Integer userId;
@@ -57,6 +56,9 @@ public class GltUserTicketRelease implements Serializable {
@Schema(description = "状态")
private Integer status;
+ @Schema(description = "备注")
+ private String remark;
+
@Schema(description = "是否删除, 0否, 1是")
@TableLogic
private Integer deleted;
diff --git a/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketReleaseMapper.java b/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketReleaseMapper.java
index baf5639..d32ecd8 100644
--- a/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketReleaseMapper.java
+++ b/src/main/java/com/gxwebsoft/glt/mapper/GltUserTicketReleaseMapper.java
@@ -37,6 +37,13 @@ public interface GltUserTicketReleaseMapper extends BaseMapper selectListRel(@Param("param") GltUserTicketReleaseParam param);
+ /**
+ * 查询当月待释放水票数据
+ * @param limitNum 查询数量
+ * @return List
+ */
+ List getThisMonthReleaseList(@Param("limitNum") Integer limitNum);
+
/**
* 查询待释放且到期的记录(加行锁,防止多实例重复处理)
*
diff --git a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml
index 8fa4c3c..d3b8c47 100644
--- a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml
+++ b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml
@@ -11,7 +11,7 @@
d.name as receiverName, d.phone as receiverPhone,
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,
- 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
LEFT JOIN shop_store b ON a.store_id = b.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 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
-
AND a.id = #{param.id}
@@ -88,7 +87,7 @@
AND a.create_time <= #{param.createTimeEnd}
- 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})
AND u.phone = #{param.phone}
@@ -103,6 +102,12 @@
OR u.phone LIKE CONCAT('%', #{param.keywords}, '%')
)
+
+ AND a.water_ticket_flag = #{param.waterTicketFlag}
+
+
+ AND o.order_status in (0, 1, 5)
+
diff --git a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketReleaseMapper.xml b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketReleaseMapper.xml
index a8364a9..e9d774c 100644
--- a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketReleaseMapper.xml
+++ b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltUserTicketReleaseMapper.xml
@@ -58,5 +58,23 @@
+
diff --git a/src/main/java/com/gxwebsoft/glt/param/GltTicketOrderParam.java b/src/main/java/com/gxwebsoft/glt/param/GltTicketOrderParam.java
index d05e078..bc64b8f 100644
--- a/src/main/java/com/gxwebsoft/glt/param/GltTicketOrderParam.java
+++ b/src/main/java/com/gxwebsoft/glt/param/GltTicketOrderParam.java
@@ -94,4 +94,7 @@ public class GltTicketOrderParam extends BaseParam {
@QueryField(type = QueryType.EQ)
private Integer orderStatus;
+ @Schema(description = "水票订单标识 0-否 1-是")
+ private Integer waterTicketFlag;
+
}
diff --git a/src/main/java/com/gxwebsoft/glt/service/GltSubscribeMessageService.java b/src/main/java/com/gxwebsoft/glt/service/GltSubscribeMessageService.java
new file mode 100644
index 0000000..7ff3ec5
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/glt/service/GltSubscribeMessageService.java
@@ -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);
+}
diff --git a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java
index 5b4ccae..247f7ab 100644
--- a/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java
+++ b/src/main/java/com/gxwebsoft/glt/service/GltTicketIssueService.java
@@ -1,28 +1,26 @@
package com.gxwebsoft.glt.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.gxwebsoft.glt.entity.GltTicketTemplate;
-import com.gxwebsoft.glt.entity.GltUserTicket;
-import com.gxwebsoft.glt.entity.GltUserTicketLog;
-import com.gxwebsoft.glt.entity.GltUserTicketRelease;
+import com.gxwebsoft.glt.entity.*;
+import com.gxwebsoft.glt.task.DealerOrderSettlement10584Task;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.entity.ShopOrderGoods;
+import com.gxwebsoft.shop.mapper.ShopOrderMapper;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
-import com.gxwebsoft.shop.service.ShopOrderService;
import lombok.RequiredArgsConstructor;
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.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
+import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* 套票发放(从订单生成用户套票 + 释放计划)的业务逻辑。
@@ -37,6 +35,9 @@ import java.util.Set;
@RequiredArgsConstructor
public class GltTicketIssueService {
+ @Resource
+ private GltTicketOrderService gltTicketOrderService;
+
public static final int CHANGE_TYPE_ISSUE = 10;
private enum IssueOutcome {
@@ -46,7 +47,7 @@ public class GltTicketIssueService {
NO_TEMPLATE
}
- private final ShopOrderService shopOrderService;
+ private final ShopOrderMapper shopOrderMapper;
private final ShopOrderGoodsService shopOrderGoodsService;
private final GltTicketTemplateService gltTicketTemplateService;
@@ -54,6 +55,7 @@ public class GltTicketIssueService {
private final GltUserTicketReleaseService gltUserTicketReleaseService;
private final GltUserTicketLogService gltUserTicketLogService;
private final TransactionTemplate transactionTemplate;
+ private final DealerOrderSettlement10584Task dealerOrderSettlement;
/**
* 扫描“今日订单”,执行套票发放。
@@ -84,7 +86,7 @@ public class GltTicketIssueService {
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
LocalDateTime tomorrowStart = todayStart.plusDays(1);
- List orders = shopOrderService.list(
+ List orders = shopOrderMapper.selectList(
new LambdaQueryWrapper()
.eq(ShopOrder::getTenantId, tenantId)
.in(ShopOrder::getFormId, uniqueGoodsIds)
@@ -128,6 +130,74 @@ public class GltTicketIssueService {
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()
+ .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 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 goodsIds, ShopOrder order) {
List goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
if (goodsList == null || goodsList.isEmpty()) {
@@ -155,15 +225,10 @@ public class GltTicketIssueService {
if (shouldCompleteOrder) {
LocalDateTime now = LocalDateTime.now();
// 任务执行完后将订单置为“已完成”,避免后续扫描重复处理(幂等虽可挡住,但会产生大量无意义查询)。
- shopOrderService.update(
- new LambdaUpdateWrapper()
- .eq(ShopOrder::getOrderId, order.getOrderId())
- .eq(ShopOrder::getTenantId, tenantId)
- .eq(ShopOrder::getOrderStatus, 0)
- .set(ShopOrder::getOrderStatus, 1)
- .set(ShopOrder::getHasTakeGift, true)
- .set(ShopOrder::getUpdateTime, now)
- );
+ order.setOrderStatus(1);
+ order.setHasTakeGift(true);
+ order.setUpdateTime(LocalDateTime.now());
+ shopOrderMapper.updateById(order);
}
return issuedCount;
@@ -184,7 +249,7 @@ public class GltTicketIssueService {
// - 这里先对商城订单行加行锁,保证同一订单在同一时刻只会被一个事务处理。
// (注意:需数据库支持 SELECT ... FOR UPDATE,且 shop_order.order_id 为主键/有索引)
if (order.getOrderId() != null) {
- shopOrderService.getOne(
+ shopOrderMapper.selectOne(
new LambdaQueryWrapper()
.eq(ShopOrder::getOrderId, order.getOrderId())
.eq(ShopOrder::getTenantId, tenantId)
@@ -304,12 +369,14 @@ public class GltTicketIssueService {
// 若启用了 releasePeriods 且首期释放时机为“支付成功当刻”,则将首期释放量直接计入可用,
// 避免用户刚购买后短时间内无可用水票;后续期数仍由自动释放任务按 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);
Integer firstQtyObj = first.getReleaseQty();
LocalDateTime firstTime = first.getReleaseTime();
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.setUpdateTime(now);
@@ -376,10 +443,13 @@ public class GltTicketIssueService {
// 首期释放时间
LocalDateTime firstReleaseTime;
+ LocalDateTime referenceTime;
if (Objects.equals(template.getFirstReleaseMode(), 1)) {
firstReleaseTime = nextMonthSameDay(baseTime);
+ referenceTime = firstReleaseTime.withDayOfMonth(1).toLocalDate().atStartOfDay();
} else {
firstReleaseTime = baseTime;
+ referenceTime = firstReleaseTime.withDayOfMonth(1).toLocalDate().atStartOfDay();
}
// 每期释放数量计算
@@ -393,7 +463,11 @@ public class GltTicketIssueService {
if (qty <= 0) {
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;
}
@@ -410,7 +484,11 @@ public class GltTicketIssueService {
break;
}
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;
@@ -422,7 +500,7 @@ public class GltTicketIssueService {
LocalDateTime releaseTime,
LocalDateTime now) {
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.setPeriodNo(periodNo);
r.setReleaseQty(releaseQty);
diff --git a/src/main/java/com/gxwebsoft/glt/service/GltTicketOrderService.java b/src/main/java/com/gxwebsoft/glt/service/GltTicketOrderService.java
index 8590380..48cdf5c 100644
--- a/src/main/java/com/gxwebsoft/glt/service/GltTicketOrderService.java
+++ b/src/main/java/com/gxwebsoft/glt/service/GltTicketOrderService.java
@@ -2,6 +2,7 @@ package com.gxwebsoft.glt.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.glt.dto.GltTransferOrderDto;
import com.gxwebsoft.glt.entity.GltTicketOrder;
import com.gxwebsoft.glt.param.GltTicketOrderParam;
@@ -64,6 +65,13 @@ public interface GltTicketOrderService extends IService {
*/
void accept(Integer id, Integer riderId, Integer tenantId);
+ /**
+ * 配送员转单
+ * @param orderDto
+ * @return
+ */
+ Boolean transferOrder(GltTransferOrderDto orderDto);
+
/**
* 指派/接单成功后,同步关联商城订单发货状态为“已发货”(deliveryStatus=20)。
*
@@ -76,7 +84,7 @@ public interface GltTicketOrderService extends IService {
*
* 用于后台直接改 deliveryStatus=40 等不经过 confirmReceive/autoConfirmTimeout 的兜底同步。
*/
- void markShopOrderCompletedAfterTicketFinished(Integer ticketOrderId, Integer tenantId);
+ void markShopOrderCompletedAfterTicketFinished(Integer id);
/**
* 配送员开始配送:10 -> 20,并写 sendStartTime。
@@ -106,4 +114,10 @@ public interface GltTicketOrderService extends IService {
*/
int autoConfirmTimeout(Integer tenantId, LocalDateTime now, int timeoutHours, int batchSize);
+ /**
+ * 派送订单调度
+ * @return
+ */
+ Boolean dispatchOrder(String orderNo, Integer tenantId);
+
}
diff --git a/src/main/java/com/gxwebsoft/glt/service/GltTicketRevokeService.java b/src/main/java/com/gxwebsoft/glt/service/GltTicketRevokeService.java
index 196be88..af77ad2 100644
--- a/src/main/java/com/gxwebsoft/glt/service/GltTicketRevokeService.java
+++ b/src/main/java/com/gxwebsoft/glt/service/GltTicketRevokeService.java
@@ -133,7 +133,7 @@ public class GltTicketRevokeService {
LambdaUpdateWrapper uw = new LambdaUpdateWrapper()
.eq(GltUserTicketRelease::getTenantId, tenantId)
.eq(GltUserTicketRelease::getDeleted, 0)
- .eq(GltUserTicketRelease::getUserTicketId, userTicketId.longValue())
+ .eq(GltUserTicketRelease::getUserTicketId, userTicketId)
// status 为空时也视为“未完成”
.and(w -> w.ne(GltUserTicketRelease::getStatus, RELEASE_STATUS_DONE)
.or().isNull(GltUserTicketRelease::getStatus))
diff --git a/src/main/java/com/gxwebsoft/glt/service/GltUserTicketAutoReleaseService.java b/src/main/java/com/gxwebsoft/glt/service/GltUserTicketAutoReleaseService.java
index 83a7fb3..188709a 100644
--- a/src/main/java/com/gxwebsoft/glt/service/GltUserTicketAutoReleaseService.java
+++ b/src/main/java/com/gxwebsoft/glt/service/GltUserTicketAutoReleaseService.java
@@ -17,10 +17,8 @@ public interface GltUserTicketAutoReleaseService {
int releaseDue(LocalDateTime now, int batchSize);
/**
- * 释放到期的冻结水票(使用系统当前时间)
+ * 释放到期的冻结水票【当次执行1000条】
*/
- default int releaseDue(int batchSize) {
- return releaseDue(LocalDateTime.now(), batchSize);
- }
+ void releaseTask();
}
diff --git a/src/main/java/com/gxwebsoft/glt/service/impl/GltSubscribeMessageServiceImpl.java b/src/main/java/com/gxwebsoft/glt/service/impl/GltSubscribeMessageServiceImpl.java
new file mode 100644
index 0000000..287fc03
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/glt/service/impl/GltSubscribeMessageServiceImpl.java
@@ -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;
+
+/**
+ * 微信订阅消息服务实现
+ *
+ * 功能:
+ *
+ * - 新订单通知配送员
+ * - 订单状态变更通知
+ *
+ *
+ */
+@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 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 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 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 data) {
+ String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken;
+
+ Map 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;
+ }
+}
diff --git a/src/main/java/com/gxwebsoft/glt/service/impl/GltTicketOrderServiceImpl.java b/src/main/java/com/gxwebsoft/glt/service/impl/GltTicketOrderServiceImpl.java
index cd01711..cc8a5ce 100644
--- a/src/main/java/com/gxwebsoft/glt/service/impl/GltTicketOrderServiceImpl.java
+++ b/src/main/java/com/gxwebsoft/glt/service/impl/GltTicketOrderServiceImpl.java
@@ -1,44 +1,60 @@
package com.gxwebsoft.glt.service.impl;
+import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import 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.enums.GlobalErrorCodeConstants;
+import com.gxwebsoft.common.core.utils.LoginUserUtil;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
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.GltUserTicket;
import com.gxwebsoft.glt.entity.GltUserTicketLog;
import com.gxwebsoft.glt.mapper.GltTicketOrderMapper;
import com.gxwebsoft.glt.mapper.GltUserTicketMapper;
import com.gxwebsoft.glt.param.GltTicketOrderParam;
+import com.gxwebsoft.glt.service.GltSubscribeMessageService;
import com.gxwebsoft.glt.service.GltTicketOrderService;
import com.gxwebsoft.glt.service.GltUserTicketLogService;
import com.gxwebsoft.glt.service.GltUserTicketService;
-import com.gxwebsoft.shop.entity.ShopDealerCapital;
-import com.gxwebsoft.shop.entity.ShopDealerUser;
-import com.gxwebsoft.shop.entity.ShopOrder;
-import com.gxwebsoft.shop.entity.ShopOrderGoods;
+import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
+import com.gxwebsoft.shop.entity.*;
+import com.gxwebsoft.shop.mapper.ShopGoodsMapper;
+import com.gxwebsoft.shop.mapper.ShopStoreRiderMapper;
+import com.gxwebsoft.shop.mapper.ShopUserAddressMapper;
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
import com.gxwebsoft.shop.service.ShopDealerUserService;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService;
+import com.gxwebsoft.shop.vo.ShopOrderGoodsInfoVO;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.util.StringUtils;
+import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
+import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
/**
* 送水订单Service实现
@@ -83,11 +99,54 @@ public class GltTicketOrderServiceImpl extends ServiceImpl pageRel(GltTicketOrderParam param) {
PageParam page = new PageParam<>(param);
- page.setDefaultOrder("sort_number asc, create_time desc");
+ page.setDefaultOrder("create_time desc");
List list = baseMapper.selectPageRel(page, param);
+ if(CollectionUtils.isNotEmpty(list)){
+ List addressIdList = list.stream().map(GltTicketOrder::getAddressId).distinct().collect(Collectors.toList());
+ List orderNoList = list.stream().filter(gltTicketOrder -> StrUtil.isNotBlank(gltTicketOrder.getOrderNo())).map(GltTicketOrder::getOrderNo).distinct().collect(Collectors.toList());
+ List orderGoodsInfoVOList = new ArrayList<>();
+ if(CollectionUtils.isNotEmpty(orderNoList)){
+ orderGoodsInfoVOList = shopOrderService.getOrderGoodsInfoByOrderNos(orderNoList);
+ }
+
+ List userAddressList = shopUserAddressMapper.selectBatchIds(addressIdList);
+ List 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());
}
@@ -116,6 +175,10 @@ public class GltTicketOrderServiceImpl extends ServiceImpl 20已发货
- updateShopOrderDeliveryStatusAfterAccept(id, tenantId, riderId, now);
+ updateShopOrderDeliveryStatusAfterAcceptV2(id, tenantId, riderId, now);
return;
}
@@ -294,13 +384,34 @@ public class GltTicketOrderServiceImpl extends ServiceImpl 0;
+ }else {
+ throw new BusinessException(GlobalErrorCodeConstants.NOT_FOUND.getMsg());
+ }
}
@Override
- public void markShopOrderCompletedAfterTicketFinished(Integer ticketOrderId, Integer tenantId) {
- updateShopOrderOrderStatusAfterTicketFinished(ticketOrderId, tenantId, LocalDateTime.now());
+ public void markShopOrderShippedAfterRiderAssigned(Integer ticketOrderId, Integer tenantId, Integer riderId) {
+ 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) {
@@ -370,22 +481,6 @@ public class GltTicketOrderServiceImpl extends ServiceImpl backfill = new LambdaUpdateWrapper()
- .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 uw = new LambdaUpdateWrapper()
@@ -433,6 +528,20 @@ public class GltTicketOrderServiceImpl extends ServiceImpl shopStoreRiders = shopStoreRiderMapper.selectList(new LambdaQueryWrapper().select(ShopStoreRider::getUserId).in(ShopStoreRider::getWorkStatus, Arrays.asList(1, 2)));
+ if(CollectionUtils.isNotEmpty(shopStoreRiders)){
+ List 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) {
return;
}
@@ -666,7 +848,8 @@ public class GltTicketOrderServiceImpl extends ServiceImpl 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 uw = new LambdaUpdateWrapper()
.eq(ShopOrder::getTenantId, tenantId)
.eq(ShopOrder::getDeleted, 0)
@@ -753,7 +1010,6 @@ public class GltTicketOrderServiceImpl extends ServiceImpl 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) {
if (ticketOrderId == null || tenantId == null) {
return;
@@ -845,13 +1199,13 @@ public class GltTicketOrderServiceImpl extends ServiceImpl()
.eq(ShopDealerUser::getTenantId, tenantId)
.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())
.set(ShopDealerUser::getUpdateTime, now)
);
@@ -878,7 +1232,8 @@ public class GltTicketOrderServiceImpl extends ServiceImpl sysUsers = userMapper.selectByIdIgnoreTenant(riderId);
+ User sysUser = (sysUsers != null && !sysUsers.isEmpty()) ? sysUsers.get(0) : null;
if (sysUser != null) {
newDealerUser.setRealName(sysUser.getRealName() != null ? sysUser.getRealName() : sysUser.getNickname());
newDealerUser.setMobile(sysUser.getPhone());
@@ -895,7 +1250,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl()
.eq(ShopDealerUser::getTenantId, tenantId)
.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())
.set(ShopDealerUser::getUpdateTime, now)
);
diff --git a/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketAutoReleaseServiceImpl.java b/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketAutoReleaseServiceImpl.java
index fb576e4..9a7d894 100644
--- a/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketAutoReleaseServiceImpl.java
+++ b/src/main/java/com/gxwebsoft/glt/service/impl/GltUserTicketAutoReleaseServiceImpl.java
@@ -1,5 +1,6 @@
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.GltUserTicketLog;
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.GltUserTicketReleaseMapper;
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 org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
/**
* 冻结水票自动释放实现:
@@ -23,7 +31,7 @@ import java.util.List;
*/
@Slf4j
@Service
-@RequiredArgsConstructor
+@AllArgsConstructor
public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoReleaseService {
/**
@@ -44,6 +52,13 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel
private final GltUserTicketMapper userTicketMapper;
private final GltUserTicketLogMapper userTicketLogMapper;
+ private GltUserTicketReleaseService gltUserTicketReleaseService;
+
+ private GltUserTicketService gltUserTicketService;
+
+ private GltUserTicketLogService gltUserTicketLogService;
+
+
@Override
@Transactional(rollbackFor = Exception.class)
public int releaseDue(LocalDateTime now, int batchSize) {
@@ -71,12 +86,7 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel
continue;
}
- long userTicketIdLong = rel.getUserTicketId();
- if (userTicketIdLong > Integer.MAX_VALUE || userTicketIdLong < 1) {
- markFailed(rel.getId(), now, "userTicketId超范围");
- continue;
- }
- Integer userTicketId = (int) userTicketIdLong;
+ Integer userTicketId = rel.getUserTicketId();
// 先释放冻结数量(条件更新,确保 frozen_qty >= qty)
int updated = userTicketMapper.releaseFrozenQty(
@@ -129,4 +139,96 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel
releaseMapper.updateStatus(releaseId, RELEASE_STATUS_FAILED, now);
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 releaseList = releaseMapper.getThisMonthReleaseList(1000);
+ if(CollectionUtils.isEmpty(releaseList)){
+ log.info("***本轮任务无待释放水票数据***");
+ return;
+ }
+
+ //查询用户水票信息
+ List userTickIdList = releaseList.stream().map(GltUserTicketRelease::getUserTicketId).distinct().collect(Collectors.toList());
+ LambdaQueryWrapper userTicketLambdaQueryWrapper = new LambdaQueryWrapper().select(GltUserTicket::getId, GltUserTicket::getAvailableQty,
+ GltUserTicket::getFrozenQty, GltUserTicket::getReleasedQty).in(GltUserTicket::getId, userTickIdList);
+ List userTicketList = userTicketMapper.selectList(userTicketLambdaQueryWrapper);
+
+ //创建修改用户水票集合、水票释放计划记录集合
+ List updateUserTicketList = new ArrayList<>();
+ List 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);
+ }
}
diff --git a/src/main/java/com/gxwebsoft/glt/task/DealerCommissionUnfreeze10584Task.java b/src/main/java/com/gxwebsoft/glt/task/DealerCommissionUnfreeze10584Task.java
index 1791b86..ac80e0e 100644
--- a/src/main/java/com/gxwebsoft/glt/task/DealerCommissionUnfreeze10584Task.java
+++ b/src/main/java/com/gxwebsoft/glt/task/DealerCommissionUnfreeze10584Task.java
@@ -33,6 +33,7 @@ import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -48,7 +49,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* 1) 送水套餐(formId in 水票模板 goodsId):订单号关联的水票产生了第一次送水订单,且该第一次送水订单状态=已完成(40) -> 解冻。
* 2) 非送水套餐(formId not in 水票模板 goodsId):订单已确认收货(orderStatus=1) -> 解冻。
*
- * 实现策略:以 ShopDealerCapital(flowType=10) 的“佣金明细”为解冻粒度,
+ *
实现策略:以 ShopDealerCapital(flowType=10) 的"佣金明细"为解冻粒度,
* 每条佣金明细对应生成一条 ShopDealerCapital(flowType=50) 作为幂等标记,并执行
* ShopDealerUser.freezeMoney -> ShopDealerUser.money 的转移。
*/
@@ -76,7 +77,7 @@ public class DealerCommissionUnfreeze10584Task {
if (rawRate == null || rawRate.signum() <= 0) {
return null;
}
- // 如果录入 >= 1,按“百分比”处理(1 => 1%)
+ // 如果录入 >= 1,按"百分比"处理(1 => 1%)
if (rawRate.compareTo(BigDecimal.ONE) >= 0) {
return rawRate.movePointLeft(2);
}
@@ -115,7 +116,7 @@ public class DealerCommissionUnfreeze10584Task {
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 精确过滤")
public void run() {
if (!running.compareAndSet(false, true)) {
@@ -124,46 +125,77 @@ public class DealerCommissionUnfreeze10584Task {
}
try {
+ // ========== 步骤1: 加载水票模板 ==========
Set waterFormIds = loadWaterFormIds();
+ log.info("【步骤1】加载水票模板 - tenantId={}, waterFormIds={}", TENANT_ID, waterFormIds);
if (waterFormIds.isEmpty()) {
// 送水/非送水的判断依赖模板 goodsId;拿不到会导致误判,宁可跳过本轮。
log.warn("分销佣金解冻任务跳过:未找到水票模板 goodsId - tenantId={}", TENANT_ID);
return;
}
- // 先按“最近确认收货”的订单扫描,避免总是卡在很早的历史订单上。
+ // ========== 步骤2: 扫描非送水订单(优先最新) ==========
Set eligibleOrderNos = new HashSet<>();
- eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(waterFormIds, true));
- eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds));
+ List nonWaterOrders = findEligibleNonWaterOrderNos(waterFormIds, true);
+ log.info("【步骤2】扫描非送水订单(最新优先)- tenantId={}, count={}, orderNos={}", TENANT_ID, nonWaterOrders.size(), nonWaterOrders.size() <= 20 ? nonWaterOrders : nonWaterOrders.subList(0, 20));
+ eligibleOrderNos.addAll(nonWaterOrders);
+
+ // ========== 步骤3: 扫描送水订单(第一条送水完成) ==========
+ Set waterOrders = findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds);
+ log.info("【步骤3】扫描送水订单(第一条送水完成)- tenantId={}, count={}, orderNos={}", TENANT_ID, waterOrders.size(), waterOrders);
+ eligibleOrderNos.addAll(waterOrders);
if (eligibleOrderNos.isEmpty()) {
+ log.info("【步骤4-9】无可处理订单,本轮结束 - tenantId={}", TENANT_ID);
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;
+ List rewardedOrders = new ArrayList<>();
for (String orderNo : eligibleOrderNos) {
try {
if (settleDeliveryRewardIfNeeded(orderNo)) {
rewarded++;
+ rewardedOrders.add(orderNo);
}
} catch (Exception 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 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()) {
- // 若本轮没有取到佣金明细,回退再按“最早确认收货”的订单扫一轮,尽量覆盖历史遗留未解冻。
+ // ========== 步骤6.1: 兜底扫描历史订单 ==========
+ log.info("【步骤6.1】本轮未取到佣金明细,执行兜底扫描(最早确认收货) - tenantId={}", TENANT_ID);
eligibleOrderNos.clear();
- eligibleOrderNos.addAll(findEligibleNonWaterOrderNos(waterFormIds, false));
- eligibleOrderNos.addAll(findEligibleWaterOrderNosByFirstFinishedTicketOrder(waterFormIds));
+ List fallbackNonWater = findEligibleNonWaterOrderNos(waterFormIds, false);
+ log.info("【步骤6.1】兜底-非送水订单 - tenantId={}, count={}", TENANT_ID, fallbackNonWater.size());
+ eligibleOrderNos.addAll(fallbackNonWater);
+
+ Set 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) {
try {
if (settleDeliveryRewardIfNeeded(orderNo)) {
rewarded++;
+ rewardedOrders.add(orderNo + "(兜底)");
}
} catch (Exception e) {
log.error("发放配送奖励失败,将在下次任务重试 - tenantId={}, orderNo={}", TENANT_ID, orderNo, e);
@@ -171,32 +203,41 @@ public class DealerCommissionUnfreeze10584Task {
}
capitals = findCapitalsByEligibleOrderNos(eligibleOrderNos);
+ log.info("【步骤6.1】兜底扫描到佣金明细 - tenantId={}, count={}", TENANT_ID, capitals.size());
}
if (capitals.isEmpty()) {
+ log.info("【步骤7-9】仍未查到佣金明细,本轮结束 - tenantId={}", TENANT_ID);
return;
}
+ // ========== 步骤7: 执行佣金解冻 ==========
+ log.info("【步骤7】开始执行佣金解冻 - tenantId={}, totalCount={}", TENANT_ID, capitals.size());
int unfrozen = 0;
+ List unfrozenDetails = new ArrayList<>();
for (ShopDealerCapital cap : capitals) {
try {
boolean ok = unfreezeOneCapitalIfNeeded(cap);
if (ok) {
unfrozen++;
+ unfrozenDetails.add("capitalId=" + cap.getId() + ", orderNo=" + cap.getOrderNo() + ", amount=" + cap.getMoney());
}
} catch (Exception e) {
log.error("解冻佣金失败,将在下次任务重试 - tenantId={}, capitalId={}, orderNo={}, userId={}",
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) {
- log.info("分销佣金解冻完成 - tenantId={}, eligibleOrderNos={}, scannedCapitals={}, unfrozen={}",
- TENANT_ID, eligibleOrderNos.size(), capitals.size(), unfrozen);
- }
- if (rewarded > 0) {
- log.info("配送奖励发放完成 - tenantId={}, eligibleOrderNos={}, rewarded={}", TENANT_ID, eligibleOrderNos.size(), rewarded);
- }
+ // ========== 步骤8: 更新分销订单状态 ==========
+ log.info("【步骤8】检查并更新分销订单状态(isUnfreeze=1)- tenantId={}, unfrozenCount={}", TENANT_ID, unfrozen);
+
+ // ========== 步骤9: 汇总报告 ==========
+ log.info("========================================");
+ log.info("【步骤9】本轮任务执行完毕 - tenantId={}", TENANT_ID);
+ log.info(" - 配送奖励: rewarded={}, orders={}", rewarded, rewardedOrders);
+ log.info(" - 佣金解冻: unfrozen={}, details={}", unfrozen, unfrozenDetails);
+ log.info("========================================");
} finally {
running.set(false);
}
@@ -204,8 +245,10 @@ public class DealerCommissionUnfreeze10584Task {
private boolean settleDeliveryRewardIfNeeded(String orderNo) {
if (orderNo == null || orderNo.isBlank()) {
+ log.debug("【步骤5.X】配送奖励跳过:orderNo为空 - tenantId={}", TENANT_ID);
return false;
}
+ log.debug("【步骤5.X】开始处理配送奖励 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
ShopOrder order = shopOrderService.getOne(
new LambdaQueryWrapper()
@@ -215,13 +258,16 @@ public class DealerCommissionUnfreeze10584Task {
.last("limit 1")
);
if (order == null) {
+ log.debug("【步骤5.X】配送奖励跳过:订单不存在 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
return false;
}
Integer riderId = order.getRiderId();
if (riderId == null || riderId <= 0) {
+ log.debug("【步骤5.X】配送奖励跳过:无配送员 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
return false;
}
+ log.debug("【步骤5.X】找到配送员 - tenantId={}, orderNo={}, riderId={}", TENANT_ID, orderNo, riderId);
// 快速幂等检查:已发放则跳过(事务内仍会二次校验避免并发重复)
boolean already = shopDealerCapitalService.count(
@@ -231,147 +277,162 @@ public class DealerCommissionUnfreeze10584Task {
.eq(ShopDealerCapital::getOrderNo, orderNo)
) > 0;
if (already) {
+ log.debug("【步骤5.X】配送奖励跳过:已发放(幂等)- tenantId={}, orderNo={}", TENANT_ID, orderNo);
return false;
}
+ log.debug("【步骤5.X】进入事务 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
- return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
- LocalDateTime now = LocalDateTime.now();
+ try {
+ return Boolean.TRUE.equals(transactionTemplate.execute(status -> {
+ LocalDateTime now = LocalDateTime.now();
+ log.debug("【步骤5.X】事务内开始 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
- // 锁定配送员资金明细 marker,确保并发幂等
- ShopDealerCapital existedMarker = shopDealerCapitalService.getOne(
- new LambdaQueryWrapper()
- .eq(ShopDealerCapital::getTenantId, TENANT_ID)
- .eq(ShopDealerCapital::getFlowType, FLOW_TYPE_DELIVERY_REWARD)
- .eq(ShopDealerCapital::getOrderNo, orderNo)
- .last("limit 1 for update")
- );
- if (existedMarker != null) {
- return false;
- }
-
- Integer orderId = order.getOrderId();
- if (orderId == null) {
- return false;
- }
-
- List orderGoodsList = shopOrderGoodsService.list(
- new LambdaQueryWrapper()
- .eq(ShopOrderGoods::getTenantId, TENANT_ID)
- .eq(ShopOrderGoods::getOrderId, orderId)
- );
- if (orderGoodsList == null || orderGoodsList.isEmpty()) {
- return false;
- }
-
- List goodsIds = orderGoodsList.stream()
- .map(ShopOrderGoods::getGoodsId)
- .filter(Objects::nonNull)
- .distinct()
- .toList();
- if (goodsIds.isEmpty()) {
- return false;
- }
-
- Map goodsDeliveryMoneyMap = shopGoodsService.list(
- new LambdaQueryWrapper()
- .eq(ShopGoods::getTenantId, TENANT_ID)
- .in(ShopGoods::getGoodsId, goodsIds)
- ).stream().collect(java.util.stream.Collectors.toMap(
- ShopGoods::getGoodsId,
- g -> g.getDeliveryMoney() != null ? g.getDeliveryMoney() : BigDecimal.ZERO,
- (a, b) -> a
- ));
-
- BigDecimal reward = BigDecimal.ZERO;
- for (ShopOrderGoods og : orderGoodsList) {
- Integer goodsId = og.getGoodsId();
- if (goodsId == null) {
- continue;
+ // 锁定配送员资金明细 marker,确保并发幂等
+ ShopDealerCapital existedMarker = shopDealerCapitalService.getOne(
+ new LambdaQueryWrapper()
+ .eq(ShopDealerCapital::getTenantId, TENANT_ID)
+ .eq(ShopDealerCapital::getFlowType, FLOW_TYPE_DELIVERY_REWARD)
+ .eq(ShopDealerCapital::getOrderNo, orderNo)
+ .last("limit 1 for update")
+ );
+ if (existedMarker != null) {
+ log.debug("【步骤5.X】配送奖励跳过:事务内检测已存在 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
+ return false;
}
- int qty = og.getTotalNum() == null ? 0 : og.getTotalNum();
- if (qty <= 0) {
- continue;
- }
- BigDecimal rawRate = goodsDeliveryMoneyMap.getOrDefault(goodsId, BigDecimal.ZERO);
- BigDecimal rate = normalizeDeliveryRate(rawRate);
- if (rate == null || rate.signum() <= 0) {
- continue;
- }
- BigDecimal unitPrice = og.getPrice() != null ? og.getPrice() : BigDecimal.ZERO;
- if (unitPrice.signum() <= 0) {
- continue;
- }
- BigDecimal lineAmount = unitPrice.multiply(BigDecimal.valueOf(qty));
- reward = reward.add(lineAmount.multiply(rate));
- }
- reward = reward.setScale(2, RoundingMode.HALF_UP);
- if (reward.signum() <= 0) {
- return false;
- }
+ Integer orderId = order.getOrderId();
+ if (orderId == null) {
+ log.debug("【步骤5.X】配送奖励跳过:orderId为空 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
+ return false;
+ }
- // 锁定/创建配送员分销账户
- ShopDealerUser dealerUser = shopDealerUserService.getOne(
- new LambdaQueryWrapper()
- .eq(ShopDealerUser::getTenantId, TENANT_ID)
- .eq(ShopDealerUser::getUserId, riderId)
- .last("limit 1 for update")
- );
- if (dealerUser == null) {
- ShopDealerUser newDealerUser = new ShopDealerUser();
- newDealerUser.setTenantId(TENANT_ID);
- newDealerUser.setUserId(riderId);
- newDealerUser.setType(0);
- newDealerUser.setIsDelete(0);
- newDealerUser.setSortNumber(0);
- newDealerUser.setFirstNum(0);
- newDealerUser.setSecondNum(0);
- newDealerUser.setThirdNum(0);
- newDealerUser.setMoney(BigDecimal.ZERO);
- newDealerUser.setFreezeMoney(BigDecimal.ZERO);
- newDealerUser.setTotalMoney(BigDecimal.ZERO);
- newDealerUser.setCreateTime(now);
- newDealerUser.setUpdateTime(now);
- shopDealerUserService.save(newDealerUser);
+ List orderGoodsList = shopOrderGoodsService.list(
+ new LambdaQueryWrapper()
+ .eq(ShopOrderGoods::getTenantId, TENANT_ID)
+ .eq(ShopOrderGoods::getOrderId, orderId)
+ );
+ if (orderGoodsList == null || orderGoodsList.isEmpty()) {
+ log.debug("【步骤5.X】配送奖励跳过:订单商品为空 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
+ return false;
+ }
- dealerUser = shopDealerUserService.getOne(
+ List goodsIds = orderGoodsList.stream()
+ .map(ShopOrderGoods::getGoodsId)
+ .filter(Objects::nonNull)
+ .distinct()
+ .toList();
+ if (goodsIds.isEmpty()) {
+ log.debug("【步骤5.X】配送奖励跳过:商品ID列表为空 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
+ return false;
+ }
+
+ Map goodsDeliveryMoneyMap = shopGoodsService.list(
+ new LambdaQueryWrapper()
+ .eq(ShopGoods::getTenantId, TENANT_ID)
+ .in(ShopGoods::getGoodsId, goodsIds)
+ ).stream().collect(java.util.stream.Collectors.toMap(
+ ShopGoods::getGoodsId,
+ g -> g.getDeliveryMoney() != null ? g.getDeliveryMoney() : BigDecimal.ZERO,
+ (a, b) -> a
+ ));
+
+ BigDecimal reward = BigDecimal.ZERO;
+ for (ShopOrderGoods og : orderGoodsList) {
+ Integer goodsId = og.getGoodsId();
+ if (goodsId == null) {
+ continue;
+ }
+ int qty = og.getTotalNum() == null ? 0 : og.getTotalNum();
+ if (qty <= 0) {
+ continue;
+ }
+ BigDecimal rawRate = goodsDeliveryMoneyMap.getOrDefault(goodsId, BigDecimal.ZERO);
+ BigDecimal rate = normalizeDeliveryRate(rawRate);
+ if (rate == null || rate.signum() <= 0) {
+ continue;
+ }
+ BigDecimal unitPrice = og.getPrice() != null ? og.getPrice() : BigDecimal.ZERO;
+ if (unitPrice.signum() <= 0) {
+ continue;
+ }
+ BigDecimal lineAmount = unitPrice.multiply(BigDecimal.valueOf(qty));
+ reward = reward.add(lineAmount.multiply(rate));
+ }
+
+ reward = reward.setScale(2, RoundingMode.HALF_UP);
+ if (reward.signum() <= 0) {
+ log.debug("【步骤5.X】配送奖励跳过:计算金额为0 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
+ return false;
+ }
+ log.debug("【步骤5.X】计算配送奖励 - tenantId={}, orderNo={}, reward={}", TENANT_ID, orderNo, reward);
+
+ // 锁定/创建配送员分销账户
+ ShopDealerUser dealerUser = shopDealerUserService.getOne(
new LambdaQueryWrapper()
.eq(ShopDealerUser::getTenantId, TENANT_ID)
.eq(ShopDealerUser::getUserId, riderId)
.last("limit 1 for update")
);
if (dealerUser == null) {
- log.warn("配送奖励入账失败:未找到/创建分销账户 - tenantId={}, orderNo={}, riderId={}", TENANT_ID, orderNo, riderId);
+ log.info("【步骤5.X】创建配送员分销账户 - tenantId={}, orderNo={}, riderId={}", TENANT_ID, orderNo, riderId);
+ ShopDealerUser newDealerUser = new ShopDealerUser();
+ newDealerUser.setTenantId(TENANT_ID);
+ newDealerUser.setUserId(riderId);
+ newDealerUser.setType(0);
+ newDealerUser.setIsDelete(0);
+ newDealerUser.setSortNumber(0);
+ newDealerUser.setFirstNum(0);
+ newDealerUser.setSecondNum(0);
+ newDealerUser.setThirdNum(0);
+ newDealerUser.setMoney(BigDecimal.ZERO);
+ newDealerUser.setFreezeMoney(BigDecimal.ZERO);
+ newDealerUser.setTotalMoney(BigDecimal.ZERO);
+ newDealerUser.setCreateTime(now);
+ newDealerUser.setUpdateTime(now);
+ shopDealerUserService.save(newDealerUser);
+
+ dealerUser = shopDealerUserService.getOne(
+ new LambdaQueryWrapper()
+ .eq(ShopDealerUser::getTenantId, TENANT_ID)
+ .eq(ShopDealerUser::getUserId, riderId)
+ .last("limit 1 for update")
+ );
+ if (dealerUser == null) {
+ log.warn("配送奖励入账失败:未找到/创建分销账户 - tenantId={}, orderNo={}, riderId={}", TENANT_ID, orderNo, riderId);
+ return false;
+ }
+ }
+
+ BigDecimal moneyVal = dealerUser.getMoney() != null ? dealerUser.getMoney() : BigDecimal.ZERO;
+ BigDecimal totalMoneyVal = dealerUser.getTotalMoney() != null ? dealerUser.getTotalMoney() : BigDecimal.ZERO;
+ dealerUser.setMoney(moneyVal.add(reward));
+ dealerUser.setTotalMoney(totalMoneyVal.add(reward));
+ dealerUser.setUpdateTime(now);
+ if (!shopDealerUserService.updateById(dealerUser)) {
+ log.warn("配送奖励入账失败:更新分销账户失败 - tenantId={}, orderNo={}, riderId={}, reward={}", TENANT_ID, orderNo, riderId, reward);
return false;
}
- }
- BigDecimal moneyVal = dealerUser.getMoney() != null ? dealerUser.getMoney() : BigDecimal.ZERO;
- BigDecimal totalMoneyVal = dealerUser.getTotalMoney() != null ? dealerUser.getTotalMoney() : BigDecimal.ZERO;
- dealerUser.setMoney(moneyVal.add(reward));
- dealerUser.setTotalMoney(totalMoneyVal.add(reward));
- dealerUser.setUpdateTime(now);
- if (!shopDealerUserService.updateById(dealerUser)) {
- log.warn("配送奖励入账失败:更新分销账户失败 - tenantId={}, orderNo={}, riderId={}, reward={}", TENANT_ID, orderNo, riderId, reward);
- return false;
- }
+ ShopDealerCapital cap = new ShopDealerCapital();
+ cap.setUserId(riderId);
+ cap.setOrderNo(orderNo);
+ cap.setFlowType(FLOW_TYPE_DELIVERY_REWARD);
+ cap.setMoney(reward);
+ cap.setComments("配送奖励");
+ cap.setToUserId(order.getUserId());
+ cap.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
+ cap.setTenantId(TENANT_ID);
+ cap.setCreateTime(now);
+ cap.setUpdateTime(now);
+ shopDealerCapitalService.save(cap);
- ShopDealerCapital cap = new ShopDealerCapital();
- cap.setUserId(riderId);
- cap.setOrderNo(orderNo);
- cap.setFlowType(FLOW_TYPE_DELIVERY_REWARD);
- cap.setMoney(reward);
- cap.setComments("配送奖励");
- cap.setToUserId(order.getUserId());
- cap.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
- cap.setTenantId(TENANT_ID);
- cap.setCreateTime(now);
- cap.setUpdateTime(now);
- shopDealerCapitalService.save(cap);
-
- log.info("配送奖励发放成功 - tenantId={}, orderNo={}, riderId={}, reward={}", TENANT_ID, orderNo, riderId, reward);
- return true;
- }));
+ log.info("【步骤5.X】配送奖励发放成功 - tenantId={}, orderNo={}, riderId={}, reward={}", TENANT_ID, orderNo, riderId, reward);
+ return true;
+ }));
+ } catch (Exception e) {
+ log.error("【步骤5.X】配送奖励发放异常 - tenantId={}, orderNo={}, error={}", TENANT_ID, orderNo, e.getMessage(), e);
+ return false;
+ }
}
private Set loadWaterFormIds() {
@@ -408,10 +469,11 @@ public class DealerCommissionUnfreeze10584Task {
}
private Set findEligibleWaterOrderNosByFirstFinishedTicketOrder(Set waterFormIds) {
- // 缓存减少 DB 往返:userTicketId -> 是否“第一次送水单已完成”
+ // 缓存减少 DB 往返:userTicketId -> 是否"第一次送水单已完成"
Map firstFinishedCache = new HashMap<>();
Map userTicketOrderNoCache = new HashMap<>();
+ log.info("【步骤3.1】查询已完成的送水订单 - tenantId={}, limit={}", TENANT_ID, MAX_ELIGIBLE_TICKET_ORDERS_PER_RUN);
List finishedTicketOrders = gltTicketOrderService.list(
new LambdaQueryWrapper()
.eq(GltTicketOrder::getTenantId, TENANT_ID)
@@ -421,21 +483,27 @@ public class DealerCommissionUnfreeze10584Task {
.orderByDesc(GltTicketOrder::getId)
.last("limit " + MAX_ELIGIBLE_TICKET_ORDERS_PER_RUN)
);
+ log.info("【步骤3.1】查到已完成的送水订单数量 - tenantId={}, count={}", TENANT_ID, finishedTicketOrders.size());
Set orderNos = new HashSet<>();
+ int checked = 0, skippedNull = 0, skippedNotFirst = 0, skippedNoOrderNo = 0, skippedNotWater = 0;
for (GltTicketOrder ticketOrder : finishedTicketOrders) {
Integer userTicketId = ticketOrder.getUserTicketId();
+ checked++;
if (userTicketId == null) {
+ skippedNull++;
continue;
}
boolean firstFinished = firstFinishedCache.computeIfAbsent(userTicketId, id -> isFirstTicketOrderFinished(id));
if (!firstFinished) {
+ skippedNotFirst++;
continue;
}
String orderNo = userTicketOrderNoCache.computeIfAbsent(userTicketId, id -> findOrderNoByUserTicketId(id));
if (orderNo == null || orderNo.isBlank()) {
+ skippedNoOrderNo++;
continue;
}
@@ -449,6 +517,7 @@ public class DealerCommissionUnfreeze10584Task {
.last("limit 1")
);
if (shopOrder == null || shopOrder.getFormId() == null || !waterFormIds.contains(shopOrder.getFormId())) {
+ skippedNotWater++;
continue;
}
@@ -457,6 +526,8 @@ public class DealerCommissionUnfreeze10584Task {
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;
}
@@ -493,7 +564,9 @@ public class DealerCommissionUnfreeze10584Task {
if (eligibleOrderNos == null || eligibleOrderNos.isEmpty()) {
return List.of();
}
- return shopDealerCapitalService.list(
+ // 先按 eligibleOrderNos 集合的顺序(最新订单优先)处理佣金
+ // 注意:MyBatis-Plus 的 in 查询会保持 in() 列表的顺序
+ List capitals = shopDealerCapitalService.list(
new LambdaQueryWrapper()
.eq(ShopDealerCapital::getTenantId, TENANT_ID)
.eq(ShopDealerCapital::getFlowType, 10)
@@ -502,6 +575,14 @@ public class DealerCommissionUnfreeze10584Task {
.orderByAsc(ShopDealerCapital::getId)
.last("limit " + MAX_CAPITALS_PER_RUN)
);
+ // 按 eligibleOrderNos 的顺序重新排序(最新订单优先)
+ List 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) {
@@ -546,7 +627,7 @@ public class DealerCommissionUnfreeze10584Task {
return false;
}
- // RR 隔离级别下,先锁 user 行,再用锁定读检查 marker,避免“读到旧快照”导致重复解冻。
+ // RR 隔离级别下,先锁 user 行,再用锁定读检查 marker,避免"读到旧快照"导致重复解冻。
ShopDealerCapital existedMarker = shopDealerCapitalService.getOne(
new LambdaQueryWrapper()
.eq(ShopDealerCapital::getTenantId, TENANT_ID)
@@ -590,7 +671,7 @@ public class DealerCommissionUnfreeze10584Task {
marker.setUpdateTime(now);
shopDealerCapitalService.save(marker);
- // 佣金全部解冻完成后,将分销订单状态置为“已解冻”(0)。
+ // 佣金全部解冻完成后,将分销订单状态置为"已解冻"(0)。
// 以当前任务生成的 flowType=50 marker 数量作为完成度判断,避免提前将订单置为已解冻。
setDealerOrderUnfrozenIfCompleted(orderNo, now);
diff --git a/src/main/java/com/gxwebsoft/glt/task/DealerOrderSettlement10584Task.java b/src/main/java/com/gxwebsoft/glt/task/DealerOrderSettlement10584Task.java
index 25b2b37..b995553 100644
--- a/src/main/java/com/gxwebsoft/glt/task/DealerOrderSettlement10584Task.java
+++ b/src/main/java/com/gxwebsoft/glt/task/DealerOrderSettlement10584Task.java
@@ -1,41 +1,38 @@
package com.gxwebsoft.glt.task;
+import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
-import com.gxwebsoft.glt.entity.GltTicketTemplate;
-import com.gxwebsoft.glt.service.GltTicketTemplateService;
-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.core.enums.ShopDealerCapitalUpdateEnum;
+import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.mapper.UserMapper;
-import com.gxwebsoft.shop.service.ShopDealerCapitalService;
-import com.gxwebsoft.shop.service.ShopDealerOrderService;
-import com.gxwebsoft.shop.service.ShopDealerRefereeService;
-import com.gxwebsoft.shop.service.ShopDealerSettingService;
-import com.gxwebsoft.shop.service.ShopDealerUserService;
-import com.gxwebsoft.shop.service.ShopGoodsService;
-import com.gxwebsoft.shop.service.ShopOrderService;
-import com.gxwebsoft.shop.service.ShopOrderGoodsService;
+import com.gxwebsoft.glt.entity.GltTicketTemplate;
+import com.gxwebsoft.glt.service.GltTicketTemplateService;
+import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
+import com.gxwebsoft.shop.entity.*;
+import com.gxwebsoft.shop.mapper.ShopDealerRefereeMapper;
+import com.gxwebsoft.shop.mapper.ShopOrderMapper;
+import com.gxwebsoft.shop.service.*;
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 org.apache.commons.collections4.CollectionUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
/**
* 租户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_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_01 = new BigDecimal("0.01");
private static final BigDecimal TOTAL_DEALER_DIVIDEND_RATE = RATE_0_01;
@@ -92,14 +88,23 @@ public class DealerOrderSettlement10584Task {
@Resource
private GltTicketTemplateService gltTicketTemplateService;
+ @Resource
+ private ShopOrderMapper shopOrderMapper;
+
+ @Resource
+ private ShopDealerRefereeMapper shopDealerRefereeMapper;
+
/**
* 每10秒执行一次。
*/
- @Scheduled(cron = "0/10 * * * * ?")
+// @Scheduled(cron = "0/10 * * * * ?")
@IgnoreTenant("该定时任务仅处理租户10584,但需要显式按tenantId过滤,避免定时任务线程无租户上下文导致查询异常")
public void settleTenant10584Orders() {
try {
+ //获取水票模板对应的商品信息列表
Set waterFormIds = loadWaterFormIds();
+
+ //查询商品列表存在已支付未核销订单数据
List orders = findUnsettledPaidOrders(waterFormIds);
if (orders.isEmpty()) {
return;
@@ -108,7 +113,11 @@ public class DealerOrderSettlement10584Task {
// Per-run caches to reduce DB chatter across orders.
Map level1ParentCache = new HashMap<>();
Map shopRoleCache = new HashMap<>();
+
+ //获取系统设置分销等级
DealerBasicSetting dealerBasicSetting = findDealerBasicSetting();
+
+ //获取分销员type=2第一个分销人作为平台总分红人
ShopDealerUser totalDealerUser = findTotalDealerUser();
if (totalDealerUser == null || totalDealerUser.getUserId() == null) {
log.warn("未找到分红账号,订单仍可结算但不会发放分红 - tenantId={}", TENANT_ID);
@@ -124,6 +133,7 @@ public class DealerOrderSettlement10584Task {
try {
transactionTemplate.executeWithoutResult(status -> {
// 先“认领”订单:并发/多实例下避免重复结算(update=0 表示被其他线程/实例处理)
+ //更新商品订单为已结算状态
if (!claimOrderToSettle(order.getOrderId(), waterFormIds)) {
return;
}
@@ -138,6 +148,88 @@ public class DealerOrderSettlement10584Task {
}
}
+ /**
+ * 每10分钟执行一次。
+ */
+ @Scheduled(cron = "0 0/10 * * * ?")
+ @IgnoreTenant("该定时任务仅处理租户10584,但需要显式按tenantId过滤,避免定时任务线程无租户上下文导致查询异常")
+ public void settleTenant10584OrdersV2() {
+ try {
+ //获取水票模板对应的商品信息列表
+ Set waterFormIds = loadWaterFormIds();
+
+ //查询商品列表存在已支付未核销订单数据【isSettled = 0, payStatus= 1, orderStatus 不在列表(2, 3, 4, 5, 6, 7)】
+ List orders = findUnsettledPaidOrders(waterFormIds);
+ if (orders.isEmpty()) {
+ return;
+ }
+
+ // Per-run caches to reduce DB chatter across orders.
+ Map level1ParentCache = new HashMap<>();
+ Map 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 orderLambdaQueryWrapper = new LambdaQueryWrapper().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 level1ParentCache = new HashMap<>();
+ Map shopRoleCache = new HashMap<>();
+
+ transactionTemplate.executeWithoutResult(status -> {
+ // 先“认领”订单:并发/多实例下避免重复结算(update=0 表示被其他线程/实例处理)
+ //更新商品订单为已结算状态
+ if (!claimOrderToSettleV2(order.getOrderId())) {
+ return;
+ }
+ settleOneOrderV2(order, level1ParentCache, shopRoleCache, totalDealerUser, dealerBasicSetting.level);
+ });
+ }
+ }
+
private List findUnsettledPaidOrders(Set waterFormIds) {
// 租户10584约定:
// - 普通订单:以发货为准(deliveryStatus=20)才结算;
@@ -148,7 +240,7 @@ public class DealerOrderSettlement10584Task {
.eq(ShopOrder::getPayStatus, true)
.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()) {
qw.and(w -> w.eq(ShopOrder::getDeliveryStatus, 20).or().in(ShopOrder::getFormId, waterFormIds));
@@ -178,6 +270,17 @@ public class DealerOrderSettlement10584Task {
return shopOrderService.update(uw);
}
+ private boolean claimOrderToSettleV2(Integer orderId) {
+ LambdaUpdateWrapper uw = new LambdaUpdateWrapper()
+ .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 loadWaterFormIds() {
try {
return gltTicketTemplateService.list(
@@ -252,6 +355,45 @@ public class DealerOrderSettlement10584Task {
log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), baseAmount);
}
+ private void settleOneOrderV2(ShopOrder order, Map level1ParentCache, Map 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 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(
ShopOrder order,
BigDecimal baseAmount,
@@ -329,6 +471,95 @@ public class DealerOrderSettlement10584Task {
return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney);
}
+ /**
+ * 获取分销员分销霍金数据
+ * @param order
+ * @param orderGoodsVOList
+ * @param dealerLevel
+ * @return
+ */
+ private DealerRefereeCommissionV2 settleDealerRefereeCommissionV2(ShopOrder order, BigDecimal rate, List orderGoodsVOList, int dealerLevel) {
+ Integer directDealerId = null;
+ Integer simpleDealerId = null;
+ AtomicReference directMoney = new AtomicReference<>(BigDecimal.ZERO);
+ AtomicReference 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) {
return getDealerRefereeId(userId, 1);
}
@@ -412,6 +643,54 @@ public class DealerOrderSettlement10584Task {
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
}
+ private ShopRoleCommission settleShopRoleRefereeCommissionV2(ShopOrder order, BigDecimal rate, List orderGoodsVOList, Map level1ParentCache, Map shopRoleCache) {
+ List 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 storeDirectMoney = new AtomicReference<>(BigDecimal.ZERO);
+ AtomicReference 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(
ShopOrder order,
BigDecimal baseAmount,
@@ -438,6 +717,32 @@ public class DealerOrderSettlement10584Task {
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() {
return shopDealerUserService.getOne(
new LambdaQueryWrapper()
@@ -620,7 +925,8 @@ public class DealerOrderSettlement10584Task {
newDealerUser.setTotalMoney(BigDecimal.ZERO);
// 尽量补齐基础信息,避免表字段 NOT NULL 导致插入失败(插入失败会让门店分佣“找到了人但入不了账”)。
try {
- User sysUser = userMapper.selectByIdIgnoreTenant(dealerUserId);
+ List sysUsers = userMapper.selectByIdIgnoreTenant(dealerUserId);
+ User sysUser = (sysUsers != null && !sysUsers.isEmpty()) ? sysUsers.get(0) : null;
if (sysUser != null) {
newDealerUser.setRealName(sysUser.getRealName() != null ? sysUser.getRealName() : sysUser.getNickname());
newDealerUser.setMobile(sysUser.getPhone());
@@ -755,6 +1061,92 @@ public class DealerOrderSettlement10584Task {
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()
+ .eq(ShopDealerOrder::getTenantId, TENANT_ID)
+ .eq(ShopDealerOrder::getOrderNo, order.getOrderNo())
+ .last("limit 1")
+ );
+ if (existed != null) {
+ // 允许“补发”门店分润时回填分润字段,避免订单已结算但分润字段一直为空,影响排查/对账。
+ LambdaUpdateWrapper uw = new LambdaUpdateWrapper()
+ .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(
DealerRefereeCommission dealerRefereeCommission,
ShopRoleCommission shopRoleCommission,
@@ -769,6 +1161,30 @@ public class DealerOrderSettlement10584Task {
+ ",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) {
if (order == 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 final Integer storeDirectUserId;
private final BigDecimal storeDirectMoney;
diff --git a/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java b/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java
index 8d0d103..a4f8ce6 100644
--- a/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java
+++ b/src/main/java/com/gxwebsoft/glt/task/GltTicketIssue10584Task.java
@@ -33,7 +33,7 @@ public class GltTicketIssue10584Task {
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 精确过滤")
public void run() {
if (!running.compareAndSet(false, true)) {
diff --git a/src/main/java/com/gxwebsoft/glt/task/GltTicketOrderAutoConfirm10584Task.java b/src/main/java/com/gxwebsoft/glt/task/GltTicketOrderAutoConfirm10584Task.java
index 4866fce..ae4d0ee 100644
--- a/src/main/java/com/gxwebsoft/glt/task/GltTicketOrderAutoConfirm10584Task.java
+++ b/src/main/java/com/gxwebsoft/glt/task/GltTicketOrderAutoConfirm10584Task.java
@@ -34,7 +34,7 @@ public class GltTicketOrderAutoConfirm10584Task {
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 精确过滤")
public void run() {
if (!running.compareAndSet(false, true)) {
diff --git a/src/main/java/com/gxwebsoft/glt/task/GltTicketOrderAutoDispatch10584Task.java b/src/main/java/com/gxwebsoft/glt/task/GltTicketOrderAutoDispatch10584Task.java
index 3a1ef96..c619100 100644
--- a/src/main/java/com/gxwebsoft/glt/task/GltTicketOrderAutoDispatch10584Task.java
+++ b/src/main/java/com/gxwebsoft/glt/task/GltTicketOrderAutoDispatch10584Task.java
@@ -35,7 +35,7 @@ public class GltTicketOrderAutoDispatch10584Task {
@Value("${glt.ticket.dispatch10584.batchSize:50}")
private int batchSize;
- @Scheduled(cron = "${glt.ticket.dispatch10584.cron:0/20 * * * * ?}")
+// @Scheduled(cron = "${glt.ticket.dispatch10584.cron:0/20 * * * * ?}")
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
public void run() {
if (!running.compareAndSet(false, true)) {
diff --git a/src/main/java/com/gxwebsoft/glt/task/GltUserTicketAutoReleaseTask.java b/src/main/java/com/gxwebsoft/glt/task/GltUserTicketAutoReleaseTask.java
index 91855db..0aaabe5 100644
--- a/src/main/java/com/gxwebsoft/glt/task/GltUserTicketAutoReleaseTask.java
+++ b/src/main/java/com/gxwebsoft/glt/task/GltUserTicketAutoReleaseTask.java
@@ -4,18 +4,19 @@ import com.gxwebsoft.common.core.annotation.IgnoreTenant;
import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
-import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * 冻结水票自动释放任务:
- * - 扫描 glt_user_ticket_release 中到期且待释放(status=0)的记录
- * - 释放成功:frozen -> available,并将 release.status 置为 1
+ * 冻结水票自动释放任务:每月1日凌晨1-3点每2分钟执行一次任务
+ * - 扫描 glt_user_ticket_release 中本月到期且待释放(status=0)的记录
+ * - 释放成功:
+ * 1.修改水票释放任务为已释放状态 glt_user_ticket_release
+ * 2.修改个人水票中:可用数量、冻结数量、已释放数量 glt_user_ticket
+ * 3.同步生产水票释放记录数据 glt_user_ticket_log
*/
@Slf4j
@Component
@@ -25,25 +26,17 @@ public class GltUserTicketAutoReleaseTask {
private final GltUserTicketAutoReleaseService autoReleaseService;
- @Value("${glt.ticket.auto-release.batch-size:200}")
- private int batchSize;
-
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")
public void run() {
if (!running.compareAndSet(false, true)) {
log.warn("冻结水票自动释放任务仍在执行中,本轮跳过");
return;
}
-
try {
- LocalDateTime now = LocalDateTime.now();
- int released = autoReleaseService.releaseDue(now, Math.max(batchSize, 1));
- if (released > 0) {
- log.info("冻结水票自动释放完成 - released={}, now={}", released, now);
- }
+ autoReleaseService.releaseTask();
} finally {
running.set(false);
}
diff --git a/src/main/java/com/gxwebsoft/payment/constants/PaymentConstants.java b/src/main/java/com/gxwebsoft/payment/constants/PaymentConstants.java
index 80f0354..ffd8c2c 100644
--- a/src/main/java/com/gxwebsoft/payment/constants/PaymentConstants.java
+++ b/src/main/java/com/gxwebsoft/payment/constants/PaymentConstants.java
@@ -230,7 +230,7 @@ public class PaymentConstants {
*/
public static class Environment {
/** 开发环境 */
- public static final String DEV = "dev";
+ public static final String DEV = "local";
/** 测试环境 */
public static final String TEST = "test";
/** 生产环境 */
diff --git a/src/main/java/com/gxwebsoft/payment/service/WxPayConfigService.java b/src/main/java/com/gxwebsoft/payment/service/WxPayConfigService.java
index a681c3d..b61fdb5 100644
--- a/src/main/java/com/gxwebsoft/payment/service/WxPayConfigService.java
+++ b/src/main/java/com/gxwebsoft/payment/service/WxPayConfigService.java
@@ -148,7 +148,7 @@ public class WxPayConfigService {
log.info("从数据库获取支付配置成功,租户ID: {},将缓存配置", tenantId);
// 开发环境下,如果apiclientKey为空,设置默认值
- if ("dev".equals(activeProfile) &&
+ if ("local".equals(activeProfile) &&
(payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty())) {
log.warn("开发环境:数据库配置中apiclientKey为空,使用默认值,租户ID: {}", tenantId);
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);
}
@@ -180,7 +180,7 @@ public class WxPayConfigService {
* 获取证书文件路径
*/
private String getCertificatePath(Integer tenantId, Payment payment) throws PaymentException {
- if ("dev".equals(activeProfile)) {
+ if ("local".equals(activeProfile)) {
return getDevCertificatePath(tenantId);
} else {
return getProdCertificatePath(payment);
@@ -237,7 +237,7 @@ public class WxPayConfigService {
*/
private Config createWxPayConfig(Payment payment, String certificatePath) throws PaymentException {
try {
- if ("dev".equals(activeProfile) && payment == null) {
+ if ("local".equals(activeProfile) && payment == null) {
// 开发环境测试配置
return createDevTestConfig(certificatePath);
} else if (payment != null) {
@@ -343,7 +343,7 @@ public class WxPayConfigService {
// 开发环境下,如果apiclientKey为空,给一个警告但不抛异常
// 生产环境必须有apiclientKey
if (payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty()) {
- if ("dev".equals(activeProfile)) {
+ if ("local".equals(activeProfile)) {
log.warn("开发环境:证书文件名(apiclientKey)未配置,将使用默认值");
} else {
throw PaymentException.systemError("证书文件名(apiclientKey)未配置", null);
diff --git a/src/main/java/com/gxwebsoft/shop/constants/WxPayConstants.java b/src/main/java/com/gxwebsoft/shop/constants/WxPayConstants.java
index 1618dd8..8d6eb84 100644
--- a/src/main/java/com/gxwebsoft/shop/constants/WxPayConstants.java
+++ b/src/main/java/com/gxwebsoft/shop/constants/WxPayConstants.java
@@ -78,7 +78,7 @@ public class WxPayConstants {
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";
}
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopActiveImageController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopActiveImageController.java
new file mode 100644
index 0000000..0d9b16b
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopActiveImageController.java
@@ -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> page(ShopActiveImageParam param) {
+ // 使用关联查询
+ return success(shopActiveImageService.pageRel(param));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopActiveImage:list')")
+ @Operation(summary = "查询全部推广码底图")
+ @GetMapping()
+ public ApiResult> list(ShopActiveImageParam param) {
+ // 使用关联查询
+ return success(shopActiveImageService.listRel(param));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopActiveImage:list')")
+ @Operation(summary = "根据id查询推广码底图")
+ @GetMapping("/{id}")
+ public ApiResult 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 list) {
+ if (shopActiveImageService.saveBatch(list)) {
+ return success("添加成功");
+ }
+ return fail("添加失败");
+ }
+
+ @PreAuthorize("hasAuthority('shop:shopActiveImage:update')")
+ @OperationLog
+ @Operation(summary = "批量修改推广码底图")
+ @PutMapping("/batch")
+ public ApiResult> removeBatch(@RequestBody BatchParam 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 ids) {
+ if (shopActiveImageService.removeByIds(ids)) {
+ return success("删除成功");
+ }
+ return fail("删除失败");
+ }
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerCapitalController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerCapitalController.java
index d7ccaf1..f61a483 100644
--- a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerCapitalController.java
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerCapitalController.java
@@ -1,17 +1,19 @@
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.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.param.ShopDealerCapitalParam;
-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.system.entity.User;
+import com.gxwebsoft.shop.service.ShopDealerCapitalService;
+import com.gxwebsoft.shop.vo.ShopDealerCapitalWaterVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springdoc.api.annotations.ParameterObject;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -34,11 +36,19 @@ public class ShopDealerCapitalController extends BaseController {
@PreAuthorize("hasAuthority('shop:shopDealerCapital:list')")
@Operation(summary = "分页查询分销商资金明细表")
@GetMapping("/page")
- public ApiResult> page(ShopDealerCapitalParam param) {
+ public ApiResult> page(@ParameterObject ShopDealerCapitalParam param) {
// 使用关联查询
return success(shopDealerCapitalService.pageRel(param));
}
+ @PreAuthorize("hasAuthority('shop:shopDealerCapital:list')")
+ @Operation(summary = "分页查询分销商个人流水")
+ @GetMapping("/myCapitalWater")
+ public ApiResult> myCapitalWater(ShopDealerCapitalWaterDto waterDto) {
+ // 使用关联查询
+ return success(shopDealerCapitalService.myCapitalWater(waterDto));
+ }
+
@PreAuthorize("hasAuthority('shop:shopDealerCapital:list')")
@Operation(summary = "查询全部分销商资金明细表")
@GetMapping()
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerOrderController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerOrderController.java
index 2ecc05e..97f490d 100644
--- a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerOrderController.java
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerOrderController.java
@@ -3,19 +3,18 @@ package com.gxwebsoft.shop.controller;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.util.ObjectUtil;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-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.annotation.OperationLog;
import com.gxwebsoft.common.core.utils.JSONUtil;
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.annotation.OperationLog;
+import com.gxwebsoft.common.core.web.PageResult;
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.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -24,7 +23,9 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
+import java.math.BigDecimal;
import java.util.List;
+import java.util.Map;
/**
* 分销商订单记录表控制器
@@ -39,6 +40,9 @@ public class ShopDealerOrderController extends BaseController {
@Resource
private ShopDealerOrderService shopDealerOrderService;
+ @Resource
+ private OrderSettlementTask orderSettlementTask;
+
@PreAuthorize("hasAuthority('shop:shopDealerOrder:list')")
@Operation(summary = "分页查询分销商订单记录表")
@GetMapping("/page")
@@ -63,6 +67,13 @@ public class ShopDealerOrderController extends BaseController {
return success(shopDealerOrderService.getByIdRel(id));
}
+ @PreAuthorize("hasAuthority('shop:shopDealerOrder:list')")
+ @Operation(summary = "查询个人今日收益")
+ @GetMapping("/todayRevenue")
+ public ApiResult todayRevenue() {
+ return success(shopDealerOrderService.todayRevenue());
+ }
+
@PreAuthorize("hasAuthority('shop:shopDealerOrder:save')")
@OperationLog
@Operation(summary = "添加分销商订单记录表")
@@ -168,4 +179,29 @@ public class ShopDealerOrderController extends BaseController {
}
return fail("导入失败", null);
}
+
+ @PreAuthorize("hasAuthority('shop:shopDealerOrder:update')")
+ @OperationLog
+ @Operation(summary = "手动触发单条订单佣金解冻")
+// @PostMapping("/unfreeze")
+ public ApiResult manualUnfreeze(@RequestBody Map 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");
+ }
}
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerRefereeController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerRefereeController.java
index 60a363f..bd5b9b0 100644
--- a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerRefereeController.java
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerRefereeController.java
@@ -1,19 +1,19 @@
package com.gxwebsoft.shop.controller;
-import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.Constants;
+import com.gxwebsoft.common.core.annotation.OperationLog;
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.param.ShopDealerRefereeParam;
-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.system.entity.User;
+import com.gxwebsoft.shop.service.ShopDealerRefereeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springdoc.api.annotations.ParameterObject;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -41,6 +41,13 @@ public class ShopDealerRefereeController extends BaseController {
return success(shopDealerRefereeService.pageRel(param));
}
+ @PreAuthorize("hasAuthority('shop:shopDealerReferee:list')")
+ @Operation(summary = "分页查询分销商推荐关系表")
+ @GetMapping("/appPage")
+ public ApiResult> appPage(@ParameterObject ShopDealerRefereeParam param) {
+ return success(shopDealerRefereeService.appPage(param));
+ }
+
@PreAuthorize("hasAuthority('shop:shopDealerReferee:list')")
@Operation(summary = "查询全部分销商推荐关系表")
@GetMapping()
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerUserController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerUserController.java
index 9d5d469..cbbbdb1 100644
--- a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerUserController.java
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerUserController.java
@@ -4,18 +4,19 @@ import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.util.ObjectUtil;
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.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.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.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -78,6 +79,13 @@ public class ShopDealerUserController extends BaseController {
return fail("添加失败");
}
+ @PreAuthorize("hasAuthority('shop:shopDealerUser:update')")
+ @Operation(summary = "开启/关闭分销商用户核销权限")
+ @PutMapping("/verifyEnable")
+ public ApiResult verifyEnable(@RequestParam("id") Integer id) {
+ return success(shopDealerUserService.verifyEnable(id));
+ }
+
@PreAuthorize("hasAuthority('shop:shopDealerUser:update')")
@Operation(summary = "修改分销商用户记录表")
@PutMapping()
@@ -178,4 +186,18 @@ public class ShopDealerUserController extends BaseController {
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));
+ }
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerWithdrawController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerWithdrawController.java
index b13ef3a..0127250 100644
--- a/src/main/java/com/gxwebsoft/shop/controller/ShopDealerWithdrawController.java
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopDealerWithdrawController.java
@@ -2,6 +2,7 @@ package com.gxwebsoft.shop.controller;
import cn.hutool.core.util.StrUtil;
import com.gxwebsoft.common.core.web.BaseController;
+import com.gxwebsoft.common.system.redis.OrderNoUtils;
import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.service.ShopDealerUserService;
import com.gxwebsoft.shop.service.ShopDealerWithdrawService;
@@ -43,6 +44,8 @@ public class ShopDealerWithdrawController extends BaseController {
private ShopDealerUserService shopDealerUserService;
@Resource
private WxTransferService wxTransferService;
+ @Resource
+ private OrderNoUtils orderNoUtils;
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:list')")
@Operation(summary = "分页查询分销商提现明细表")
@@ -89,6 +92,8 @@ public class ShopDealerWithdrawController extends BaseController {
return fail("tenantId为空,无法发起提现");
}
+ String orderNo = orderNoUtils.generate("WD");
+ shopDealerWithdraw.setOrderNo(orderNo);
shopDealerWithdraw.setTenantId(tenantId);
shopDealerWithdraw.setUserId(loginUser.getUserId());
@@ -251,7 +256,8 @@ public class ShopDealerWithdrawController extends BaseController {
}
// 使用提现记录ID构造单号,保持幂等;微信要求 5-32 且仅字母/数字
- String outBillNo = String.format("WD%03d", db.getId());
+ String outBillNo = db.getOrderNo();
+
String remark = "分销商提现";
String userName = db.getRealName();
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopFlashSaleActivityController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopFlashSaleActivityController.java
new file mode 100644
index 0000000..9ddac55
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopFlashSaleActivityController.java
@@ -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> page(ShopFlashSaleActivityParam param) {
+ // 使用关联查询
+ return success(shopFlashSaleActivityService.pageRel(param));
+ }
+
+ @Operation(summary = "个人获取秒杀活动数据")
+ @GetMapping("/getMyActive")
+ public ApiResult> getMyActive(@RequestParam("tenantId") Integer tenantId, Integer popFlag) {
+ // 使用关联查询
+ return success(shopFlashSaleActivityService.getMyActive(tenantId, popFlag));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:list')")
+ @Operation(summary = "查询全部秒杀活动")
+ @GetMapping()
+ public ApiResult> list(ShopFlashSaleActivityParam param) {
+ // 使用关联查询
+ return success(shopFlashSaleActivityService.listRel(param));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:list')")
+ @Operation(summary = "根据id查询秒杀活动")
+ @GetMapping("/{id}")
+ public ApiResult 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 list) {
+ if (shopFlashSaleActivityService.saveBatch(list)) {
+ return success("添加成功");
+ }
+ return fail("添加失败");
+ }
+
+ @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:update')")
+ @OperationLog
+ @Operation(summary = "批量修改秒杀活动")
+ @PutMapping("/batch")
+ public ApiResult> removeBatch(@RequestBody BatchParam 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 ids) {
+ if (shopFlashSaleActivityService.removeByIds(ids)) {
+ return success("删除成功");
+ }
+ return fail("删除失败");
+ }
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsCategoryController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsCategoryController.java
index 49077a7..e9059a4 100644
--- a/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsCategoryController.java
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsCategoryController.java
@@ -6,7 +6,6 @@ import com.gxwebsoft.shop.entity.ShopGoodsCategory;
import com.gxwebsoft.shop.param.ShopGoodsCategoryParam;
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.system.entity.User;
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsProfitController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsProfitController.java
new file mode 100644
index 0000000..c7e8ecf
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopGoodsProfitController.java
@@ -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> page(ShopGoodsProfitParam param) {
+ // 使用关联查询
+ return success(shopGoodsProfitService.pageRel(param));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopGoodsProfit:list')")
+ @Operation(summary = "查询全部商品分润设定")
+ @GetMapping()
+ public ApiResult> list(ShopGoodsProfitParam param) {
+ // 使用关联查询
+ return success(shopGoodsProfitService.listRel(param));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopGoodsProfit:list')")
+ @Operation(summary = "根据id查询商品分润设定")
+ @GetMapping("/{id}")
+ public ApiResult 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("删除失败");
+ }
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java
index ae553dc..4866792 100644
--- a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java
@@ -5,42 +5,45 @@ import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
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.utils.RedisUtil;
+import com.gxwebsoft.common.core.config.ConfigProperties;
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.WechatPayConfigValidator;
+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.Payment;
-import com.gxwebsoft.shop.entity.ShopOrderDelivery;
-import com.gxwebsoft.shop.entity.ShopUserAddress;
-import com.gxwebsoft.shop.service.*;
+import com.gxwebsoft.common.system.entity.User;
+import com.gxwebsoft.common.system.redis.OrderNoUtils;
+import com.gxwebsoft.glt.service.GltTicketIssueService;
import com.gxwebsoft.glt.service.GltTicketRevokeService;
-import com.gxwebsoft.shop.service.impl.KuaiDi100Impl;
-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.glt.task.DealerOrderSettlement10584Task;
import com.gxwebsoft.payment.dto.PaymentResponse;
import com.gxwebsoft.payment.enums.PaymentType;
-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.system.entity.User;
+import com.gxwebsoft.payment.service.PaymentService;
+import com.gxwebsoft.shop.dto.*;
+import com.gxwebsoft.shop.entity.ShopOrder;
+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.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
-import com.wechat.pay.java.core.RSAAutoCertificateConfig;
-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 com.wechat.pay.java.service.payments.model.Transaction;
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.LoggerFactory;
+import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
@@ -53,6 +56,7 @@ import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
/**
* 订单控制器
@@ -65,6 +69,8 @@ import java.util.Objects;
@RequestMapping("/api/shop/shop-order")
public class ShopOrderController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(ShopOrderController.class);
+ /** 按商户号缓存 NotificationConfig,避免每次回调都重新拉取平台证书 */
+ private final ConcurrentHashMap notifyConfigCache = new ConcurrentHashMap<>();
@Resource
private ShopOrderService shopOrderService;
@Resource
@@ -107,6 +113,12 @@ public class ShopOrderController extends BaseController {
private GltTicketRevokeService gltTicketRevokeService;
@Resource
private ShopDealerCommissionRollbackService shopDealerCommissionRollbackService;
+ @Resource
+ private GltTicketIssueService gltTicketIssueService;
+ @Resource
+ private OrderNoUtils orderNoUtils;
+ @Resource
+ private DealerOrderSettlement10584Task dealerOrderSettlement;
@Operation(summary = "分页查询订单")
@GetMapping("/page")
@@ -130,14 +142,20 @@ public class ShopOrderController extends BaseController {
return success(shopOrderService.getByIdRel(id));
}
- @Operation(summary = "添加订单")
+// @PreAuthorize("hasAuthority('shop:shopOrder:list')")
+ @Operation(summary = "我已核销订单")
+ @GetMapping("/myVerifyOrder")
+ public ApiResult myVerifyOrder(@ParameterObject ShopOrderMyVerifyDto myVerifyDto) {
+ return success(shopOrderService.myVerifyOrder(myVerifyDto));
+ }
+
+ @Operation(summary = "添加订单【单商品添加】")
@PostMapping()
public ApiResult> save(@RequestBody OrderCreateRequest request) {
User loginUser = getLoginUser();
if (loginUser == null) {
return fail("用户未登录");
}
-
try {
Map wxOrderInfo = orderBusinessService.createOrder(request, loginUser);
return success("下单成功", wxOrderInfo);
@@ -438,15 +456,16 @@ public class ShopOrderController extends BaseController {
}
@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")
public ApiResult> refund(@RequestBody ShopOrder req) {
- if (req == null || req.getOrderId() == null || req.getOrderStatus() == null) {
- return fail("orderId 和 orderStatus 不能为空");
- }
- if (!Objects.equals(req.getOrderStatus(), 4) && !Objects.equals(req.getOrderStatus(), 6) && !Objects.equals(req.getOrderStatus(), 7)) {
- return fail("orderStatus 仅支持 4(申请退款) 或 6(同意退款) 或 7(客户端申请退款)");
- }
+ if (req == null || req.getOrderId() == null || req.getOrderStatus() == null) {
+ return fail("orderId 和 orderStatus 不能为空");
+ }
+ if (!Objects.equals(req.getOrderStatus(), 4) && !Objects.equals(req.getOrderStatus(), 5)
+ && !Objects.equals(req.getOrderStatus(), 6) && !Objects.equals(req.getOrderStatus(), 7)) {
+ return fail("orderStatus 仅支持 4(申请退款)、5(拒绝退款)、6(同意退款)、7(客户端申请退款)");
+ }
ShopOrder current = shopOrderService.getById(req.getOrderId());
if (current == null) {
@@ -490,12 +509,12 @@ public class ShopOrderController extends BaseController {
if (!Boolean.TRUE.equals(current.getPayStatus())) {
return fail("订单未支付,无法退款");
}
- if (StrUtil.isNotBlank(current.getRefundOrder())) {
- logger.warn("订单已经退款过,订单号: {}, 退款单号: {}", current.getOrderNo(), current.getRefundOrder());
+ if (StrUtil.isNotBlank(current.getRefundOrder()) || current.getOrderStatus() == 6) {
+ logger.warn("订单已经退款过,订单号: {}, 退款单号: {}", current.getOrderNo(), current.getRefundOrder() != null ? current.getRefundOrder() : "");
return fail("订单已退款,请勿重复操作");
}
- String refundNo = "RF" + IdUtil.getSnowflakeNextId();
+ String refundNo = orderNoUtils.generate("RF");
BigDecimal refundAmount = req.getRefundMoney();
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
@@ -569,7 +588,7 @@ public class ShopOrderController extends BaseController {
rollbackOrder.setOrderNo(current.getOrderNo());
rollbackOrder.setPayPrice(current.getPayPrice());
rollbackOrder.setTotalPrice(current.getTotalPrice());
- boolean rollbackOk = shopDealerCommissionRollbackService.rollbackOnOrderRefund(rollbackOrder, refundAmount);
+ boolean rollbackOk = shopDealerCommissionRollbackService.rollbackOnOrderRefundV2(rollbackOrder, refundAmount);
if (!rollbackOk) {
logger.error("退款成功但回退分红/分润/佣金失败 - tenantId={}, orderId={}, orderNo={}",
tenantId, current.getOrderId(), current.getOrderNo());
@@ -760,7 +779,7 @@ public class ShopOrderController extends BaseController {
@Schema(description = "异步通知11")
@PostMapping("/notify/{tenantId}")
public String wxNotify(@RequestHeader Map header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) {
- logger.info("异步通知*************** = " + tenantId);
+ logger.info("异步通知*************** = " + body + ",租户:" +tenantId);
// 获取支付配置信息用于解密
String key = "Payment:1:".concat(tenantId.toString());
@@ -791,90 +810,100 @@ public class ShopOrderController extends BaseController {
.body(body)
.build();
- // 创建通知配置 - 使用与下单方法相同的证书配置逻辑
+ // 创建通知配置 - 使用与下单方法相同的证书配置逻辑(按 mchId 缓存,避免重复拉取平台证书)
NotificationConfig config;
- try {
- if (active.equals("dev")) {
- // 开发环境 - 构建包含租户号的私钥路径
- String tenantCertPath = "dev/wechat/" + tenantId;
- String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
+ final String mchId = payment.getMchId();
+ config = notifyConfigCache.get(mchId);
+ if (config == null) {
+ try {
+ NotificationConfig newConfig;
+ if (active.equals("local")) {
+ // 开发环境 - 构建包含租户号的私钥路径
+ String tenantCertPath = "dev/wechat/" + tenantId;
+ String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
- logger.info("开发环境异步通知证书路径: {}", privateKeyPath);
- logger.info("租户ID: {}, 证书目录: {}", tenantId, tenantCertPath);
+ logger.info("开发环境异步通知证书路径: {}", privateKeyPath);
+ logger.info("租户ID: {}, 证书目录: {}", tenantId, tenantCertPath);
- // 检查证书文件是否存在
- if (!certificateLoader.certificateExists(privateKeyPath)) {
- logger.error("证书文件不存在: {}", privateKeyPath);
- throw new RuntimeException("证书文件不存在: " + privateKeyPath);
+ // 检查证书文件是否存在
+ if (!certificateLoader.certificateExists(privateKeyPath)) {
+ logger.error("证书文件不存在: {}", privateKeyPath);
+ throw new RuntimeException("证书文件不存在: " + privateKeyPath);
+ }
+
+ String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);
+
+ // 使用验证器获取有效的 APIv3 密钥
+ String apiV3Key = wechatPayConfigValidator.getValidApiV3Key(payment);
+
+ logger.info("私钥文件加载成功: {}", privateKey);
+ logger.info("使用APIv3密钥来源: {}", payment.getApiKey() != null && !payment.getApiKey().trim().isEmpty() ? "数据库配置" : "配置文件默认");
+ logger.info("APIv3密钥长度: {}", apiV3Key != null ? apiV3Key.length() : 0);
+ logger.info("商户证书序列号: {}", payment.getMerchantSerialNumber());
+
+ // 使用自动证书配置
+ newConfig = new RSAAutoCertificateConfig.Builder()
+ .merchantId(mchId)
+ .privateKeyFromPath(privateKey)
+ .merchantSerialNumber(payment.getMerchantSerialNumber())
+ .apiV3Key(apiV3Key)
+ .build();
+
+ logger.info("✅ 开发环境使用自动证书配置创建通知解析器成功");
+ } else {
+ // 生产环境 - 使用自动证书配置
+ final String certRootPath = certConfig.getCertRootPath();
+ logger.info("生产环境证书根路径: {}", certRootPath);
+
+ String privateKeyRelativePath = payment.getApiclientKey();
+ logger.info("数据库中的私钥相对路径: {}", privateKeyRelativePath);
+
+ // 生产环境已经没有/file目录,所有路径都直接拼接到根路径
+ String privateKeyFullPath;
+ // 处理数据库中可能存在的历史路径格式
+ String cleanPath = privateKeyRelativePath;
+ if (privateKeyRelativePath.startsWith("/file/")) {
+ // 去掉历史的 /file/ 前缀
+ cleanPath = privateKeyRelativePath.substring(6);
+ } else if (privateKeyRelativePath.startsWith("file/")) {
+ // 去掉历史的 file/ 前缀
+ cleanPath = privateKeyRelativePath.substring(5);
+ }
+ // 确保路径以 / 开头
+ if (!cleanPath.startsWith("/")) {
+ cleanPath = "/" + cleanPath;
+ }
+ privateKeyFullPath = certRootPath + cleanPath;
+
+ logger.info("生产环境私钥完整路径: {}", privateKeyFullPath);
+ String privateKey = certificateLoader.loadCertificatePath(privateKeyFullPath);
+ String apiV3Key = payment.getApiKey();
+
+ // 使用自动证书配置
+ newConfig = new RSAAutoCertificateConfig.Builder()
+ .merchantId(mchId)
+ .privateKeyFromPath(privateKey)
+ .merchantSerialNumber(payment.getMerchantSerialNumber())
+ .apiV3Key(apiV3Key)
+ .build();
+
+ logger.info("✅ 生产环境使用自动证书配置创建通知解析器成功");
}
-
- String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);
-
- // 使用验证器获取有效的 APIv3 密钥
- String apiV3Key = wechatPayConfigValidator.getValidApiV3Key(payment);
-
- logger.info("私钥文件加载成功: {}", privateKey);
- logger.info("使用APIv3密钥来源: {}", payment.getApiKey() != null && !payment.getApiKey().trim().isEmpty() ? "数据库配置" : "配置文件默认");
- logger.info("APIv3密钥长度: {}", apiV3Key != null ? apiV3Key.length() : 0);
- logger.info("商户证书序列号: {}", payment.getMerchantSerialNumber());
-
- // 使用自动证书配置
- config = new RSAAutoCertificateConfig.Builder()
- .merchantId(payment.getMchId())
- .privateKeyFromPath(privateKey)
- .merchantSerialNumber(payment.getMerchantSerialNumber())
- .apiV3Key(apiV3Key)
- .build();
-
- logger.info("✅ 开发环境使用自动证书配置创建通知解析器成功");
- } else {
- // 生产环境 - 使用自动证书配置
- final String certRootPath = certConfig.getCertRootPath();
- logger.info("生产环境证书根路径: {}", certRootPath);
-
- String privateKeyRelativePath = payment.getApiclientKey();
- logger.info("数据库中的私钥相对路径: {}", privateKeyRelativePath);
-
- // 生产环境已经没有/file目录,所有路径都直接拼接到根路径
- String privateKeyFullPath;
- // 处理数据库中可能存在的历史路径格式
- String cleanPath = privateKeyRelativePath;
- if (privateKeyRelativePath.startsWith("/file/")) {
- // 去掉历史的 /file/ 前缀
- cleanPath = privateKeyRelativePath.substring(6);
- } else if (privateKeyRelativePath.startsWith("file/")) {
- // 去掉历史的 file/ 前缀
- cleanPath = privateKeyRelativePath.substring(5);
- }
- // 确保路径以 / 开头
- if (!cleanPath.startsWith("/")) {
- cleanPath = "/" + cleanPath;
- }
- privateKeyFullPath = certRootPath + cleanPath;
-
- logger.info("生产环境私钥完整路径: {}", privateKeyFullPath);
- String privateKey = certificateLoader.loadCertificatePath(privateKeyFullPath);
- String apiV3Key = payment.getApiKey();
-
- // 使用自动证书配置
- config = new RSAAutoCertificateConfig.Builder()
- .merchantId(payment.getMchId())
- .privateKeyFromPath(privateKey)
- .merchantSerialNumber(payment.getMerchantSerialNumber())
- .apiV3Key(apiV3Key)
- .build();
-
- logger.info("✅ 生产环境使用自动证书配置创建通知解析器成功");
+ // 放入缓存
+ notifyConfigCache.putIfAbsent(mchId, newConfig);
+ config = notifyConfigCache.get(mchId);
+ } catch (Exception e) {
+ logger.error("❌ 创建通知配置失败 - 租户ID: {}, 商户号: {}", tenantId, mchId, e);
+ logger.error("🔍 错误详情: {}", e.getMessage());
+ logger.error("💡 请检查:");
+ logger.error("1. 证书文件是否存在且路径正确");
+ logger.error("2. APIv3密钥是否配置正确");
+ logger.error("3. 商户证书序列号是否正确");
+ logger.error("4. 网络连接是否正常");
+ throw new RuntimeException("微信支付通知配置失败: " + e.getMessage(), e);
}
- } catch (Exception e) {
- logger.error("❌ 创建通知配置失败 - 租户ID: {}, 商户号: {}", tenantId, payment.getMchId(), e);
- logger.error("🔍 错误详情: {}", e.getMessage());
- logger.error("💡 请检查:");
- logger.error("1. 证书文件是否存在且路径正确");
- logger.error("2. APIv3密钥是否配置正确");
- logger.error("3. 商户证书序列号是否正确");
- logger.error("4. 网络连接是否正常");
- throw new RuntimeException("微信支付通知配置失败: " + e.getMessage(), e);
+ } else {
+ logger.info("✅ 使用缓存的通知配置 - 商户号: {}", mchId);
}
// 初始化 NotificationParser
@@ -886,9 +915,10 @@ public class ShopOrderController extends BaseController {
logger.info("开始解析微信支付异步通知...");
Transaction transaction = parser.parse(requestParam, Transaction.class);
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 transactionId = transaction.getTransactionId();
final Integer total = transaction.getAmount().getTotal();
@@ -920,6 +950,10 @@ public class ShopOrderController extends BaseController {
System.out.println("实际付款金额 = " + order.getPayPrice());
// 更新订单状态并处理支付成功后的业务逻辑(包括累加商品销量)
shopOrderService.updateByOutTradeNo(order);
+
+ //支付成功执行异步任务
+ gltTicketIssueService.paySuccessTask(order.getOrderNo(), tenantId);
+
return "SUCCESS";
}
}
@@ -940,6 +974,33 @@ public class ShopOrderController extends BaseController {
return "fail";
}
+ @Operation(summary = "核销订单", description = "核销员扫码核销用户订单")
+ @PutMapping("/verifyOrder")
+ public ApiResult verifyOrder(@RequestBody VerifyShopOrderDto verifyDto){
+ return success(shopOrderService.verifyOrder(verifyDto));
+ }
+
+ @Operation(summary = "支付成功任务", description = "用户支付成功后执行任务")
+ @PutMapping("/paySuccessTask")
+ public ApiResult paySuccessTask(@RequestBody PaySuccessTaskDto taskDto){
+ gltTicketIssueService.paySuccessTask(taskDto.getOrderNo(), taskDto.getTenantId());
+ return success(Boolean.TRUE);
+ }
+
+ @Operation(summary = "支付成功发送水票", description = "支付成功发送水票")
+ @PutMapping("/suerTicketRelease")
+ public ApiResult suerTicketRelease(@RequestBody PaySuccessTaskDto taskDto){
+ gltTicketIssueService.suerTicketRelease(taskDto.getOrderNo(), taskDto.getTenantId());
+ return success(Boolean.TRUE);
+ }
+
+ @Operation(summary = "支付成功结算", description = "支付成功结算")
+ @PutMapping("/orderSettlement")
+ public ApiResult orderSettlement(@RequestBody PaySuccessTaskDto taskDto){
+ dealerOrderSettlement.orderSettlement(taskDto.getOrderNo());
+ return success(Boolean.TRUE);
+ }
+
@Operation(summary = "更新订单支付状态", description = "用户支付成功后主动同步订单状态")
@PutMapping("/payment-status")
public ApiResult> updateOrderPaymentStatus(@RequestBody UpdatePaymentStatusRequest request) {
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopSurchargeConfigController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopSurchargeConfigController.java
new file mode 100644
index 0000000..b5cf848
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopSurchargeConfigController.java
@@ -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> page(ShopSurchargeConfigParam param) {
+ // 使用关联查询
+ return success(shopSurchargeConfigService.pageRel(param));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:list')")
+ @Operation(summary = "根据id查询步梯费用设置")
+ @GetMapping("/{id}")
+ public ApiResult get(@PathVariable("id") Integer id) {
+ // 使用关联查询
+ return success(shopSurchargeConfigService.getByIdRel(id));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:list')")
+ @Operation(summary = "根据类型查询步梯费用设置")
+ @GetMapping("/getInfoByType")
+ public ApiResult getInfoByType(@RequestParam Integer type) {
+ // 使用关联查询
+ return success(shopSurchargeConfigService.getInfoByType(type));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:save')")
+ @OperationLog
+ @Operation(summary = "添加步梯费用设置")
+ @PostMapping()
+ public ApiResult save(@RequestBody ShopSurchargeConfig shopSurchargeConfig) {
+ return success(shopSurchargeConfigService.saveInfo(shopSurchargeConfig));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:update')")
+ @OperationLog
+ @Operation(summary = "修改步梯费用设置")
+ @PutMapping()
+ public ApiResult update(@RequestBody ShopSurchargeConfig shopSurchargeConfig) {
+ return success(shopSurchargeConfigService.updateInfo(shopSurchargeConfig));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:update')")
+ @OperationLog
+ @Operation(summary = "修改步梯费用设置")
+ @PutMapping("/updateStatus")
+ public ApiResult 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("删除失败");
+ }
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopUserAddressController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopUserAddressController.java
index e2290f9..9cc92fb 100644
--- a/src/main/java/com/gxwebsoft/shop/controller/ShopUserAddressController.java
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopUserAddressController.java
@@ -79,11 +79,8 @@
@OperationLog
@Operation(summary = "修改收货地址")
@PutMapping()
- public ApiResult> update(@RequestBody ShopUserAddress shopUserAddress) {
- if (shopUserAddressService.updateById(shopUserAddress)) {
- return success("修改成功");
- }
- return fail("修改失败");
+ public ApiResult update(@RequestBody ShopUserAddress shopUserAddress) {
+ return success(shopUserAddressService.updateInfo(shopUserAddress));
}
@PreAuthorize("hasAuthority('shop:shopUserAddress:remove')")
diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopVerifyUserController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopVerifyUserController.java
new file mode 100644
index 0000000..f915df5
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/controller/ShopVerifyUserController.java
@@ -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> page(ShopVerifyUserParam param) {
+ // 使用关联查询
+ return success(shopVerifyUserService.pageRel(param));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopVerifyUser:list')")
+ @Operation(summary = "查询全部订单核销人管理")
+ @GetMapping()
+ public ApiResult> list(ShopVerifyUserParam param) {
+ // 使用关联查询
+ return success(shopVerifyUserService.listRel(param));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopVerifyUser:list')")
+ @Operation(summary = "根据id查询订单核销人管理")
+ @GetMapping("/{id}")
+ public ApiResult get(@PathVariable("id") Integer id) {
+ // 使用关联查询
+ return success(shopVerifyUserService.getByIdRel(id));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopVerifyUser:list')")
+ @Operation(summary = "根据用户ID查询订单核销人管理")
+ @GetMapping("/getByUserId")
+ public ApiResult getByUserId(@RequestParam("userId") Integer userId) {
+ return success(shopVerifyUserService.getInfo(userId));
+ }
+
+// @PreAuthorize("hasAuthority('shop:shopVerifyUser:save')")
+ @OperationLog
+ @Operation(summary = "添加订单核销人管理")
+ @PostMapping()
+ public ApiResult 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 updateStatus(@RequestParam("id") Integer id) {
+ return success(shopVerifyUserService.updateStatus(id));
+ }
+
+ @Operation(summary = "修改订单核销人核销权限")
+ @PutMapping("/updateVerifyFlag")
+ public ApiResult 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("删除失败");
+ }
+
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/dto/OrderCreateRequest.java b/src/main/java/com/gxwebsoft/shop/dto/OrderCreateRequest.java
index be9ca22..4d5164a 100644
--- a/src/main/java/com/gxwebsoft/shop/dto/OrderCreateRequest.java
+++ b/src/main/java/com/gxwebsoft/shop/dto/OrderCreateRequest.java
@@ -27,6 +27,9 @@ public class OrderCreateRequest {
@Max(value = 2, message = "订单类型值无效")
private Integer type;
+ @Schema(description = "订单类型 1-及时自配送 2-自提 3-预约自配送 4-发快递 5-配送【系统自动识别电子围栏内转及时配送,电子围栏外发快递】")
+ private Integer orderType;
+
@Size(max = 60, message = "备注长度不能超过60个字符")
@Schema(description = "订单标题")
private String title;
@@ -88,6 +91,12 @@ public class OrderCreateRequest {
@Schema(description = "发货店铺")
private String expressMerchantName;
+ @Schema(description = "配送方式 0-电梯 1-步梯")
+ private Integer deliveryMethod;
+
+ @Schema(description = "楼层")
+ private Integer deliveryFloor;
+
@Schema(description = "订单总额")
@NotNull(message = "订单总额不能为空")
@DecimalMin(value = "0.01", message = "订单总额必须大于0")
@@ -147,6 +156,9 @@ public class OrderCreateRequest {
@NotNull(message = "租户ID不能为空")
private Integer tenantId;
+ @Schema(description = "秒杀活动ID")
+ private Integer activityId;
+
@Schema(description = "订单商品列表")
@Valid
@NotEmpty(message = "订单商品列表不能为空")
@@ -158,6 +170,9 @@ public class OrderCreateRequest {
@Data
@Schema(name = "OrderGoodsItem", description = "订单商品项")
public static class OrderGoodsItem {
+ @Schema(description = "秒杀活动ID")
+ private Integer activityId;
+
@Schema(description = "商品ID", required = true)
@NotNull(message = "商品ID不能为空")
private Integer goodsId;
diff --git a/src/main/java/com/gxwebsoft/shop/dto/PaySuccessTaskDto.java b/src/main/java/com/gxwebsoft/shop/dto/PaySuccessTaskDto.java
new file mode 100644
index 0000000..85a4cb9
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/dto/PaySuccessTaskDto.java
@@ -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;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/dto/ShopDealerCapitalWaterDto.java b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerCapitalWaterDto.java
new file mode 100644
index 0000000..88a8c32
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerCapitalWaterDto.java
@@ -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;
+
+}
+
diff --git a/src/main/java/com/gxwebsoft/shop/dto/ShopDealerRefundDto.java b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerRefundDto.java
new file mode 100644
index 0000000..c1576f3
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerRefundDto.java
@@ -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;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/dto/ShopDealerSettlementDto.java b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerSettlementDto.java
new file mode 100644
index 0000000..2c950e2
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerSettlementDto.java
@@ -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 itemList;
+}
diff --git a/src/main/java/com/gxwebsoft/shop/dto/ShopDealerSettlementItemDto.java b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerSettlementItemDto.java
new file mode 100644
index 0000000..7d57e13
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerSettlementItemDto.java
@@ -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;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/dto/ShopDealerUserReduceDto.java b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerUserReduceDto.java
new file mode 100644
index 0000000..cd9b53b
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/dto/ShopDealerUserReduceDto.java
@@ -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;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/dto/ShopOrderMyVerifyDto.java b/src/main/java/com/gxwebsoft/shop/dto/ShopOrderMyVerifyDto.java
new file mode 100644
index 0000000..39bffd8
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/dto/ShopOrderMyVerifyDto.java
@@ -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;
+}
+
diff --git a/src/main/java/com/gxwebsoft/shop/dto/UserOrderStats.java b/src/main/java/com/gxwebsoft/shop/dto/UserOrderStats.java
index ae563ca..5ce7eeb 100644
--- a/src/main/java/com/gxwebsoft/shop/dto/UserOrderStats.java
+++ b/src/main/java/com/gxwebsoft/shop/dto/UserOrderStats.java
@@ -26,7 +26,7 @@ public class UserOrderStats implements Serializable {
@Schema(description = "待核销(statusFilter=2)")
private Long waitVerify;
- @Schema(description = "待收货(statusFilter=3)")
+ @Schema(description = "待收货/使用(statusFilter=3)")
private Long waitReceive;
@Schema(description = "待评价(statusFilter=4)")
diff --git a/src/main/java/com/gxwebsoft/shop/dto/VerifyShopOrderDto.java b/src/main/java/com/gxwebsoft/shop/dto/VerifyShopOrderDto.java
new file mode 100644
index 0000000..99630eb
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/dto/VerifyShopOrderDto.java
@@ -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;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopActiveImage.java b/src/main/java/com/gxwebsoft/shop/entity/ShopActiveImage.java
new file mode 100644
index 0000000..a2d3de0
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopActiveImage.java
@@ -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 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;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerCapital.java b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerCapital.java
index 0dadc24..1d44e4d 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerCapital.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerCapital.java
@@ -1,10 +1,11 @@
package com.gxwebsoft.shop.entity;
import java.math.BigDecimal;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
+
+import com.baomidou.mybatisplus.annotation.*;
+
import java.time.LocalDateTime;
+
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -20,6 +21,7 @@ import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopDealerCapital对象", description = "分销商资金明细表")
+@TableName("shop_dealer_capital")
public class ShopDealerCapital implements Serializable {
private static final long serialVersionUID = 1L;
@@ -27,26 +29,41 @@ public class ShopDealerCapital implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
+ @Schema(description = "订单编号")
+ private String no;
+
@Schema(description = "分销商用户ID")
private Integer userId;
+ @Schema(description = "变动类型 1-操作冻结账户余额 2-操作提现账户余额【直接结算】 3-解冻 4-退款")
+ private Integer type;
+
@Schema(description = "分销商昵称")
@TableField(exist = false)
private String nickName;
- @Schema(description = "订单编号")
+ @Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "订单状态")
@TableField(exist = false)
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;
- @Schema(description = "金额")
+ @Schema(description = "变更金额")
private BigDecimal money;
+ @Schema(description = "变更后金额")
+ private BigDecimal moneyAfter;
+
+ @Schema(description = "变更冻结金额")
+ private BigDecimal freezeMoney;
+
+ @Schema(description = "变更冻结后金额")
+ private BigDecimal freezeMoneyAfter;
+
@Schema(description = "描述")
private String comments;
@@ -60,9 +77,15 @@ public class ShopDealerCapital implements Serializable {
@Schema(description = "结算月份")
private String month;
+ @Schema(description = "结算标识 0-未结算 1-已结算")
+ private Integer paymentFlag;
+
@Schema(description = "商城ID")
private Integer tenantId;
+ @Schema(description = "创建人")
+ private Integer creator;
+
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@@ -71,4 +94,8 @@ public class ShopDealerCapital implements Serializable {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
+ @Schema(description = "是否删除 0-未删 1-已删")
+ @TableLogic
+ private Integer deleted;
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerOrder.java b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerOrder.java
index 43bbff8..52cf35a 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerOrder.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerOrder.java
@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
+
+import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
@@ -23,6 +25,7 @@ import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopDealerOrder对象", description = "分销商订单记录表")
+@TableName("shop_dealer_order")
public class ShopDealerOrder implements Serializable {
private static final long serialVersionUID = 1L;
@@ -66,6 +69,9 @@ public class ShopDealerOrder implements Serializable {
@TableField(exist = false)
private String firstNickname;
+ @Schema(description = "分销佣金(一级)")
+ private BigDecimal firstMoney;
+
@Schema(description = "分销商用户id(二级)")
private Integer secondUserId;
@@ -73,42 +79,57 @@ public class ShopDealerOrder implements Serializable {
@TableField(exist = false)
private String secondNickname;
- @Schema(description = "分销商用户id(三级)")
- private Integer thirdUserId;
-
- @Schema(description = "分销商用户昵称(三级)")
- @TableField(exist = false)
- private String thirdNickname;
-
- @Schema(description = "分销佣金(一级)")
- private BigDecimal firstMoney;
-
@Schema(description = "分销佣金(二级)")
private BigDecimal secondMoney;
- @Schema(description = "分销佣金(三级)")
+ @Schema(description = "分销商用户id(弃用)")
+ private Integer thirdUserId;
+
+ @Schema(description = "分销商用户昵称(弃用)")
+ @TableField(exist = false)
+ private String thirdNickname;
+
+ @Schema(description = "分销佣金(弃用)")
private BigDecimal thirdMoney;
- @Schema(description = "门店(一级)")
+ @Schema(description = "一级服务商/门店")
private Integer firstDividendUser;
- @Schema(description = "门店名称(一级)")
+ @Schema(description = "一级服务商/门店名称")
@TableField(exist = false)
private String firstDividendUserName;
- @Schema(description = "分红(一级)")
+ @Schema(description = "一级服务商/门店管理津贴")
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;
- @Schema(description = "门店名称(二级)")
+ @Schema(description = "二级服务商/门店名称")
@TableField(exist = false)
private String secondDividendUserName;
- @Schema(description = "分红(二级)")
+ @Schema(description = "二级服务商/门店管理津贴")
private BigDecimal secondDividend;
+ @Schema(description = "二级服务商/门店结算标识 0-否 1-是")
+ private Integer secondDividendFlag;
+
+ @Schema(description = "二级服务商/门店结算单号")
+ private String secondDividendNo;
+
+ @Schema(description = "二级服务商/门店结算时间")
+ private LocalDateTime secondDividendTime;
+
@Schema(description = "佣金比例")
private BigDecimal rate;
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerReferee.java b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerReferee.java
index 24ac1f5..54d84a6 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerReferee.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerReferee.java
@@ -3,6 +3,8 @@ package com.gxwebsoft.shop.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
+
+import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
@@ -87,4 +89,16 @@ public class ShopDealerReferee implements Serializable {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
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;
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerUser.java b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerUser.java
index a3530cc..4272a7e 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerUser.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerUser.java
@@ -1,10 +1,11 @@
package com.gxwebsoft.shop.entity;
import java.math.BigDecimal;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
+
+import com.baomidou.mybatisplus.annotation.*;
+
import java.time.LocalDateTime;
+
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -20,6 +21,7 @@ import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopDealerUser对象", description = "分销商用户记录表")
+@TableName("shop_dealer_user")
public class ShopDealerUser implements Serializable {
private static final long serialVersionUID = 1L;
@@ -102,6 +104,9 @@ public class ShopDealerUser implements Serializable {
@Schema(description = "排序号")
private Integer sortNumber;
+ @Schema(description = "核销权限是否开启 0-未开启 1-已开启")
+ private Boolean verifyFlag;
+
@Schema(description = "是否删除")
private Integer isDelete;
@@ -112,8 +117,18 @@ public class ShopDealerUser implements Serializable {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
+ @Schema(description = "更新人")
+ private Integer updater;
+
@Schema(description = "修改时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
+ @Schema(description = "分销商等级:0-普通用户 1-超级管理员 2-合伙人(总店) 3-合伙人(分店)")
+ private Integer dealerLevel;
+
+ @Schema(description = "删除 0-未删 1-已删")
+ @TableLogic
+ private Integer deleted;
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerWithdraw.java b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerWithdraw.java
index 6289926..752db70 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopDealerWithdraw.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopDealerWithdraw.java
@@ -27,6 +27,9 @@ public class ShopDealerWithdraw implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
+ @Schema(description = "订单号")
+ private String orderNo;
+
@Schema(description = "分销商用户ID")
private Integer userId;
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopFlashSaleActivity.java b/src/main/java/com/gxwebsoft/shop/entity/ShopFlashSaleActivity.java
new file mode 100644
index 0000000..41d06b2
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopFlashSaleActivity.java
@@ -0,0 +1,104 @@
+package com.gxwebsoft.shop.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+
+import com.gxwebsoft.common.core.constants.BaseConstants;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+/**
+ * 秒杀活动
+ *
+ * @author xm
+ * @since 2026-04-22 17:18:17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Schema(name = "ShopFlashSaleActivity对象", description = "秒杀活动")
+@TableName("shop_flash_sale_activity")
+public class ShopFlashSaleActivity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "秒杀活动编号")
+ @TableId(value = "id", type = IdType.AUTO)
+ private Integer id;
+
+ @Schema(description = "秒杀活动名称")
+ private String name;
+
+ @Schema(description = "秒杀活动商品")
+ private Integer goodsId;
+
+ @Schema(description = "商品skuId")
+ private Integer skuId;
+
+ @Schema(description = "商品数量")
+ private Integer num;
+
+ @Schema(description = "秒杀活动商品价格")
+ private BigDecimal price;
+
+ @Schema(description = "活动状态 0-开启 1-关闭")
+ private Integer status;
+
+ @Schema(
+ description = "活动开始时间",
+ type = "string",
+ pattern = BaseConstants.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND,
+ example = "2026-04-01 12:00:00"
+ )
+ private LocalDateTime startTime;
+
+ @Schema(
+ description = "活动结束时间",
+ type = "string",
+ pattern = BaseConstants.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND,
+ example = "2027-01-01 12:00:00"
+ )
+ private LocalDateTime endTime;
+
+ @Schema(description = "活动限购数量")
+ private Integer saleLimit;
+
+ @Schema(description = "库存")
+ private Integer stock;
+
+ @Schema(description = "展示类型,0:普通用户,1:新用户")
+ private Integer displayType;
+
+ @Schema(description = "备注")
+ private String remark;
+
+ @Schema(description = "排序")
+ private Integer sortNumber;
+
+ @Schema(description = "是否弹窗 0-否 1-是")
+ private Integer popFlag;
+
+ @Schema(description = "租户id")
+ private Integer tenantId;
+
+ @Schema(description = "创建者")
+ private String creator;
+
+ @Schema(description = "创建时间", pattern = BaseConstants.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime createTime;
+
+ @Schema(description = "更新者")
+ private String updater;
+
+ @Schema(description = "更新时间")
+ @DateTimeFormat(pattern = BaseConstants.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime updateTime;
+
+ @Schema(description = "是否删除")
+ @TableLogic
+ private Integer deleted;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java b/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java
index 7d31385..0b60932 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopGoods.java
@@ -1,13 +1,16 @@
package com.gxwebsoft.shop.entity;
import java.math.BigDecimal;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
+
+import com.baomidou.mybatisplus.annotation.*;
+
import java.time.LocalDateTime;
+
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
+import java.util.List;
+
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -21,6 +24,7 @@ import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopGoods对象", description = "商品")
+@TableName("shop_goods")
public class ShopGoods implements Serializable {
private static final long serialVersionUID = 1L;
@@ -104,6 +108,15 @@ public class ShopGoods implements Serializable {
@Schema(description = "配送奖金")
private BigDecimal deliveryMoney;
+ @Schema(description = "推广核验佣金比率")
+ private BigDecimal verifyRate;
+
+ @Schema(description = "推广核验上级/指定人")
+ private Integer verifyParentUserId;
+
+ @Schema(description = "推广核验上级佣金比率/指定分佣")
+ private BigDecimal verifyParentRate;
+
@Schema(description = "库存计算方式(10下单减库存 20付款减库存)")
@JsonAlias({"cdeductStockType"})
private Integer deductStockType;
@@ -153,6 +166,12 @@ public class ShopGoods implements Serializable {
@Schema(description = "状态, 0上架 1待上架 2待审核 3审核不通过")
private Integer status;
+ @Schema(description = "活动方式: 0全平台 1新用户专享")
+ private Integer activityType;
+
+ @Schema(description = "配送方式: 0送上门 1限自提")
+ private Integer deliveryMode;
+
@Schema(description = "备注")
private String comments;
@@ -162,15 +181,44 @@ public class ShopGoods implements Serializable {
@Schema(description = "用户ID")
private Integer userId;
+ @Schema(description = "配送方式:1-自配送 2-自提 4-发快递 5-水票 多属性用','隔开")
+ private String deliveryType;
+
+ @Schema(description = "配送方式:1-自配送 2-自提 4-发快递 5-水票 多属性用','隔开")
+ @TableField(exist = false)
+ private List deliveryTypeList;
+
+ @Schema(description = "水票标识 0-否 1-是")
+ private Integer waterTicketFlag;
+
+ @Schema(description = "水票ID")
+ private Integer waterTickerId;
+
+ @Schema(description = "0-按每桶楼层计算 1-固定金额")
+ private Integer deliveryFeeType;
+
+ @Schema(description = "配送楼层步梯费(元)")
+ private BigDecimal deliveryFee;
+
@Schema(description = "租户id")
private Integer tenantId;
+ @Schema(description = "创建人")
+ private Integer creator;
+
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
+ @Schema(description = "修改人")
+ private Integer updater;
+
@Schema(description = "修改时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
+ @Schema(description = "是否删除 0-未删 1-已删")
+ @TableLogic
+ private Integer deleted;
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopGoodsCategory.java b/src/main/java/com/gxwebsoft/shop/entity/ShopGoodsCategory.java
index 6b902cd..ba05f0a 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopGoodsCategory.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopGoodsCategory.java
@@ -2,15 +2,16 @@ package com.gxwebsoft.shop.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
-import java.time.LocalDateTime;
-import com.fasterxml.jackson.annotation.JsonFormat;
import com.baomidou.mybatisplus.annotation.TableLogic;
-import java.io.Serializable;
-import io.swagger.v3.oas.annotations.media.Schema;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
/**
* 商品分类
*
@@ -20,6 +21,7 @@ import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopGoodsCategory对象", description = "商品分类")
+@TableName("shop_goods_category")
public class ShopGoodsCategory implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopGoodsProfit.java b/src/main/java/com/gxwebsoft/shop/entity/ShopGoodsProfit.java
new file mode 100644
index 0000000..d679966
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopGoodsProfit.java
@@ -0,0 +1,65 @@
+package com.gxwebsoft.shop.entity;
+
+import java.math.BigDecimal;
+import com.baomidou.mybatisplus.annotation.IdType;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import java.io.Serializable;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 商品分润设定
+ *
+ * @author xm
+ * @since 2026-05-20 14:33:00
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Schema(name = "ShopGoodsProfit对象", description = "商品分润设定")
+@TableName("shop_goods_profit")
+public class ShopGoodsProfit implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "主键ID")
+ @TableId(value = "id", type = IdType.AUTO)
+ private Integer id;
+
+ @Schema(description = "类型 1-股东分红 2-其他")
+ private Integer type;
+
+ @Schema(description = "商品ID")
+ private Integer goodsId;
+
+ @Schema(description = "用户ID")
+ private Integer userId;
+
+ @Schema(description = "分佣比例 百分比")
+ private BigDecimal profit;
+
+ @Schema(description = "开启状态 0-未开启 1-开启")
+ private Integer status;
+
+ @Schema(description = "租户ID")
+ private Integer tenantId;
+
+ @Schema(description = "创建人")
+ private Integer creator;
+
+ @Schema(description = "创建时间")
+ private LocalDateTime createTime;
+
+ @Schema(description = "更新人")
+ private Integer updater;
+
+ @Schema(description = "更新人")
+ private LocalDateTime updateTime;
+
+ @Schema(description = "删除 0-未删 1-已删")
+ @TableLogic
+ private Integer deleted;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java b/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java
index 97566ce..5d95126 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopOrder.java
@@ -1,12 +1,12 @@
package com.gxwebsoft.shop.entity;
import java.math.BigDecimal;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
+
+import com.baomidou.mybatisplus.annotation.*;
+
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
-import com.baomidou.mybatisplus.annotation.TableLogic;
+
import java.io.Serializable;
import java.util.List;
@@ -25,6 +25,7 @@ import javax.validation.constraints.*;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopOrder对象", description = "订单")
+@TableName("shop_order")
public class ShopOrder implements Serializable {
private static final long serialVersionUID = 1L;
@@ -32,6 +33,9 @@ public class ShopOrder implements Serializable {
@TableId(value = "order_id", type = IdType.AUTO)
private Integer orderId;
+ @Schema(description = "订单类型 1-及时自配送 2-自提 3-预约自配送 4-发快递 5-水票 6-(配送:自配送/快递)")
+ private Integer orderType;
+
@Schema(description = "订单编号")
private String orderNo;
@@ -186,7 +190,7 @@ public class ShopOrder implements Serializable {
@Schema(description = "0未使用,1已完成,2已取消,3取消中,4退款申请中,5退款被拒绝,6退款成功,7客户端申请退款")
private Integer orderStatus;
- @Schema(description = "发货状态(10未发货 20已发货 30部分发货)")
+ @Schema(description = "发货状态(10未发货 20已发货 30部分发货 40已送达)")
private Integer deliveryStatus;
@Schema(description = "发货备注")
@@ -306,6 +310,59 @@ public class ShopOrder implements Serializable {
@NotNull(message = "租户ID不能为空")
private Integer tenantId;
+ @Schema(description = "秒杀活动ID")
+ private Integer activityId;
+
+ @Schema(description = "秒杀活动ID")
+ @TableField(exist = false)
+ private String activityName;
+
+ @Schema(description = "水票订单标识 0-否 1-是")
+ private Integer waterTicketFlag;
+
+ @Schema(description = "核销码")
+ private String verifyCode;
+
+ @Schema(description = "核销状态 0-未核销 1-已核销")
+ private Integer verifyStatus;
+
+ @Schema(description = "推广佣金结算核销过期时间")
+ private LocalDateTime verifyExpTime;
+
+ @Schema(description = "核销时间")
+ private LocalDateTime verifyTime;
+
+ @Schema(description = "核销人")
+ private Integer verifyUser;
+
+ @Schema(description = "核销人")
+ @TableField(exist = false)
+ private String verifyUserName;
+
+ @Schema(description = "推广核销佣金")
+ private BigDecimal verifyMoney;
+
+ @Schema(description = "推广核验上级/指定人")
+ private Integer verifyParentUser;
+
+ @Schema(description = "推广核验上级/指定人佣金")
+ private BigDecimal verifyParentMoney;
+
+ @Schema(description = "门店、服务商结算标识 0-未结算 1-已结算")
+ private Integer teamSettlementFlag;
+
+ @Schema(description = "分红结算标识 0-未结算 1-已结算")
+ private Integer dividendSettlementFlag;
+
+ @Schema(description = "配送方式 0-电梯 1-步梯")
+ private Integer deliveryMethod;
+
+ @Schema(description = "楼层")
+ private Integer deliveryFloor;
+
+ @Schema(description = "配送费(步梯配送特有)")
+ private BigDecimal deliveryFee;
+
@Schema(description = "修改时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@@ -350,4 +407,8 @@ public class ShopOrder implements Serializable {
@TableField(exist = false)
private ShopOrderDelivery shopOrderDelivery;
+
+ @Schema(description = "详细地址")
+ @TableField(exist = false)
+ private String fullAddress;
}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopOrderGoods.java b/src/main/java/com/gxwebsoft/shop/entity/ShopOrderGoods.java
index 195affc..fc886c5 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopOrderGoods.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopOrderGoods.java
@@ -54,6 +54,14 @@ public class ShopOrderGoods implements Serializable {
@Schema(description = "商品名称")
private String goodsName;
+ @Schema(description = "商品名称")
+ @TableField(exist = false)
+ private String activeNam;
+
+ @Schema(description = "核销码")
+ @TableField(exist = false)
+ private String verifyCode;
+
@Schema(description = "商品规格")
private String spec;
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopSurchargeConfig.java b/src/main/java/com/gxwebsoft/shop/entity/ShopSurchargeConfig.java
new file mode 100644
index 0000000..cdf9f85
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopSurchargeConfig.java
@@ -0,0 +1,69 @@
+package com.gxwebsoft.shop.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import java.io.Serializable;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 步梯费用设置
+ *
+ * @author xm
+ * @since 2026-04-28 16:30:00
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Schema(name = "ShopSurchargeConfig对象", description = "步梯费用设置")
+@TableName("shop_surcharge_config")
+public class ShopSurchargeConfig 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 = "商品ID")
+ private Integer goodsId;
+
+ @Schema(description = "价格(元)")
+ private BigDecimal price;
+
+ @Schema(description = "类型 0-步梯费 1-其他")
+ private Integer type;
+
+ @Schema(description = "状态 0-开启 1-关闭")
+ private Integer status;
+
+ @Schema(description = "排序")
+ private Integer sortNumber;
+
+ @Schema(description = "租户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 Boolean deleted;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopUserAddress.java b/src/main/java/com/gxwebsoft/shop/entity/ShopUserAddress.java
index 490e45b..8ae5544 100644
--- a/src/main/java/com/gxwebsoft/shop/entity/ShopUserAddress.java
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopUserAddress.java
@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import java.time.LocalDateTime;
+
+import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -20,6 +22,7 @@ import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopUserAddress对象", description = "收货地址")
+@TableName("shop_user_address")
public class ShopUserAddress implements Serializable {
private static final long serialVersionUID = 1L;
@@ -48,7 +51,7 @@ public class ShopUserAddress implements Serializable {
@Schema(description = "收货地址")
private String address;
- @Schema(description = "收货地址")
+ @Schema(description = "详细地址")
private String fullAddress;
private String lat;
diff --git a/src/main/java/com/gxwebsoft/shop/entity/ShopVerifyUser.java b/src/main/java/com/gxwebsoft/shop/entity/ShopVerifyUser.java
new file mode 100644
index 0000000..a16f037
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/entity/ShopVerifyUser.java
@@ -0,0 +1,61 @@
+package com.gxwebsoft.shop.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+
+import java.time.LocalDateTime;
+import java.io.Serializable;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 订单核销人管理
+ *
+ * @author xm
+ * @since 2026-05-25 17:13:00
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Schema(name = "ShopVerifyUser对象", description = "订单核销人管理")
+@TableName("shop_verify_user")
+public class ShopVerifyUser implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "主键ID")
+ @TableId(value = "id", type = IdType.AUTO)
+ private Integer id;
+
+ @Schema(description = "用户ID")
+ private Integer userId;
+
+ @Schema(description = "用户名称")
+ @TableField(exist = false)
+ private String userName;
+
+ @Schema(description = "核销权限是否开启 0-未开启 1-已开启")
+ private Integer verifyFlag;
+
+ @Schema(description = "是否开启 0-未开始 1-已开启")
+ private Integer status;
+
+ @Schema(description = "排序号")
+ private Integer sortNumber;
+
+ @Schema(description = "创建人")
+ private Integer creator;
+
+ @Schema(description = "创建时间")
+ private LocalDateTime createTime;
+
+ @Schema(description = "更新人")
+ private Integer updater;
+
+ @Schema(description = "更新时间")
+ private LocalDateTime updateTime;
+
+ @Schema(description = "是否删除 0-未删 1-已删")
+ @TableLogic
+ private Integer deleted;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopActiveImageMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopActiveImageMapper.java
new file mode 100644
index 0000000..b747c4f
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopActiveImageMapper.java
@@ -0,0 +1,37 @@
+package com.gxwebsoft.shop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gxwebsoft.shop.entity.ShopActiveImage;
+import com.gxwebsoft.shop.param.ShopActiveImageParam;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 推广码底图Mapper
+ *
+ * @author xm
+ * @since 2026-04-27 18:02:17
+ */
+public interface ShopActiveImageMapper extends BaseMapper {
+
+ /**
+ * 分页查询
+ *
+ * @param page 分页对象
+ * @param param 查询参数
+ * @return List
+ */
+ List selectPageRel(@Param("page") IPage page,
+ @Param("param") ShopActiveImageParam param);
+
+ /**
+ * 查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List selectListRel(@Param("param") ShopActiveImageParam param);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerCapitalMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerCapitalMapper.java
index d996c07..fd02d59 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerCapitalMapper.java
+++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerCapitalMapper.java
@@ -2,8 +2,11 @@ package com.gxwebsoft.shop.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gxwebsoft.shop.dto.ShopDealerCapitalWaterDto;
import com.gxwebsoft.shop.entity.ShopDealerCapital;
import com.gxwebsoft.shop.param.ShopDealerCapitalParam;
+import com.gxwebsoft.shop.vo.ShopDealerCapitalWaterVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -34,4 +37,12 @@ public interface ShopDealerCapitalMapper extends BaseMapper {
*/
List selectListRel(@Param("param") ShopDealerCapitalParam param);
+ /**
+ * 查询个人资金流水
+ * @param page
+ * @param entity
+ * @return
+ */
+ IPage getPageInfo(@Param("page") Page page, @Param("entity") ShopDealerCapitalWaterDto entity);
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerOrderMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerOrderMapper.java
index 928b066..01776aa 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerOrderMapper.java
+++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerOrderMapper.java
@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.shop.entity.ShopDealerOrder;
import com.gxwebsoft.shop.param.ShopDealerOrderParam;
+import com.gxwebsoft.shop.vo.ShopDealerOrderTaskVO;
+import com.gxwebsoft.shop.vo.ShopDealerOrderTodayRevenueVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -34,4 +36,18 @@ public interface ShopDealerOrderMapper extends BaseMapper {
*/
List selectListRel(@Param("param") ShopDealerOrderParam param);
+ /**
+ * 获取订单门店/分销商待结算数据
+ * @param tenantId 租户ID
+ * @return
+ */
+ List getDealerOrderList(@Param("tenantId") Integer tenantId);
+
+ /**
+ * 获取用户当日分佣数据
+ * @param userId
+ * @return
+ */
+ List getTodayRevenue(@Param("userId") Integer userId);
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerRefereeMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerRefereeMapper.java
index 70ddf37..0fffecd 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerRefereeMapper.java
+++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopDealerRefereeMapper.java
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.shop.entity.ShopDealerReferee;
import com.gxwebsoft.shop.param.ShopDealerRefereeParam;
+import com.gxwebsoft.shop.vo.ShopDealerRefereeVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -34,4 +35,11 @@ public interface ShopDealerRefereeMapper extends BaseMapper {
*/
List selectListRel(@Param("param") ShopDealerRefereeParam param);
+ /**
+ * 通过用户ID查询上一级分销人信息【type = 0】
+ * @param userId 用户ID
+ * @return
+ */
+ ShopDealerRefereeVO getDealerIdByUserId(@Param("userId") Integer userId);
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopFlashSaleActivityMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopFlashSaleActivityMapper.java
new file mode 100644
index 0000000..d58b0b6
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopFlashSaleActivityMapper.java
@@ -0,0 +1,38 @@
+package com.gxwebsoft.shop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gxwebsoft.shop.entity.ShopFlashSaleActivity;
+import com.gxwebsoft.shop.param.ShopFlashSaleActivityParam;
+import com.gxwebsoft.shop.vo.ShopFlashSaleActivityVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 秒杀活动Mapper
+ *
+ * @author xm
+ * @since 2026-04-22 17:18:17
+ */
+public interface ShopFlashSaleActivityMapper extends BaseMapper {
+
+ /**
+ * 分页查询
+ *
+ * @param page 分页对象
+ * @param param 查询参数
+ * @return List
+ */
+ List selectPageRel(@Param("page") IPage page,
+ @Param("param") ShopFlashSaleActivityParam param);
+
+ /**
+ * 查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List selectListRel(@Param("param") ShopFlashSaleActivityParam param);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopGoodsProfitMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopGoodsProfitMapper.java
new file mode 100644
index 0000000..6ef99c6
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopGoodsProfitMapper.java
@@ -0,0 +1,37 @@
+package com.gxwebsoft.shop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gxwebsoft.shop.entity.ShopGoodsProfit;
+import com.gxwebsoft.shop.param.ShopGoodsProfitParam;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 商品分润设定Mapper
+ *
+ * @author xm
+ * @since 2026-05-20 14:33:00
+ */
+public interface ShopGoodsProfitMapper extends BaseMapper {
+
+ /**
+ * 分页查询
+ *
+ * @param page 分页对象
+ * @param param 查询参数
+ * @return List
+ */
+ List selectPageRel(@Param("page") IPage page,
+ @Param("param") ShopGoodsProfitParam param);
+
+ /**
+ * 查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List selectListRel(@Param("param") ShopGoodsProfitParam param);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopOrderMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopOrderMapper.java
index e36a492..c5a7083 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/ShopOrderMapper.java
+++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopOrderMapper.java
@@ -3,11 +3,15 @@ package com.gxwebsoft.shop.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.gxwebsoft.shop.dto.ShopOrderMyVerifyDto;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.param.ShopOrderParam;
+import com.gxwebsoft.shop.vo.ShopOrderGoodsInfoVO;
+import com.gxwebsoft.shop.vo.ShopOrderGoodsVO;
+import com.gxwebsoft.shop.vo.ShopOrderMyVerifyItemVO;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
-import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
@@ -60,4 +64,27 @@ public interface ShopOrderMapper extends BaseMapper {
Map selectUserOrderStats(@Param("userId") Integer userId,
@Param("tenantId") Integer tenantId,
@Param("type") Integer type);
+
+ /**
+ * 通过订单号查询订单所有商品分润信息
+ * @param orderNo 订单号
+ * @return
+ */
+ List getOrderGoodsInfo(@Param("orderNo") String orderNo);
+
+ /**
+ * 查询我的核销订单数据
+ * @param page
+ * @param entity
+ * @return
+ */
+ IPage getMyVerifyPageInfo(@Param("page") Page page, @Param("entity") ShopOrderMyVerifyDto entity);
+
+ /**
+ * 通过订单号查询订单商品名称
+ * @param orderNoList
+ * @return
+ */
+ List getOrderGoodsInfoByOrderNos(@Param("orderNoList") List orderNoList);
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopSurchargeConfigMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopSurchargeConfigMapper.java
new file mode 100644
index 0000000..7674ee8
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopSurchargeConfigMapper.java
@@ -0,0 +1,37 @@
+package com.gxwebsoft.shop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gxwebsoft.shop.entity.ShopSurchargeConfig;
+import com.gxwebsoft.shop.param.ShopSurchargeConfigParam;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 步梯费用设置Mapper
+ *
+ * @author xm
+ * @since 2026-04-28 16:30:00
+ */
+public interface ShopSurchargeConfigMapper extends BaseMapper {
+
+ /**
+ * 分页查询
+ *
+ * @param page 分页对象
+ * @param param 查询参数
+ * @return List
+ */
+ List selectPageRel(@Param("page") IPage page,
+ @Param("param") ShopSurchargeConfigParam param);
+
+ /**
+ * 查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List selectListRel(@Param("param") ShopSurchargeConfigParam param);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/ShopVerifyUserMapper.java b/src/main/java/com/gxwebsoft/shop/mapper/ShopVerifyUserMapper.java
new file mode 100644
index 0000000..4144e63
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/ShopVerifyUserMapper.java
@@ -0,0 +1,37 @@
+package com.gxwebsoft.shop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.gxwebsoft.shop.entity.ShopVerifyUser;
+import com.gxwebsoft.shop.param.ShopVerifyUserParam;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 订单核销人管理Mapper
+ *
+ * @author xm
+ * @since 2026-05-25 17:13:00
+ */
+public interface ShopVerifyUserMapper extends BaseMapper {
+
+ /**
+ * 分页查询
+ *
+ * @param page 分页对象
+ * @param param 查询参数
+ * @return List
+ */
+ List selectPageRel(@Param("page") IPage page,
+ @Param("param") ShopVerifyUserParam param);
+
+ /**
+ * 查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List selectListRel(@Param("param") ShopVerifyUserParam param);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopActiveImageMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopActiveImageMapper.xml
new file mode 100644
index 0000000..7d1efd8
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopActiveImageMapper.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+ SELECT a.*
+ FROM shop_active_image a
+
+
+ AND a.id = #{param.id}
+
+
+ AND a.name LIKE CONCAT('%', #{param.name}, '%')
+
+
+ AND a.type = #{param.type}
+
+
+ AND a.img_url LIKE CONCAT('%', #{param.imgUrl}, '%')
+
+
+ AND a.status = #{param.status}
+
+
+ AND a.sort_number = #{param.sortNumber}
+
+
+ AND a.creator LIKE CONCAT('%', #{param.creator}, '%')
+
+
+ AND a.create_time >= #{param.createTimeStart}
+
+
+ AND a.create_time <= #{param.createTimeEnd}
+
+
+ AND a.updater LIKE CONCAT('%', #{param.updater}, '%')
+
+
+ AND a.deleted = #{param.deleted}
+
+
+ AND a.deleted = 0
+
+
+ AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
+ )
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerCapitalMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerCapitalMapper.xml
index d1fa10e..688e3b9 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerCapitalMapper.xml
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerCapitalMapper.xml
@@ -40,7 +40,6 @@
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
- OR a.user_id = #{param.keywords}
OR a.order_no = #{param.keywords}
)
@@ -56,5 +55,23 @@
+
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerOrderMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerOrderMapper.xml
index b49c42d..b1cc0eb 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerOrderMapper.xml
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerOrderMapper.xml
@@ -89,5 +89,46 @@
+
+
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerRefereeMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerRefereeMapper.xml
index a8635f1..72960dd 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerRefereeMapper.xml
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerRefereeMapper.xml
@@ -57,5 +57,21 @@
+
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerUserMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerUserMapper.xml
index f103c11..d6e3c30 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerUserMapper.xml
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopDealerUserMapper.xml
@@ -63,6 +63,9 @@
AND a.sort_number = #{param.sortNumber}
+
+ AND a.dealer_level = #{param.dealerLevel}
+
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
OR a.user_id = #{param.keywords} OR a.dealer_name LIKE CONCAT('%', #{param.keywords}, '%') OR a.real_name LIKE CONCAT('%', #{param.keywords}, '%') OR a.mobile LIKE CONCAT('%', #{param.keywords}, '%')
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopFlashSaleActivityMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopFlashSaleActivityMapper.xml
new file mode 100644
index 0000000..60a78a9
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopFlashSaleActivityMapper.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+ SELECT a.*
+ FROM shop_flash_sale_activity a
+
+
+ AND a.id = #{param.id}
+
+
+ AND a.name LIKE CONCAT('%', #{param.name}, '%')
+
+
+ AND a.goods_id LIKE CONCAT('%', #{param.goodsId}, '%')
+
+
+ AND a.status = #{param.status}
+
+
+ AND a.start_time LIKE CONCAT('%', #{param.startTime}, '%')
+
+
+ AND a.end_time LIKE CONCAT('%', #{param.endTime}, '%')
+
+
+ AND a.stock = #{param.stock}
+
+
+ AND a.display_type = #{param.displayType}
+
+
+ AND a.remark LIKE CONCAT('%', #{param.remark}, '%')
+
+
+ AND a.sort_number = #{param.sortNumber}
+
+
+ AND a.creator LIKE CONCAT('%', #{param.creator}, '%')
+
+
+ AND a.create_time >= #{param.createTimeStart}
+
+
+ AND a.create_time <= #{param.createTimeEnd}
+
+
+ AND a.updater LIKE CONCAT('%', #{param.updater}, '%')
+
+
+ AND a.deleted = #{param.deleted}
+
+
+ AND a.deleted = 0
+
+
+ AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
+ )
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsMapper.xml
index a14c354..3a74a14 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsMapper.xml
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsMapper.xml
@@ -110,6 +110,9 @@
AND a.deleted = #{param.deleted}
+
+ AND a.water_ticket_flag = #{param.waterTicketFlag}
+
AND a.deleted = 0
@@ -135,6 +138,12 @@
OR a.comments LIKE CONCAT('%', #{param.keywords}, '%')
)
+
+ AND a.activity_type = #{param.activityType}
+
+
+ AND a.delivery_mode = #{param.deliveryMode}
+
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsProfitMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsProfitMapper.xml
new file mode 100644
index 0000000..79bf817
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopGoodsProfitMapper.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+ SELECT a.*
+ FROM shop_goods_profit a
+
+
+ AND a.id = #{param.id}
+
+
+ AND a.type = #{param.type}
+
+
+ AND a.goods_id = #{param.goodsId}
+
+
+ AND a.user_id = #{param.userId}
+
+
+ AND a.profit = #{param.profit}
+
+
+ AND a.status = #{param.status}
+
+
+ AND a.creator = #{param.creator}
+
+
+ AND a.create_time >= #{param.createTimeStart}
+
+
+ AND a.create_time <= #{param.createTimeEnd}
+
+
+ AND a.updater = #{param.updater}
+
+
+ AND a.deleted = #{param.deleted}
+
+
+ AND a.deleted = 0
+
+
+ AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
+ )
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopOrderMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopOrderMapper.xml
index 0c28dbf..a9a07ef 100644
--- a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopOrderMapper.xml
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopOrderMapper.xml
@@ -233,10 +233,6 @@
OR b.nickname LIKE CONCAT('%', #{param.keywords}, '%')
)
-
-
- AND a.order_status != 2
-
@@ -245,27 +241,27 @@
- AND a.pay_status = 1 AND a.delivery_status = 10 AND a.order_status = 0
+ AND a.pay_status = 1 AND a.delivery_status = 10 AND a.order_status = 0 AND a.order_type in(1, 3, 4, 5)
- AND a.pay_status = 1 AND a.order_status = 0
+ AND a.pay_status = 1 AND a.order_status = 0 and order_type = 2
-
- AND a.delivery_status = 20 AND a.order_status != 1
+
+ AND a.pay_status = 1 AND a.order_status = 0 AND (a.delivery_status = 20 or (a.order_type = 2 and a.delivery_status = 10))
- AND a.order_status = 1 AND a.evaluate_status = 0
+ AND a.pay_status = 1 AND a.order_status = 1 AND a.evaluate_status = 0
- AND a.order_status = 1
+ AND a.pay_status = 1 AND a.order_status = 1
- AND (a.order_status = 4 OR a.order_status = 5 OR a.order_status = 6 OR a.order_status = 7)
+ AND a.pay_status = 1 AND a.order_status in(4, 5, 6, 7)
@@ -301,13 +297,13 @@
SELECT
-- “全部”默认也会过滤已关闭(取消)订单:statusFilter != 8 时会加 a.order_status != 2
COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status != 2 THEN 1 ELSE 0 END), 0) AS total,
- COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status != 2 AND a.pay_status = 0 AND a.order_status = 0 THEN 1 ELSE 0 END), 0) AS waitPay,
- COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status != 2 AND a.pay_status = 1 AND a.delivery_status = 10 AND a.order_status = 0 THEN 1 ELSE 0 END), 0) AS waitDeliver,
- COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status != 2 AND a.pay_status = 1 AND a.order_status = 0 THEN 1 ELSE 0 END), 0) AS waitVerify,
- COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status != 2 AND a.delivery_status = 20 AND a.order_status != 1 THEN 1 ELSE 0 END), 0) AS waitReceive,
- COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status != 2 AND a.order_status = 1 AND a.evaluate_status = 0 THEN 1 ELSE 0 END), 0) AS waitComment,
- COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status != 2 AND a.order_status = 1 THEN 1 ELSE 0 END), 0) AS completed,
- COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status != 2 AND (a.order_status = 4 OR a.order_status = 5 OR a.order_status = 6 OR a.order_status = 7) THEN 1 ELSE 0 END), 0) AS refund,
+ COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.pay_status = 0 AND a.order_status = 0 THEN 1 ELSE 0 END), 0) AS waitPay,
+ COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.pay_status = 1 AND a.order_status = 0 AND a.order_type in(1, 3, 4, 5) AND a.delivery_status = 10 THEN 1 ELSE 0 END), 0) AS waitDeliver,
+ COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.pay_status = 1 AND a.order_status = 0 AND a.order_type = 2 THEN 1 ELSE 0 END), 0) AS waitVerify,
+ COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.pay_status = 1 AND a.order_status = 0 AND (a.delivery_status = 20 or (a.order_type = 2 and a.delivery_status = 10)) THEN 1 ELSE 0 END), 0) AS waitReceive,
+ COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.pay_status = 1 AND a.order_status = 1 AND a.evaluate_status = 0 THEN 1 ELSE 0 END), 0) AS waitComment,
+-- COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.pay_status = 1 AND a.order_status = 1 THEN 1 ELSE 0 END), 0) AS completed,
+-- COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.pay_status = 1 AND a.order_status in(4, 5, 6, 7) THEN 1 ELSE 0 END), 0) AS refund,
COALESCE(SUM(CASE WHEN a.deleted = 1 THEN 1 ELSE 0 END), 0) AS deleted,
COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status = 2 THEN 1 ELSE 0 END), 0) AS canceled
FROM shop_order a
@@ -324,11 +320,86 @@
-
-
+
+
+
+
UPDATE shop_order
+ update_time = NOW(),
+
+ expiration_time = #{param.expirationTime},
+
pay_type = #{param.payType},
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopSurchargeConfigMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopSurchargeConfigMapper.xml
new file mode 100644
index 0000000..6827f44
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopSurchargeConfigMapper.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+ SELECT a.*
+ FROM shop_surcharge_config a
+
+
+ AND a.id = #{param.id}
+
+
+ AND a.name LIKE CONCAT('%', #{param.name}, '%')
+
+
+ AND a.type = #{param.type}
+
+
+ AND a.status = #{param.status}
+
+
+ AND a.sort_number = #{param.sortNumber}
+
+
+ AND a.creator LIKE CONCAT('%', #{param.creator}, '%')
+
+
+ AND a.create_time >= #{param.createTimeStart}
+
+
+ AND a.create_time <= #{param.createTimeEnd}
+
+
+ AND a.updater LIKE CONCAT('%', #{param.updater}, '%')
+
+
+ AND a.deleted = #{param.deleted}
+
+
+ AND a.deleted = 0
+
+
+ AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
+ )
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopVerifyUserMapper.xml b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopVerifyUserMapper.xml
new file mode 100644
index 0000000..61b545a
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/mapper/xml/ShopVerifyUserMapper.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+ SELECT a.*
+ FROM shop_verify_user a
+
+
+ AND a.id = #{param.id}
+
+
+ AND a.user_id = #{param.userId}
+
+
+ AND a.verify_flag = #{param.verifyFlag}
+
+
+ AND a.status = #{param.status}
+
+
+ AND a.creator = #{param.creator}
+
+
+ AND a.create_time >= #{param.createTimeStart}
+
+
+ AND a.create_time <= #{param.createTimeEnd}
+
+
+ AND a.updater = #{param.updater}
+
+
+ AND a.deleted = #{param.deleted}
+
+
+ AND a.deleted = 0
+
+
+ AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
+ )
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/gxwebsoft/shop/param/ShopActiveImageParam.java b/src/main/java/com/gxwebsoft/shop/param/ShopActiveImageParam.java
new file mode 100644
index 0000000..f47df2a
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/param/ShopActiveImageParam.java
@@ -0,0 +1,59 @@
+package com.gxwebsoft.shop.param;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.gxwebsoft.common.core.annotation.QueryField;
+import com.gxwebsoft.common.core.annotation.QueryType;
+import com.gxwebsoft.common.core.web.BaseParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 推广码底图查询参数
+ *
+ * @author xm
+ * @since 2026-04-27 18:02:17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Schema(name = "ShopActiveImageParam对象", description = "推广码底图查询参数")
+public class ShopActiveImageParam extends BaseParam {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "主键ID")
+ @QueryField(type = QueryType.EQ)
+ private Integer id;
+
+ @Schema(description = "名称")
+ private String name;
+
+ @Schema(description = "类型 0-推广底图 1-其他")
+ @QueryField(type = QueryType.EQ)
+ private Integer type;
+
+ @Schema(description = "图片地址")
+ private String imgUrl;
+
+ @Schema(description = "分享底图")
+ private String shareImg;
+
+ @Schema(description = "启用状态 0-启用 1-禁用")
+ @QueryField(type = QueryType.EQ)
+ private Integer status;
+
+ @Schema(description = "排序")
+ @QueryField(type = QueryType.EQ)
+ private Integer sortNumber;
+
+ @Schema(description = "创建人")
+ private String creator;
+
+ @Schema(description = "更新人")
+ private String updater;
+
+ @Schema(description = "是否删除 0-未删 1-已删")
+ @QueryField(type = QueryType.EQ)
+ private Integer deleted;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/param/ShopDealerUserParam.java b/src/main/java/com/gxwebsoft/shop/param/ShopDealerUserParam.java
index 9953b1f..6003335 100644
--- a/src/main/java/com/gxwebsoft/shop/param/ShopDealerUserParam.java
+++ b/src/main/java/com/gxwebsoft/shop/param/ShopDealerUserParam.java
@@ -86,4 +86,8 @@ public class ShopDealerUserParam extends BaseParam {
@QueryField(type = QueryType.EQ)
private Integer isDelete;
+ @Schema(description = "分销商等级:0-普通用户 1-超级管理员 2-合伙人(总店) 3-合伙人(分店)")
+ @QueryField(type = QueryType.EQ)
+ private Integer dealerLevel;
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/param/ShopFlashSaleActivityParam.java b/src/main/java/com/gxwebsoft/shop/param/ShopFlashSaleActivityParam.java
new file mode 100644
index 0000000..fba65fd
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/param/ShopFlashSaleActivityParam.java
@@ -0,0 +1,85 @@
+package com.gxwebsoft.shop.param;
+
+import java.math.BigDecimal;
+import com.gxwebsoft.common.core.annotation.QueryField;
+import com.gxwebsoft.common.core.annotation.QueryType;
+import com.gxwebsoft.common.core.web.BaseParam;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 秒杀活动查询参数
+ *
+ * @author xm
+ * @since 2026-04-22 17:18:17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Schema(name = "ShopFlashSaleActivityParam对象", description = "秒杀活动查询参数")
+public class ShopFlashSaleActivityParam extends BaseParam {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "秒杀活动编号")
+ @QueryField(type = QueryType.EQ)
+ private Integer id;
+
+ @Schema(description = "秒杀活动名称")
+ private String name;
+
+ @Schema(description = "秒杀活动商品")
+ private Integer goodsId;
+
+ @Schema(description = "商品skuId")
+ private Integer skuId;
+
+ @Schema(description = "商品数量")
+ private Integer num;
+
+ @Schema(description = "秒杀活动商品价格")
+ private BigDecimal price;
+
+ @Schema(description = "活动状态 0-开启 1-关闭")
+ @QueryField(type = QueryType.EQ)
+ private Integer status;
+
+ @Schema(description = "活动开始时间")
+ private String startTime;
+
+ @Schema(description = "活动结束时间")
+ private String endTime;
+
+ @Schema(description = "活动限购数量")
+ private Integer saleLimit;
+
+ @Schema(description = "库存")
+ @QueryField(type = QueryType.EQ)
+ private Integer stock;
+
+ @Schema(description = "展示类型,0:普通用户,1:新用户")
+ @QueryField(type = QueryType.EQ)
+ private Integer displayType;
+
+ @Schema(description = "备注")
+ private String remark;
+
+ @Schema(description = "排序")
+ @QueryField(type = QueryType.EQ)
+ private Integer sortNumber;
+
+ @Schema(description = "是否弹窗 0-否 1-是")
+ private Integer popFlag;
+
+ @Schema(description = "创建者")
+ private String creator;
+
+ @Schema(description = "更新者")
+ private String updater;
+
+ @Schema(description = "是否删除")
+ @QueryField(type = QueryType.EQ)
+ private Integer deleted;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/param/ShopGoodsParam.java b/src/main/java/com/gxwebsoft/shop/param/ShopGoodsParam.java
index 0138743..69beb5f 100644
--- a/src/main/java/com/gxwebsoft/shop/param/ShopGoodsParam.java
+++ b/src/main/java/com/gxwebsoft/shop/param/ShopGoodsParam.java
@@ -1,15 +1,17 @@
package com.gxwebsoft.shop.param;
-import java.math.BigDecimal;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.gxwebsoft.common.core.annotation.QueryField;
import com.gxwebsoft.common.core.annotation.QueryType;
import com.gxwebsoft.common.core.web.BaseParam;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
/**
* 商品查询参数
*
@@ -150,4 +152,41 @@ public class ShopGoodsParam extends BaseParam {
@QueryField(type = QueryType.EQ)
private Integer deleted;
+ @Schema(description = "活动方式: 0全平台 1新用户专享")
+ @QueryField(type = QueryType.EQ)
+ private Integer activityType;
+
+ @Schema(description = "配送方式: 0送上门 1限自提")
+ @QueryField(type = QueryType.EQ)
+ private Integer deliveryMode;
+
+ @Schema(description = "配送方式:1-自配送 2-自提 4-发快递 多属性用','隔开")
+ private String deliveryType;
+
+ @Schema(description = "水票标识 0-否 1-是")
+ private Integer waterTicketFlag;
+
+ @Schema(description = "水票ID")
+ private Integer waterTickerId;
+
+ @Schema(description = "0-按每桶楼层计算 1-固定金额")
+ private Integer deliveryFeeType;
+
+ @Schema(description = "租户id")
+ private Integer tenantId;
+
+ @Schema(description = "创建人")
+ private Integer creator;
+
+ @Schema(description = "创建时间")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime createTime;
+
+ @Schema(description = "修改人")
+ private Integer updater;
+
+ @Schema(description = "修改时间")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private LocalDateTime updateTime;
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/param/ShopGoodsProfitParam.java b/src/main/java/com/gxwebsoft/shop/param/ShopGoodsProfitParam.java
new file mode 100644
index 0000000..1014418
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/param/ShopGoodsProfitParam.java
@@ -0,0 +1,61 @@
+package com.gxwebsoft.shop.param;
+
+import java.math.BigDecimal;
+import com.gxwebsoft.common.core.annotation.QueryField;
+import com.gxwebsoft.common.core.annotation.QueryType;
+import com.gxwebsoft.common.core.web.BaseParam;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 商品分润设定查询参数
+ *
+ * @author xm
+ * @since 2026-05-20 14:33:00
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Schema(name = "ShopGoodsProfitParam对象", description = "商品分润设定查询参数")
+public class ShopGoodsProfitParam extends BaseParam {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "主键ID")
+ @QueryField(type = QueryType.EQ)
+ private Integer id;
+
+ @Schema(description = "类型 1-股东分红 2-其他")
+ @QueryField(type = QueryType.EQ)
+ private Integer type;
+
+ @Schema(description = "商品ID")
+ @QueryField(type = QueryType.EQ)
+ private Integer goodsId;
+
+ @Schema(description = "用户ID")
+ @QueryField(type = QueryType.EQ)
+ private Integer userId;
+
+ @Schema(description = "分佣比例 百分比")
+ @QueryField(type = QueryType.EQ)
+ private BigDecimal profit;
+
+ @Schema(description = "开启状态 0-未开启 1-开启")
+ @QueryField(type = QueryType.EQ)
+ private Integer status;
+
+ @Schema(description = "创建人")
+ @QueryField(type = QueryType.EQ)
+ private Integer creator;
+
+ @Schema(description = "更新人")
+ @QueryField(type = QueryType.EQ)
+ private Integer updater;
+
+ @Schema(description = "删除 0-未删 1-已删")
+ @QueryField(type = QueryType.EQ)
+ private Integer deleted;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/param/ShopOrderParam.java b/src/main/java/com/gxwebsoft/shop/param/ShopOrderParam.java
index 2d61365..becee7b 100644
--- a/src/main/java/com/gxwebsoft/shop/param/ShopOrderParam.java
+++ b/src/main/java/com/gxwebsoft/shop/param/ShopOrderParam.java
@@ -1,15 +1,15 @@
package com.gxwebsoft.shop.param;
-import java.math.BigDecimal;
+import com.fasterxml.jackson.annotation.JsonInclude;
import com.gxwebsoft.common.core.annotation.QueryField;
import com.gxwebsoft.common.core.annotation.QueryType;
import com.gxwebsoft.common.core.web.BaseParam;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
+import java.math.BigDecimal;
+
/**
* 订单查询参数
*
diff --git a/src/main/java/com/gxwebsoft/shop/param/ShopSurchargeConfigParam.java b/src/main/java/com/gxwebsoft/shop/param/ShopSurchargeConfigParam.java
new file mode 100644
index 0000000..ac1ea6e
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/param/ShopSurchargeConfigParam.java
@@ -0,0 +1,57 @@
+package com.gxwebsoft.shop.param;
+
+import java.math.BigDecimal;
+import com.gxwebsoft.common.core.annotation.QueryField;
+import com.gxwebsoft.common.core.annotation.QueryType;
+import com.gxwebsoft.common.core.web.BaseParam;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 步梯费用设置查询参数
+ *
+ * @author xm
+ * @since 2026-04-28 16:30:00
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Schema(name = "ShopSurchargeConfigParam对象", description = "步梯费用设置查询参数")
+public class ShopSurchargeConfigParam extends BaseParam {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "主键ID")
+ @QueryField(type = QueryType.EQ)
+ private Integer id;
+
+ @Schema(description = "名称")
+ private String name;
+
+ @Schema(description = "价格(元)")
+ private BigDecimal price;
+
+ @Schema(description = "类型 0-步梯费 1-其他")
+ @QueryField(type = QueryType.EQ)
+ private Integer type;
+
+ @Schema(description = "状态 0-开启 1-关闭")
+ @QueryField(type = QueryType.EQ)
+ private Integer status;
+
+ @Schema(description = "排序")
+ @QueryField(type = QueryType.EQ)
+ private Integer sortNumber;
+
+ @Schema(description = "创建人")
+ private String creator;
+
+ @Schema(description = "修改人")
+ private String updater;
+
+ @Schema(description = "是否删除 0-未删 1-已删")
+ @QueryField(type = QueryType.EQ)
+ private Boolean deleted;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/param/ShopVerifyUserParam.java b/src/main/java/com/gxwebsoft/shop/param/ShopVerifyUserParam.java
new file mode 100644
index 0000000..dd6ef77
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/param/ShopVerifyUserParam.java
@@ -0,0 +1,52 @@
+package com.gxwebsoft.shop.param;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.gxwebsoft.common.core.annotation.QueryField;
+import com.gxwebsoft.common.core.annotation.QueryType;
+import com.gxwebsoft.common.core.web.BaseParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 订单核销人管理查询参数
+ *
+ * @author xm
+ * @since 2026-05-25 17:13:00
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Schema(name = "ShopVerifyUserParam对象", description = "订单核销人管理查询参数")
+public class ShopVerifyUserParam extends BaseParam {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(description = "主键ID")
+ @QueryField(type = QueryType.EQ)
+ private Integer id;
+
+ @Schema(description = "用户ID")
+ @QueryField(type = QueryType.EQ)
+ private Integer userId;
+
+ @Schema(description = "核销权限是否开启 0-未开启 1-已开启")
+ @QueryField(type = QueryType.EQ)
+ private Integer verifyFlag;
+
+ @Schema(description = "是否开启 0-未开始 1-已开启")
+ @QueryField(type = QueryType.EQ)
+ private Integer status;
+
+ @Schema(description = "创建人")
+ @QueryField(type = QueryType.EQ)
+ private Integer creator;
+
+ @Schema(description = "更新人")
+ @QueryField(type = QueryType.EQ)
+ private Integer updater;
+
+ @Schema(description = "是否删除 0-未删 1-已删")
+ @QueryField(type = QueryType.EQ)
+ private Integer deleted;
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java b/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java
index ed8b43c..9c9548d 100644
--- a/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java
+++ b/src/main/java/com/gxwebsoft/shop/service/OrderBusinessService.java
@@ -1,13 +1,16 @@
package com.gxwebsoft.shop.service;
import cn.hutool.core.util.IdUtil;
-import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.system.entity.User;
+import com.gxwebsoft.common.system.redis.OrderNoUtils;
import com.gxwebsoft.shop.config.OrderConfigProperties;
import com.gxwebsoft.shop.dto.OrderCreateRequest;
import com.gxwebsoft.shop.entity.*;
-import com.gxwebsoft.shop.service.ShopStoreFenceService;
+import com.gxwebsoft.shop.mapper.ShopFlashSaleActivityMapper;
+import com.gxwebsoft.shop.mapper.ShopOrderMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
@@ -17,8 +20,10 @@ import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -35,9 +40,16 @@ public class OrderBusinessService {
private static final int DEDUCT_STOCK_TYPE_ORDER = 10; // 下单减库存
private static final int DEDUCT_STOCK_TYPE_PAY = 20; // 付款减库存
+ // 去除易混淆字符:0 O 1 I L 8 B Z
+ private static final String CHAR_POOL = "2345679ACDEFGHJKMNPQRSTUVWXY";
+ private static final SecureRandom RANDOM = new SecureRandom();
+
@Resource
private ShopOrderService shopOrderService;
+ @Resource
+ private ShopOrderMapper shopOrderMapper;
+
@Resource
private ShopOrderGoodsService shopOrderGoodsService;
@@ -52,11 +64,19 @@ public class OrderBusinessService {
@Resource
private ShopUserAddressService shopUserAddressService;
+
@Resource
private ShopUserCouponService shopUserCouponService;
+
@Resource
private ShopStoreFenceService shopStoreFenceService;
+ @Resource
+ private ShopFlashSaleActivityMapper shopFlashSaleActivityMapper;
+
+ @Resource
+ private OrderNoUtils orderNoUtils;
+
/**
* 创建订单
*
@@ -67,41 +87,38 @@ public class OrderBusinessService {
@Transactional(rollbackFor = Exception.class)
public Map createOrder(OrderCreateRequest request, User loginUser) {
- // 1. 参数校验
+ //1. 参数校验
validateOrderRequest(request, loginUser);
- // 2. 构建订单对象
+ //2. 构建订单对象
ShopOrder shopOrder = buildShopOrder(request, loginUser);
- // 3. 处理收货地址信息
+ //3. 处理收货地址信息
processDeliveryAddress(shopOrder, request, loginUser);
- // 3.1 下单时校验配送范围(电子围栏)
+ //4. 下单时校验配送范围(电子围栏)
validateDeliveryFenceIfNeeded(shopOrder, loginUser);
- // 4. 应用业务规则
- applyBusinessRules(shopOrder, loginUser);
-
- // 如果商品仅有一个则更新formId
+ //5.如果商品仅有一个则更新formId
if (request.getGoodsItems().size() == 1) {
shopOrder.setFormId(request.getGoodsItems().get(0).getGoodsId());
}
- // 5. 保存订单
+ //6.保存订单
boolean saved = shopOrderService.save(shopOrder);
if (!saved) {
throw new BusinessException("订单保存失败");
}
- // 6. 保存订单商品
+ //7.保存订单商品
saveOrderGoods(request, shopOrder);
- // 7. 标记优惠券为已使用
+ //8.标记优惠券为已使用
if (shopOrder.getCouponId() != null && shopOrder.getCouponId() > 0) {
markCouponAsUsed(shopOrder.getCouponId(), shopOrder.getOrderId());
}
- // 8. 创建微信支付订单
+ //9.创建微信支付订单
try {
return shopOrderService.createWxOrder(shopOrder);
} catch (Exception e) {
@@ -118,18 +135,6 @@ public class OrderBusinessService {
throw new BusinessException("用户未登录");
}
- // 检查是否为测试账号
- boolean isTestAccount = orderConfig.isTestAccount(loginUser.getPhone());
-
- if (isTestAccount) {
- // 测试账号:直接使用测试金额,跳过金额验证
- BigDecimal testAmount = orderConfig.getTestAccount().getTestPayAmount();
- request.setTotalPrice(testAmount);
- log.info("测试账号订单,用户:{},使用测试金额:{}", loginUser.getPhone(), testAmount);
- return; // 测试账号跳过后续验证
- }
-
- // 非测试账号:正常验证流程
// 验证商品信息并计算总金额
BigDecimal calculatedTotal = validateAndCalculateTotal(request);
@@ -141,7 +146,6 @@ public class OrderBusinessService {
if (request.getTotalPrice() != null &&
request.getTotalPrice().subtract(calculatedTotal).abs().compareTo(new BigDecimal("0.01")) > 0) {
log.warn("订单金额计算不一致,前端传入:{},后台计算:{}", request.getTotalPrice(), calculatedTotal);
-// throw new BusinessException("订单金额计算错误,请刷新重试");
}
// 使用后台计算的金额
@@ -167,6 +171,8 @@ public class OrderBusinessService {
BigDecimal total = BigDecimal.ZERO;
for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) {
+ Integer activityId = item.getActivityId();
+
// 验证商品ID
if (item.getGoodsId() == null) {
throw new BusinessException("商品ID不能为空");
@@ -216,6 +222,25 @@ public class OrderBusinessService {
productName = goods.getName() + "(" + (item.getSpecInfo() != null ? item.getSpecInfo() : sku.getSku()) + ")";
}
+ //秒杀商品价格以秒杀价为准
+ if(activityId != null){
+ request.setActivityId(activityId);
+ ShopFlashSaleActivity saleActivity = shopFlashSaleActivityMapper.selectById(activityId);
+ if(saleActivity == null){
+ throw new BusinessException("秒杀活动数据查询失败!");
+ }
+ if(saleActivity.getStatus() != 0){
+ throw new BusinessException("当前秒杀活动已失效!");
+ }
+ if(saleActivity.getStock() <= 0){
+ throw new BusinessException("当前秒杀活动商品已售罄!");
+ }
+ if(item.getQuantity() > saleActivity.getSaleLimit()){
+ throw new BusinessException("选购数量已超秒杀活动限购数量!");
+ }
+ actualPrice = saleActivity.getPrice();
+ }
+
// 验证实际价格
if (actualPrice == null || actualPrice.compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException("商品价格异常:" + productName);
@@ -263,6 +288,10 @@ public class OrderBusinessService {
}
shopOrder.setExpirationTime(shopOrder.getCreateTime().plusMinutes(10));
+ //设置核销码及过期时间
+ shopOrder.setVerifyCode(getVerifyCode());
+ shopOrder.setVerifyExpTime(shopOrder.getCreateTime().plusMinutes(10));
+
// 确保租户ID正确设置(关键字段,影响微信支付证书路径)
shopOrder.setTenantId(loginUser.getTenantId());
@@ -279,36 +308,14 @@ public class OrderBusinessService {
log.debug("构建订单对象 - 租户ID:{},用户ID:{}", shopOrder.getTenantId(), shopOrder.getUserId());
// 生成订单号(如果请求中没有提供)
- if (shopOrder.getOrderNo() == null || shopOrder.getOrderNo().trim().isEmpty()) {
- String generatedOrderNo = Long.toString(IdUtil.getSnowflakeNextId());
- shopOrder.setOrderNo(generatedOrderNo);
- log.info("生成新订单号: {}", generatedOrderNo);
- } else {
- log.info("使用请求中的订单号: {}", shopOrder.getOrderNo());
- }
+ String orderNo = orderNoUtils.generate("L");
+ shopOrder.setOrderNo(orderNo);
// 设置默认备注
if (shopOrder.getComments() == null) {
shopOrder.setComments(orderConfig.getDefaultConfig().getDefaultComments());
}
- // 设置价格相关字段(解决数据库字段没有默认值的问题)
- if (shopOrder.getPayPrice() == null) {
- shopOrder.setPayPrice(shopOrder.getTotalPrice()); // 实际付款默认等于订单总额
- }
-
- if (shopOrder.getPrice() == null) {
- shopOrder.setPrice(shopOrder.getTotalPrice()); // 用于统计的价格默认等于订单总额
- }
-
- if (shopOrder.getReducePrice() == null) {
- shopOrder.setReducePrice(BigDecimal.ZERO); // 减少金额默认为0
- }
-
- if (shopOrder.getMoney() == null) {
- shopOrder.setMoney(shopOrder.getTotalPrice()); // 用于积分赠送的价格默认等于订单总额
- }
-
// 设置默认状态
shopOrder.setPayStatus(false); // 未付款
shopOrder.setOrderStatus(0); // 未使用
@@ -328,9 +335,108 @@ public class OrderBusinessService {
processCoupon(shopOrder, loginUser);
}
+ //订单类型、水票判断 TODO 目前没有购物车业务,每笔订单只下一个商品,后期加购物车多商品同时下单业务需调整
+ Integer orderType = request.getOrderType();
+ List goodsItems = request.getGoodsItems();
+ if(!CollectionUtils.isEmpty(goodsItems)){
+ OrderCreateRequest.OrderGoodsItem goodsItem = goodsItems.get(0);
+ ShopGoods shopGoods = shopGoodsService.getById(goodsItem.getGoodsId());
+ if(shopGoods != null){
+ Integer waterTicketFlag = shopGoods.getWaterTicketFlag();
+ if(waterTicketFlag != null && waterTicketFlag == 1){
+ shopOrder.setWaterTicketFlag(1);
+ }else {
+ shopOrder.setWaterTicketFlag(0);
+ }
+
+ //自配送才计算步梯费用业务【选择步梯配送方式、配送楼层大于1楼、商品已设置配送费】
+ if(Arrays.asList(1, 3).contains(shopOrder.getOrderType()) && shopOrder.getDeliveryMethod() == 1 && shopOrder.getDeliveryFloor() > 1
+ && shopGoods.getDeliveryFee().compareTo(BigDecimal.ZERO) > 0){
+ BigDecimal deliveryFee = BigDecimal.ZERO;
+ if(shopGoods.getDeliveryFeeType() == 0 ){
+ deliveryFee = BigDecimal.valueOf(shopOrder.getDeliveryFloor() - 1).multiply(shopGoods.getDeliveryFee()).multiply(BigDecimal.valueOf(goodsItem.getQuantity()));
+ }else if(shopOrder.getDeliveryFloor() > 1){
+ deliveryFee = shopGoods.getDeliveryFee();
+ }
+ if(deliveryFee.compareTo(BigDecimal.ZERO) > 0){
+ shopOrder.setDeliveryFee(deliveryFee);
+ shopOrder.setTotalPrice(shopOrder.getTotalPrice().add(deliveryFee));
+ }
+ }
+
+ //单一配送方式商品如用户没选择配送方式按商品默认配送属性设定订单类型
+ String deliveryType = shopGoods.getDeliveryType();
+ if(orderType == null && StrUtil.isNotBlank(deliveryType) && !deliveryType.contains(",")){
+ switch (deliveryType){
+ case "1":
+ case "3":{
+ orderType = 1;
+ break;
+ }
+ case "2":{
+ orderType = 2;
+ break;
+ }
+ case "4":{
+ orderType = 4;
+ break;
+ }
+ default:{
+ orderType = 5;
+ }
+ }
+ }
+ }
+ }
+
+ // 设置价格相关字段(解决数据库字段没有默认值的问题)
+ if (shopOrder.getPayPrice() == null) {
+ shopOrder.setPayPrice(shopOrder.getTotalPrice()); // 实际付款默认等于订单总额
+ }
+
+ if (shopOrder.getPrice() == null) {
+ shopOrder.setPrice(shopOrder.getTotalPrice()); // 用于统计的价格默认等于订单总额
+ }
+
+ if (shopOrder.getReducePrice() == null) {
+ shopOrder.setReducePrice(BigDecimal.ZERO); // 减少金额默认为0
+ }
+
+ if (shopOrder.getMoney() == null) {
+ shopOrder.setMoney(shopOrder.getTotalPrice()); // 用于积分赠送的价格默认等于订单总额
+ }
+
+ shopOrder.setOrderType(orderType);
return shopOrder;
}
+ /**
+ * 生成8位字母数字混合核销码(推荐)
+ */
+ public static String generate6Mix() {
+ StringBuilder sb = new StringBuilder(6);
+ int len = CHAR_POOL.length();
+ for (int i = 0; i < 6; i++) {
+ int idx = RANDOM.nextInt(len);
+ sb.append(CHAR_POOL.charAt(idx));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 获取不重复的核销码
+ * @return
+ */
+ public String getVerifyCode(){
+ String verifyCode = generate6Mix();
+ LambdaQueryWrapper orderLambdaQueryWrapper = new LambdaQueryWrapper().select(ShopOrder::getOrderId).eq(ShopOrder::getVerifyCode, verifyCode);
+ List shopOrders = shopOrderMapper.selectList(orderLambdaQueryWrapper);
+ if(!CollectionUtils.isEmpty(shopOrders)){
+ getVerifyCode();
+ }
+ return verifyCode;
+ }
+
/**
* 处理优惠券
*/
@@ -528,6 +634,12 @@ public class OrderBusinessService {
return;
}
+ //自提订单类型不用校验电子围栏
+ Integer orderType = shopOrder.getOrderType();
+ if(orderType != null && orderType == 2){
+ return;
+ }
+
// 若已配置围栏,则必须有 addressId 才能从地址表取坐标进行校验
if (shopOrder.getAddressId() == null) {
if (shopStoreFenceService.hasEnabledFences(shopOrder.getTenantId())) {
@@ -558,8 +670,17 @@ public class OrderBusinessService {
throw new BusinessException("收货地址坐标异常,请重新选择收货地址");
}
- // 只做校验;具体围栏列表/points 异常处理由围栏服务统一处理
- shopStoreFenceService.validatePointInEnabledFences(shopOrder.getTenantId(), lng, lat);
+ //订单类型:6-配送【系统自动识别电子围栏内转及时配送,电子围栏外发快递】
+ if(orderType != null && orderType == 6){
+ Boolean exit = shopStoreFenceService.validatePointInEnabled(shopOrder.getTenantId(), lng, lat);
+ if(exit){
+ shopOrder.setOrderType(1);
+ }else {
+ shopOrder.setOrderType(4);
+ }
+ }else {
+ shopStoreFenceService.validatePointInEnabledFences(shopOrder.getTenantId(), lng, lat);
+ }
}
/**
@@ -651,6 +772,13 @@ public class OrderBusinessService {
}
}
+ //秒杀商品价格以秒杀价为准
+ Integer activityId = item.getActivityId();
+ if(activityId != null){
+ ShopFlashSaleActivity saleActivity = shopFlashSaleActivityMapper.selectById(activityId);
+ actualPrice = saleActivity.getPrice();
+ }
+
// 验证库存
if (actualStock == null || actualStock < item.getQuantity()) {
String stockMsg = sku != null ? "商品规格库存不足" : "商品库存不足";
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopActiveImageService.java b/src/main/java/com/gxwebsoft/shop/service/ShopActiveImageService.java
new file mode 100644
index 0000000..192cb37
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopActiveImageService.java
@@ -0,0 +1,42 @@
+package com.gxwebsoft.shop.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.shop.entity.ShopActiveImage;
+import com.gxwebsoft.shop.param.ShopActiveImageParam;
+
+import java.util.List;
+
+/**
+ * 推广码底图Service
+ *
+ * @author xm
+ * @since 2026-04-27 18:02:17
+ */
+public interface ShopActiveImageService extends IService {
+
+ /**
+ * 分页关联查询
+ *
+ * @param param 查询参数
+ * @return PageResult
+ */
+ PageResult pageRel(ShopActiveImageParam param);
+
+ /**
+ * 关联查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List listRel(ShopActiveImageParam param);
+
+ /**
+ * 根据id查询
+ *
+ * @param id 主键ID
+ * @return ShopActiveImage
+ */
+ ShopActiveImage getByIdRel(Integer id);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopDealerCapitalService.java b/src/main/java/com/gxwebsoft/shop/service/ShopDealerCapitalService.java
index 67fb29c..371fd23 100644
--- a/src/main/java/com/gxwebsoft/shop/service/ShopDealerCapitalService.java
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopDealerCapitalService.java
@@ -2,8 +2,10 @@ package com.gxwebsoft.shop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.shop.dto.ShopDealerCapitalWaterDto;
import com.gxwebsoft.shop.entity.ShopDealerCapital;
import com.gxwebsoft.shop.param.ShopDealerCapitalParam;
+import com.gxwebsoft.shop.vo.ShopDealerCapitalWaterVO;
import java.util.List;
@@ -23,6 +25,13 @@ public interface ShopDealerCapitalService extends IService {
*/
PageResult pageRel(ShopDealerCapitalParam param);
+ /**
+ * 分页查询分销商个人流水
+ * @param waterDto
+ * @return
+ */
+ PageResult myCapitalWater(ShopDealerCapitalWaterDto waterDto);
+
/**
* 关联查询全部
*
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopDealerCommissionRollbackService.java b/src/main/java/com/gxwebsoft/shop/service/ShopDealerCommissionRollbackService.java
index e0fca92..5dbd54f 100644
--- a/src/main/java/com/gxwebsoft/shop/service/ShopDealerCommissionRollbackService.java
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopDealerCommissionRollbackService.java
@@ -17,5 +17,7 @@ public interface ShopDealerCommissionRollbackService {
* @return true=执行成功或无可回退数据;false=执行失败
*/
boolean rollbackOnOrderRefund(ShopOrder order, BigDecimal refundAmount);
+
+ boolean rollbackOnOrderRefundV2(ShopOrder order, BigDecimal refundAmount);
}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopDealerOrderService.java b/src/main/java/com/gxwebsoft/shop/service/ShopDealerOrderService.java
index 8390e29..3dd1f72 100644
--- a/src/main/java/com/gxwebsoft/shop/service/ShopDealerOrderService.java
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopDealerOrderService.java
@@ -5,6 +5,7 @@ import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.shop.entity.ShopDealerOrder;
import com.gxwebsoft.shop.param.ShopDealerOrderParam;
+import java.math.BigDecimal;
import java.util.List;
/**
@@ -39,4 +40,19 @@ public interface ShopDealerOrderService extends IService {
*/
ShopDealerOrder getByIdRel(Integer id);
+ /**
+ * 查询门店/服务商本人今日收益
+ * @return
+ */
+ BigDecimal todayRevenue();
+
+ /**
+ * 手动触发单条订单的佣金解冻(保留与定时任务相同的前置条件检查)
+ *
+ * @param orderNo 分销订单号
+ * @param tenantId 租户ID
+ * @return 解冻结果描述(包含成功/失败详情)
+ */
+ String manualUnfreeze(String orderNo, Integer tenantId);
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopDealerRefereeService.java b/src/main/java/com/gxwebsoft/shop/service/ShopDealerRefereeService.java
index 2abc0ec..cfb28cf 100644
--- a/src/main/java/com/gxwebsoft/shop/service/ShopDealerRefereeService.java
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopDealerRefereeService.java
@@ -23,6 +23,13 @@ public interface ShopDealerRefereeService extends IService {
*/
PageResult pageRel(ShopDealerRefereeParam param);
+ /**
+ * app端查询用户层级关系
+ * @param param
+ * @return
+ */
+ PageResult appPage(ShopDealerRefereeParam param);
+
/**
* 关联查询全部
*
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopDealerUserService.java b/src/main/java/com/gxwebsoft/shop/service/ShopDealerUserService.java
index 8e86728..1a13058 100644
--- a/src/main/java/com/gxwebsoft/shop/service/ShopDealerUserService.java
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopDealerUserService.java
@@ -2,6 +2,9 @@ package com.gxwebsoft.shop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.shop.dto.ShopDealerRefundDto;
+import com.gxwebsoft.shop.dto.ShopDealerSettlementDto;
+import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.param.ShopDealerUserParam;
@@ -42,4 +45,33 @@ public interface ShopDealerUserService extends IService {
ShopDealerUser getByUserIdRel(Integer userId);
boolean updateByUserId(ShopDealerUser shopDealerUser);
+
+ /**
+ * 更新分销用户资金
+ * @param reduceDto
+ * @return
+ */
+ Boolean reduceBalance(ShopDealerUserReduceDto reduceDto);
+
+ /**
+ * 分销订单退款
+ * @param refundDto
+ * @return
+ */
+ Boolean refundOrder(ShopDealerRefundDto refundDto);
+
+ /**
+ * 用户资金结算
+ * @param dto
+ * @return
+ */
+ Boolean settlementBatch(ShopDealerSettlementDto dto);
+
+ /**
+ * 开启/关闭分销商用户核销权限
+ * @param id
+ * @return
+ */
+ Boolean verifyEnable(Integer id);
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopFlashSaleActivityService.java b/src/main/java/com/gxwebsoft/shop/service/ShopFlashSaleActivityService.java
new file mode 100644
index 0000000..5cad0a0
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopFlashSaleActivityService.java
@@ -0,0 +1,72 @@
+package com.gxwebsoft.shop.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.shop.entity.ShopFlashSaleActivity;
+import com.gxwebsoft.shop.param.ShopFlashSaleActivityParam;
+import com.gxwebsoft.shop.vo.ShopFlashSaleActivityVO;
+
+import java.util.List;
+
+/**
+ * 秒杀活动Service
+ *
+ * @author xm
+ * @since 2026-04-22 17:18:17
+ */
+public interface ShopFlashSaleActivityService extends IService {
+
+ /**
+ * 分页关联查询
+ *
+ * @param param 查询参数
+ * @return PageResult
+ */
+ PageResult pageRel(ShopFlashSaleActivityParam param);
+
+ /**
+ * 查询详情
+ * @param id
+ * @return
+ */
+ ShopFlashSaleActivityVO getInfoById(Integer id);
+
+ /**
+ * 关联查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List listRel(ShopFlashSaleActivityParam param);
+
+ /**
+ * 根据id查询
+ *
+ * @param id 秒杀活动编号
+ * @return ShopFlashSaleActivity
+ */
+ ShopFlashSaleActivity getByIdRel(Integer id);
+
+ /**
+ * 查询个人可参与的活动数据
+ * @param tenantId 租户ID
+ * @return
+ */
+ List getMyActive(Integer tenantId, Integer popFlag);
+
+ /**
+ * 修改秒杀活动状态
+ * @param id
+ * @return
+ */
+ Boolean updateStatus(Integer id);
+
+ /**
+ * 修改秒杀活动排序
+ * @param id
+ * @param sortNumber
+ * @return
+ */
+ Boolean updateSortNumber(Integer id, Integer sortNumber);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopGoodsProfitService.java b/src/main/java/com/gxwebsoft/shop/service/ShopGoodsProfitService.java
new file mode 100644
index 0000000..aaa31bf
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopGoodsProfitService.java
@@ -0,0 +1,42 @@
+package com.gxwebsoft.shop.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.shop.entity.ShopGoodsProfit;
+import com.gxwebsoft.shop.param.ShopGoodsProfitParam;
+
+import java.util.List;
+
+/**
+ * 商品分润设定Service
+ *
+ * @author xm
+ * @since 2026-05-20 14:33:00
+ */
+public interface ShopGoodsProfitService extends IService {
+
+ /**
+ * 分页关联查询
+ *
+ * @param param 查询参数
+ * @return PageResult
+ */
+ PageResult pageRel(ShopGoodsProfitParam param);
+
+ /**
+ * 关联查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List listRel(ShopGoodsProfitParam param);
+
+ /**
+ * 根据id查询
+ *
+ * @param id 主键ID
+ * @return ShopGoodsProfit
+ */
+ ShopGoodsProfit getByIdRel(Integer id);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopOrderService.java b/src/main/java/com/gxwebsoft/shop/service/ShopOrderService.java
index e2ebdea..744e42c 100644
--- a/src/main/java/com/gxwebsoft/shop/service/ShopOrderService.java
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopOrderService.java
@@ -2,9 +2,13 @@ package com.gxwebsoft.shop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.shop.dto.ShopOrderMyVerifyDto;
import com.gxwebsoft.shop.dto.UserOrderStats;
+import com.gxwebsoft.shop.dto.VerifyShopOrderDto;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.param.ShopOrderParam;
+import com.gxwebsoft.shop.vo.ShopOrderGoodsInfoVO;
+import com.gxwebsoft.shop.vo.ShopOrderMyVerifyVO;
import java.math.BigDecimal;
import java.util.HashMap;
@@ -42,6 +46,12 @@ public interface ShopOrderService extends IService {
*/
ShopOrder getByIdRel(Integer orderId);
+ /**
+ * 我已核销订单
+ * @return
+ */
+ ShopOrderMyVerifyVO myVerifyOrder(ShopOrderMyVerifyDto myVerifyDto);
+
HashMap createWxOrder(ShopOrder shopOrder);
ShopOrder getByOutTradeNo(String outTradeNo);
@@ -86,4 +96,18 @@ public interface ShopOrderService extends IService {
* @param type 订单类型(可为空)
*/
UserOrderStats getUserOrderStats(Integer userId, Integer tenantId, Integer type);
+
+ /**
+ * 核销订单
+ * @param verifyDto 核销码
+ * @return
+ */
+ Boolean verifyOrder(VerifyShopOrderDto verifyDto);
+
+ /**
+ * 通过订单号查询订单商品名称
+ * @param orderNoList
+ * @return
+ */
+ List getOrderGoodsInfoByOrderNos(List orderNoList);
}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopStoreFenceService.java b/src/main/java/com/gxwebsoft/shop/service/ShopStoreFenceService.java
index b930b30..0954bcb 100644
--- a/src/main/java/com/gxwebsoft/shop/service/ShopStoreFenceService.java
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopStoreFenceService.java
@@ -59,4 +59,19 @@ public interface ShopStoreFenceService extends IService {
*/
void validatePointInEnabledFences(Integer tenantId, double lng, double lat);
+ /**
+ * 校验坐标是否落在任一启用围栏内。
+ *
+ * 约定:
+ * - 围栏按 tenantId + status=0 过滤;
+ * - 支持多个围栏:命中任意一个即通过;
+ * - 无围栏配置:直接放行;
+ * - 围栏 points 异常:抛出异常(避免误送)。
+ *
+ * @param tenantId 租户ID
+ * @param lng 经度
+ * @param lat 纬度
+ */
+ Boolean validatePointInEnabled(Integer tenantId, double lng, double lat);
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopSurchargeConfigService.java b/src/main/java/com/gxwebsoft/shop/service/ShopSurchargeConfigService.java
new file mode 100644
index 0000000..f11178b
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopSurchargeConfigService.java
@@ -0,0 +1,70 @@
+package com.gxwebsoft.shop.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.shop.entity.ShopSurchargeConfig;
+import com.gxwebsoft.shop.param.ShopSurchargeConfigParam;
+
+import java.util.List;
+
+/**
+ * 步梯费用设置Service
+ *
+ * @author xm
+ * @since 2026-04-28 16:30:00
+ */
+public interface ShopSurchargeConfigService extends IService {
+
+ /**
+ * 分页关联查询
+ *
+ * @param param 查询参数
+ * @return PageResult
+ */
+ PageResult pageRel(ShopSurchargeConfigParam param);
+
+ /**
+ * 关联查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List listRel(ShopSurchargeConfigParam param);
+
+ /**
+ * 根据id查询
+ *
+ * @param id 主键ID
+ * @return ShopSurchargeConfig
+ */
+ ShopSurchargeConfig getByIdRel(Integer id);
+
+ /**
+ * 通过类型获取数据
+ * @param type
+ * @return
+ */
+ ShopSurchargeConfig getInfoByType(Integer type);
+
+ /**
+ * 保存信息
+ * @param shopSurchargeConfig
+ * @return
+ */
+ Integer saveInfo(ShopSurchargeConfig shopSurchargeConfig);
+
+ /**
+ * 修改信息
+ * @param shopSurchargeConfig
+ * @return
+ */
+ Boolean updateInfo(ShopSurchargeConfig shopSurchargeConfig);
+
+ /**
+ * 更新数据状态
+ * @param id
+ * @return
+ */
+ Boolean updateStatus(Integer id);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopUserAddressService.java b/src/main/java/com/gxwebsoft/shop/service/ShopUserAddressService.java
index 70880e4..d8ec530 100644
--- a/src/main/java/com/gxwebsoft/shop/service/ShopUserAddressService.java
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopUserAddressService.java
@@ -55,4 +55,11 @@ public interface ShopUserAddressService extends IService {
*/
List getUserAddresses(Integer userId);
+ /**
+ * 修改用户信息
+ * @param shopUserAddress
+ * @return
+ */
+ Boolean updateInfo(ShopUserAddress shopUserAddress);
+
}
diff --git a/src/main/java/com/gxwebsoft/shop/service/ShopVerifyUserService.java b/src/main/java/com/gxwebsoft/shop/service/ShopVerifyUserService.java
new file mode 100644
index 0000000..225676a
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/service/ShopVerifyUserService.java
@@ -0,0 +1,70 @@
+package com.gxwebsoft.shop.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.shop.entity.ShopVerifyUser;
+import com.gxwebsoft.shop.param.ShopVerifyUserParam;
+
+import java.util.List;
+
+/**
+ * 订单核销人管理Service
+ *
+ * @author xm
+ * @since 2026-05-25 17:13:00
+ */
+public interface ShopVerifyUserService extends IService {
+
+ /**
+ * 分页关联查询
+ *
+ * @param param 查询参数
+ * @return PageResult
+ */
+ PageResult pageRel(ShopVerifyUserParam param);
+
+ /**
+ * 关联查询全部
+ *
+ * @param param 查询参数
+ * @return List
+ */
+ List listRel(ShopVerifyUserParam param);
+
+ /**
+ * 根据id查询
+ *
+ * @param id 主键ID
+ * @return ShopVerifyUser
+ */
+ ShopVerifyUser getByIdRel(Integer id);
+
+ /**
+ * 通过用户ID查询
+ * @param userId
+ * @return
+ */
+ ShopVerifyUser getInfo(Integer userId);
+
+ /**
+ * 修改订单核销人开启状态
+ * @param id 主键ID
+ * @return
+ */
+ Boolean updateStatus(Integer id);
+
+ /**
+ * 修改订单核销人核销权限
+ * @param id 主键ID
+ * @return
+ */
+ Boolean updateVerifyFlag(Integer id);
+
+ /**
+ * 保存数据
+ * @param shopVerifyUser
+ * @return
+ */
+ Boolean saveInfo(ShopVerifyUser shopVerifyUser);
+
+}
diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/ShopActiveImageServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/ShopActiveImageServiceImpl.java
new file mode 100644
index 0000000..ba147b8
--- /dev/null
+++ b/src/main/java/com/gxwebsoft/shop/service/impl/ShopActiveImageServiceImpl.java
@@ -0,0 +1,87 @@
+package com.gxwebsoft.shop.service.impl;
+
+import com.aliyuncs.utils.StringUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.gxwebsoft.common.core.web.PageParam;
+import com.gxwebsoft.common.core.web.PageResult;
+import com.gxwebsoft.shop.entity.ShopActiveImage;
+import com.gxwebsoft.shop.entity.ShopGoods;
+import com.gxwebsoft.shop.mapper.ShopActiveImageMapper;
+import com.gxwebsoft.shop.mapper.ShopGoodsMapper;
+import com.gxwebsoft.shop.param.ShopActiveImageParam;
+import com.gxwebsoft.shop.service.ShopActiveImageService;
+import lombok.AllArgsConstructor;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 推广码底图Service实现
+ *
+ * @author xm
+ * @since 2026-04-27 17:53:00
+ */
+@Service
+@AllArgsConstructor
+public class ShopActiveImageServiceImpl extends ServiceImpl