Compare commits

28 Commits

Author SHA1 Message Date
xm
65f1fa861d 配置修改 2026-05-14 01:33:58 +08:00
xm
cdabd5d446 优化水票释放业务 2026-05-13 17:29:45 +08:00
xm
86d27db76d Merge branch 'dev' into dev_xm
# Conflicts:
#	src/test/java/com/gxwebsoft/generator/ShopGenerator.java
2026-05-13 16:33:42 +08:00
xm
c3fcb36f66 增加退款按订单号回退分销用户钱包流水及同步生产回退流水功能 2026-05-13 16:32:09 +08:00
xm
f3d6bbef63 1.优化分销员计算一级、二级分销佣金业务
2.优化送水订单获取用户收货地址业务
3.优化收益明细订单号、用户ID查询业务
4.提现业务增加订单号,避免使用订单ID传入会与生产ID重复,导致都是已使用订单号
2026-05-13 09:55:49 +08:00
xm
92ca45a5c1 1.优化分佣算法业务
2.修改配置
2026-05-12 16:47:41 +08:00
xm
991b6fe529 1.调整订单分销、分润、分红结算算法功能
2.商品订单支付成功增加执行分销员分销、统计门店/服务商分销业务、执行总分红业务功能
3.配送员完成配送增加解冻商品订单业务功能
4.订单分销记录增加关联结算单号、结算状态字段,方便门店/服务商做计算做准备
2026-05-11 17:55:48 +08:00
xm
bc88f54c24 优化商品下单业务、水票解冻业务 2026-05-09 14:56:53 +08:00
xm
c6cec21d12 1.优化链接配置
2.关闭套票发放任务【支付成功回调取代】
3.优化定时解冻水票业务,每月1号即可释放本月水票
2026-05-09 14:36:40 +08:00
xm
81a9974e64 1.优化配置
2.商品订单支付成功后增加执行发放水票业务功能
3.送水订单、商品订单优化生产订单号业务
2026-05-08 15:00:06 +08:00
xm
3c7ee5057f swagger增加glt包扫描,获取接口文档 2026-05-08 09:58:23 +08:00
xm
82ce775807 1.调整开发端微信支付回调参数,保证测试环境能正常使用微信支付功能
2.调整送水订单显示用户具体收货地址业务
2026-05-08 09:36:05 +08:00
xm
f3dc242b5c 1.增加更新分销用户资金变更api,此功能提供统一分销资金结算、分销资金解冻、扫码直返佣等业务功能,变更账户同时生成对应流水
2.分销资金明细表增加:订单号、变动类型、变更后金额、创建人、删除等字段及相关功能
3.商品增加推广核验佣金比率业务
4.商品订单增加统计推广核销佣金、查询用户收货详细地址业务
5.增加订单扫码核销功能
6.新增统一获取订单工具类
2026-05-07 16:09:34 +08:00
xm
1a68b70591 调整订单取消任务每5分钟执行一次 2026-05-06 09:52:45 +08:00
xm
587caa78d7 1.优化小程序端用户待付款、待发货、待收货、已完成、退货/售后总单量数据及对应订单明细查询功能
2.新增分销用户查询团队成员订单数、订单金额、团队成员数查询业务功能
2026-05-05 17:39:26 +08:00
xm
a575907623 1.下单增加商品判断是否水票优化、默认订单类型功能
2.订单增加生成核销码功能
3.自提订单下单不校验电子围栏;配送订单如在电子围栏内默认自配送,在电子围栏外默认发快递
4.商品增加:配送方式、水票标识业务;商品订单增加:订单类型、水票标识、核销码业务
2026-05-05 09:31:45 +08:00
xm
044bb24b57 优化用户收货地址修改默认属性业务,一个地址设置为默认其他地址同步设置非默认 2026-04-30 10:39:58 +08:00
xm
53c0dc9cd7 常用实体类增加表名称对应名称,方便快速查询业务 2026-04-29 15:34:35 +08:00
xm
fb46af7bc3 增加步梯费用设置业务功能 2026-04-29 09:46:03 +08:00
1350250847@qq.com
8a22ad771a Merge branch 'dev' into dev_xm 2026-04-28 15:21:05 +08:00
1350250847@qq.com
68d2a99b77 秒杀活动增加商品图片、单位信息 2026-04-28 14:31:29 +08:00
1350250847@qq.com
359c080023 增加活动底图功能 2026-04-28 09:43:58 +08:00
1350250847@qq.com
70b299eda6 代码生成调整ID查询回退为Integer类型 2026-04-27 17:47:04 +08:00
1350250847@qq.com
9eeb0c5682 秒杀活动主键类型切换 2026-04-27 17:45:00 +08:00
1350250847@qq.com
818be01c7c 1.商品下单优化秒杀订单以秒杀价格为准
2.修改水票套票释放逻辑,个人水票发放以次月以1日凌晨为时间节点
3.增加以订单号形式发送水票套票信息
2026-04-27 17:23:08 +08:00
1350250847@qq.com
1ae7a76901 调整秒杀业务ID类型 2026-04-27 17:09:39 +08:00
1350250847@qq.com
95964219a5 优化秒杀活动限购数量业务 2026-04-23 17:17:05 +08:00
1350250847@qq.com
9344f3750c 1.修改数据库链接配置
2.增加系统异常码常量池
3.调整代码生产业务
4.增加秒杀活动业务功能
2026-04-23 15:59:10 +08:00
100 changed files with 4242 additions and 281 deletions

View File

@@ -91,18 +91,26 @@ public class MybatisPlusConfig {
"sys_dictionary", "sys_dictionary",
"sys_dictionary_data", "sys_dictionary_data",
"apps_test_data", "apps_test_data",
"cms_lang" "cms_lang",
// "hjm_car", // "hjm_car",
// "hjm_fence" // "hjm_fence"
// "cms_website" // "cms_website"
// "sys_user" // "sys_user"
// "cms_domain" // "cms_domain"
// "shop_order_goods", // "shop_order_goods",
// "shop_goods" "shop_goods",
// "shop_users", // "shop_users",
// "shop_order" // 移除shop_order改为通过注解控制 "shop_order" , // 移除shop_order改为通过注解控制
// "shop_order_info", // "shop_order_info",
// "booking_user_invoice" // "booking_user_invoice"
"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"
).contains(tableName); ).contains(tableName);
} }
}; };

View File

@@ -87,14 +87,15 @@ public class SwaggerConfig {
} }
/** /**
* OA 模块分组 * Glt 模块分组
*/ */
@Bean @Bean
public GroupedOpenApi oaApi() { public GroupedOpenApi gltApi() {
return GroupedOpenApi.builder() return GroupedOpenApi.builder()
.group("oa") .group("glt")
.pathsToMatch("/api/oa/**") // 订单等用户侧接口在 shop 包内,但路径使用 /api/user/**(前端统一 user 侧 API 前缀)
.packagesToScan("com.gxwebsoft.oa") .pathsToMatch("/api/glt/**", "/api/user/**")
.packagesToScan("com.gxwebsoft.glt")
.build(); .build();
} }

View File

@@ -2,4 +2,7 @@ package com.gxwebsoft.common.core.constants;
public class BaseConstants { public class BaseConstants {
public static final String[] STATUS = {"未定义","显示","隐藏"}; public static final String[] STATUS = {"未定义","显示","隐藏"};
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
} }

View File

@@ -0,0 +1,38 @@
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),
WITHDRAW_PAYMENT(20, "提现支出", false),
TRANSFER_PAYMENT(30, "转账支出", false),
TRANSFER_INCOME(40, "转账收入", true),
FREEZE_MONEY_THAW(50, "佣金解冻", true),
DELIVERY_INCOME(60, "配送奖励", true),
ORDER_REFUND(70, "佣金退回(退单)", false),
;
/**
* 业务分类
*/
private final Integer type;
/**
* 说明
*/
private final String description;
/**
* 是否为增加余额
*/
private final boolean add;
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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, "门店财务账单不存在");
}

View File

@@ -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;
}
}

View File

@@ -262,6 +262,9 @@ public class User implements UserDetails {
@Schema(description = "微信unionid") @Schema(description = "微信unionid")
private String unionid; private String unionid;
@Schema(description = "核销权限是否开启 0-未开启 1-已开启")
private Integer verifyFlag;
@Schema(description = "关联用户ID") @Schema(description = "关联用户ID")
@TableField(exist = false) @TableField(exist = false)
private Integer sysUserId; private Integer sysUserId;

View File

@@ -49,6 +49,9 @@ public interface UserMapper extends BaseMapper<User> {
@InterceptorIgnore(tenantLine = "true") @InterceptorIgnore(tenantLine = "true")
List<User> getOne(@Param("param") UserParam param); List<User> getOne(@Param("param") UserParam param);
@InterceptorIgnore(tenantLine = "true")
User getById(@Param("userId") Integer userId);
List<User> selectListStatisticsRel(@Param("param") UserParam param); List<User> selectListStatisticsRel(@Param("param") UserParam param);
@InterceptorIgnore(tenantLine = "true") @InterceptorIgnore(tenantLine = "true")

View File

@@ -260,5 +260,8 @@
WHERE a.user_id = #{userId} WHERE a.user_id = #{userId}
AND a.deleted = 0 AND a.deleted = 0
</select> </select>
<select id="getById" resultType="com.gxwebsoft.common.system.entity.User">
SELECT * FROM gxwebsoft_core.sys_user WHERE user_id = #{userId} and deleted = 0
</select>
</mapper> </mapper>

View File

@@ -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;
}
}

View File

@@ -329,16 +329,14 @@ public class GltTicketOrderController extends BaseController {
if (addr == null) { if (addr == null) {
return null; return null;
} }
if (StrUtil.isNotBlank(addr.getFullAddress())) {
return addr.getFullAddress();
}
// 兼容旧数据fullAddress 为空时,拼接省市区 + 详细地址 // 兼容旧数据fullAddress 为空时,拼接省市区 + 详细地址
return StrUtil.blankToDefault( return StrUtil.blankToDefault(
StrUtil.join("", StrUtil.join("",
StrUtil.nullToEmpty(addr.getProvince()), StrUtil.nullToEmpty(addr.getProvince()),
StrUtil.nullToEmpty(addr.getCity()), StrUtil.nullToEmpty(addr.getCity()),
StrUtil.nullToEmpty(addr.getRegion()), StrUtil.nullToEmpty(addr.getRegion()),
StrUtil.nullToEmpty(addr.getAddress()) StrUtil.nullToEmpty(addr.getAddress()),
StrUtil.nullToEmpty(addr.getFullAddress())
), ),
addr.getAddress() addr.getAddress()
); );

View File

@@ -1,15 +1,15 @@
package com.gxwebsoft.glt.controller; package com.gxwebsoft.glt.controller;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.glt.service.GltUserTicketReleaseService;
import com.gxwebsoft.glt.entity.GltUserTicketRelease; import com.gxwebsoft.glt.entity.GltUserTicketRelease;
import com.gxwebsoft.glt.param.GltUserTicketReleaseParam; import com.gxwebsoft.glt.param.GltUserTicketReleaseParam;
import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.glt.service.GltUserTicketReleaseService;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.glt.service.impl.GltUserTicketAutoReleaseServiceImpl;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.annotation.OperationLog;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@@ -31,6 +31,9 @@ public class GltUserTicketReleaseController extends BaseController {
@Resource @Resource
private GltUserTicketReleaseService gltUserTicketReleaseService; private GltUserTicketReleaseService gltUserTicketReleaseService;
@Resource
private GltUserTicketAutoReleaseServiceImpl gltUserTicketAutoReleaseService;
@PreAuthorize("hasAuthority('glt:gltUserTicketRelease:list')") @PreAuthorize("hasAuthority('glt:gltUserTicketRelease:list')")
@Operation(summary = "分页查询水票释放") @Operation(summary = "分页查询水票释放")
@GetMapping("/page") @GetMapping("/page")
@@ -126,4 +129,11 @@ public class GltUserTicketReleaseController extends BaseController {
return fail("删除失败"); return fail("删除失败");
} }
@Operation(summary = "水票释放测试")
@PostMapping("/releaseTask")
public ApiResult<?> releaseTask() {
gltUserTicketAutoReleaseService.releaseTask();
return success(true);
}
} }

View File

@@ -1,9 +1,6 @@
package com.gxwebsoft.glt.entity; package com.gxwebsoft.glt.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@@ -22,16 +19,20 @@ import java.time.LocalDateTime;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "GltTicketOrder对象", description = "送水订单") @Schema(name = "GltTicketOrder对象", description = "送水订单")
@TableName("glt_ticket_order")
public class GltTicketOrder implements Serializable { public class GltTicketOrder implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO) @TableId(value = "id", type = IdType.AUTO)
private Integer id; private Integer id;
@Schema(description = "订单编号")
private String no;
@Schema(description = "用户水票ID") @Schema(description = "用户水票ID")
private Integer userTicketId; private Integer userTicketId;
@Schema(description = "订单编号") @Schema(description = "关联订单编号")
@TableField(exist = false) @TableField(exist = false)
private String orderNo; private String orderNo;
@@ -197,6 +198,10 @@ public class GltTicketOrder implements Serializable {
@Schema(description = "楼层(步梯+送上楼时有值从2开始") @Schema(description = "楼层(步梯+送上楼时有值从2开始")
private Integer deliveryFloor; private Integer deliveryFloor;
@Schema(description = "详细地址")
@TableField(exist = false)
private String fullAddress;
@Schema(description = "配送费(步梯+送上楼时计算:数量 × (楼层-1)") @Schema(description = "配送费(步梯+送上楼时计算:数量 × (楼层-1)")
private BigDecimal deliveryFee; private BigDecimal deliveryFee;

View File

@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable; import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@@ -19,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "GltTicketTemplate对象", description = "水票") @Schema(name = "GltTicketTemplate对象", description = "水票")
@TableName("glt_ticket_template")
public class GltTicketTemplate implements Serializable { public class GltTicketTemplate implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -1,12 +1,9 @@
package com.gxwebsoft.glt.entity; package com.gxwebsoft.glt.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@@ -22,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "GltUserTicket对象", description = "我的水票") @Schema(name = "GltUserTicket对象", description = "我的水票")
@TableName("glt_user_ticket")
public class GltUserTicket implements Serializable { public class GltUserTicket implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -1,10 +1,8 @@
package com.gxwebsoft.glt.entity; package com.gxwebsoft.glt.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@@ -20,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "GltUserTicketLog对象", description = "消费日志") @Schema(name = "GltUserTicketLog对象", description = "消费日志")
@TableName("glt_user_ticket_log")
public class GltUserTicketLog implements Serializable { public class GltUserTicketLog implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -1,10 +1,8 @@
package com.gxwebsoft.glt.entity; package com.gxwebsoft.glt.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@@ -20,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "GltUserTicketRelease对象", description = "水票释放") @Schema(name = "GltUserTicketRelease对象", description = "水票释放")
@TableName("glt_user_ticket_release")
public class GltUserTicketRelease implements Serializable { public class GltUserTicketRelease implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -27,7 +26,7 @@ public class GltUserTicketRelease implements Serializable {
private Long id; private Long id;
@Schema(description = "水票ID") @Schema(description = "水票ID")
private Long userTicketId; private Integer userTicketId;
@Schema(description = "用户ID") @Schema(description = "用户ID")
private Integer userId; private Integer userId;
@@ -57,6 +56,9 @@ public class GltUserTicketRelease implements Serializable {
@Schema(description = "状态") @Schema(description = "状态")
private Integer status; private Integer status;
@Schema(description = "备注")
private String remark;
@Schema(description = "是否删除, 0否, 1是") @Schema(description = "是否删除, 0否, 1是")
@TableLogic @TableLogic
private Integer deleted; private Integer deleted;

View File

@@ -37,6 +37,13 @@ public interface GltUserTicketReleaseMapper extends BaseMapper<GltUserTicketRele
*/ */
List<GltUserTicketRelease> selectListRel(@Param("param") GltUserTicketReleaseParam param); List<GltUserTicketRelease> selectListRel(@Param("param") GltUserTicketReleaseParam param);
/**
* 查询当月待释放水票数据
* @param limitNum 查询数量
* @return List<User>
*/
List<GltUserTicketRelease> getThisMonthReleaseList(@Param("limitNum") Integer limitNum);
/** /**
* 查询待释放且到期的记录(加行锁,防止多实例重复处理) * 查询待释放且到期的记录(加行锁,防止多实例重复处理)
* *

View File

@@ -58,5 +58,23 @@
<select id="selectListRel" resultType="com.gxwebsoft.glt.entity.GltUserTicketRelease"> <select id="selectListRel" resultType="com.gxwebsoft.glt.entity.GltUserTicketRelease">
<include refid="selectSql"></include> <include refid="selectSql"></include>
</select> </select>
<select id="getThisMonthReleaseList" resultType="com.gxwebsoft.glt.entity.GltUserTicketRelease">
SELECT
id,
user_ticket_id,
user_id,
release_qty,
tenant_id
FROM
glt_user_ticket_release
WHERE
STATUS = 0
AND deleted = 0
AND release_qty > 0
AND DATE_FORMAT(release_time, '%Y-%m') = DATE_FORMAT(NOW(), '%Y-%m')
ORDER BY
release_time ASC
LIMIT #{limitNum}
</select>
</mapper> </mapper>

View File

@@ -1,28 +1,28 @@
package com.gxwebsoft.glt.service; package com.gxwebsoft.glt.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.gxwebsoft.glt.entity.GltTicketTemplate; import com.gxwebsoft.glt.entity.GltTicketTemplate;
import com.gxwebsoft.glt.entity.GltUserTicket; import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.glt.entity.GltUserTicketLog; import com.gxwebsoft.glt.entity.GltUserTicketLog;
import com.gxwebsoft.glt.entity.GltUserTicketRelease; import com.gxwebsoft.glt.entity.GltUserTicketRelease;
import com.gxwebsoft.glt.task.DealerOrderSettlement10584Task;
import com.gxwebsoft.shop.entity.ShopOrder; import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.entity.ShopOrderGoods; import com.gxwebsoft.shop.entity.ShopOrderGoods;
import com.gxwebsoft.shop.mapper.ShopOrderMapper;
import com.gxwebsoft.shop.service.ShopOrderGoodsService; import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/** /**
* 套票发放(从订单生成用户套票 + 释放计划)的业务逻辑。 * 套票发放(从订单生成用户套票 + 释放计划)的业务逻辑。
@@ -46,7 +46,7 @@ public class GltTicketIssueService {
NO_TEMPLATE NO_TEMPLATE
} }
private final ShopOrderService shopOrderService; private final ShopOrderMapper shopOrderMapper;
private final ShopOrderGoodsService shopOrderGoodsService; private final ShopOrderGoodsService shopOrderGoodsService;
private final GltTicketTemplateService gltTicketTemplateService; private final GltTicketTemplateService gltTicketTemplateService;
@@ -54,6 +54,7 @@ public class GltTicketIssueService {
private final GltUserTicketReleaseService gltUserTicketReleaseService; private final GltUserTicketReleaseService gltUserTicketReleaseService;
private final GltUserTicketLogService gltUserTicketLogService; private final GltUserTicketLogService gltUserTicketLogService;
private final TransactionTemplate transactionTemplate; private final TransactionTemplate transactionTemplate;
private final DealerOrderSettlement10584Task dealerOrderSettlement;
/** /**
* 扫描“今日订单”,执行套票发放。 * 扫描“今日订单”,执行套票发放。
@@ -84,7 +85,7 @@ public class GltTicketIssueService {
LocalDateTime todayStart = LocalDate.now().atStartOfDay(); LocalDateTime todayStart = LocalDate.now().atStartOfDay();
LocalDateTime tomorrowStart = todayStart.plusDays(1); LocalDateTime tomorrowStart = todayStart.plusDays(1);
List<ShopOrder> orders = shopOrderService.list( List<ShopOrder> orders = shopOrderMapper.selectList(
new LambdaQueryWrapper<ShopOrder>() new LambdaQueryWrapper<ShopOrder>()
.eq(ShopOrder::getTenantId, tenantId) .eq(ShopOrder::getTenantId, tenantId)
.in(ShopOrder::getFormId, uniqueGoodsIds) .in(ShopOrder::getFormId, uniqueGoodsIds)
@@ -128,6 +129,71 @@ public class GltTicketIssueService {
tenantId, uniqueGoodsIds, orders.size(), success, skipped, failed); tenantId, uniqueGoodsIds, orders.size(), success, skipped, failed);
} }
/**
* 商品订单支付成功后调后需处理业务
* @param orderNo 订单号
* @param tenantId 租户ID
*/
@Async
public void paySuccessTask(String orderNo, Integer tenantId){
//1.发送水票
suerTicketRelease(orderNo, tenantId);
//2.执行分销员分销、统计门店/服务商分销业务
dealerOrderSettlement.orderSettlement(orderNo);
//3.执行平台分红业务 TODO 待开发
}
/**
* 订单支付成功,直接发送水票【后期优化订单类型,为水票的订单才需要执行此业务】
* @param orderNo 订单号
* @param tenantId 租户ID
*/
@Transactional
public void suerTicketRelease(String orderNo, Integer tenantId){
//1.订单为空跳过执行
ShopOrder shopOrder = shopOrderMapper.selectOne(new LambdaQueryWrapper<ShopOrder>()
.eq(ShopOrder::getOrderNo, orderNo)
.eq(ShopOrder::getTenantId, tenantId));
if(shopOrder == null){
return;
}
//2.只有水票订单才需要发送水票
if(!(shopOrder.getWaterTicketFlag() != null && shopOrder.getWaterTicketFlag() == 1)){
return;
}
//3.跳过已完成发放套票订单
if(shopOrder.getOrderStatus() == 1){
return;
}
//4.订单商品为空跳过执行
List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(shopOrder.getOrderId());
if (CollectionUtils.isEmpty(goodsList)) {
return;
}
//5.执行水票发放业务
AtomicBoolean release = new AtomicBoolean(false);
goodsList.forEach(orderGood ->{
IssueOutcome outcome = transactionTemplate.execute(status -> doIssueOne(tenantId, shopOrder, orderGood));
if(Arrays.asList(IssueOutcome.ISSUED, IssueOutcome.ALREADY_ISSUED).contains(outcome)){
release.set(true);
}
});
//6.更新商品订单为已完成、已收到赠品状态
if (release.get()) {
shopOrder.setHasTakeGift(true);
shopOrder.setUpdateTime(LocalDateTime.now());
shopOrderMapper.updateById(shopOrder);
}
}
private int issueForOrder(Integer tenantId, Set<Integer> goodsIds, ShopOrder order) { private int issueForOrder(Integer tenantId, Set<Integer> goodsIds, ShopOrder order) {
List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId()); List<ShopOrderGoods> goodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
if (goodsList == null || goodsList.isEmpty()) { if (goodsList == null || goodsList.isEmpty()) {
@@ -155,15 +221,10 @@ public class GltTicketIssueService {
if (shouldCompleteOrder) { if (shouldCompleteOrder) {
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
// 任务执行完后将订单置为“已完成”,避免后续扫描重复处理(幂等虽可挡住,但会产生大量无意义查询)。 // 任务执行完后将订单置为“已完成”,避免后续扫描重复处理(幂等虽可挡住,但会产生大量无意义查询)。
shopOrderService.update( order.setOrderStatus(1);
new LambdaUpdateWrapper<ShopOrder>() order.setHasTakeGift(true);
.eq(ShopOrder::getOrderId, order.getOrderId()) order.setUpdateTime(LocalDateTime.now());
.eq(ShopOrder::getTenantId, tenantId) shopOrderMapper.updateById(order);
.eq(ShopOrder::getOrderStatus, 0)
.set(ShopOrder::getOrderStatus, 1)
.set(ShopOrder::getHasTakeGift, true)
.set(ShopOrder::getUpdateTime, now)
);
} }
return issuedCount; return issuedCount;
@@ -184,7 +245,7 @@ public class GltTicketIssueService {
// - 这里先对商城订单行加行锁,保证同一订单在同一时刻只会被一个事务处理。 // - 这里先对商城订单行加行锁,保证同一订单在同一时刻只会被一个事务处理。
// (注意:需数据库支持 SELECT ... FOR UPDATE且 shop_order.order_id 为主键/有索引) // (注意:需数据库支持 SELECT ... FOR UPDATE且 shop_order.order_id 为主键/有索引)
if (order.getOrderId() != null) { if (order.getOrderId() != null) {
shopOrderService.getOne( shopOrderMapper.selectOne(
new LambdaQueryWrapper<ShopOrder>() new LambdaQueryWrapper<ShopOrder>()
.eq(ShopOrder::getOrderId, order.getOrderId()) .eq(ShopOrder::getOrderId, order.getOrderId())
.eq(ShopOrder::getTenantId, tenantId) .eq(ShopOrder::getTenantId, tenantId)
@@ -304,12 +365,14 @@ public class GltTicketIssueService {
// 若启用了 releasePeriods 且首期释放时机为“支付成功当刻”,则将首期释放量直接计入可用, // 若启用了 releasePeriods 且首期释放时机为“支付成功当刻”,则将首期释放量直接计入可用,
// 避免用户刚购买后短时间内无可用水票;后续期数仍由自动释放任务按 release_time 释放。 // 避免用户刚购买后短时间内无可用水票;后续期数仍由自动释放任务按 release_time 释放。
if (useReleasePeriods && !releases.isEmpty() && !Objects.equals(template.getFirstReleaseMode(), 1)) { // if (useReleasePeriods && !releases.isEmpty() && !Objects.equals(template.getFirstReleaseMode(), 1)) {
if (!releases.isEmpty() && !Objects.equals(template.getFirstReleaseMode(), 1)) {
GltUserTicketRelease first = releases.get(0); GltUserTicketRelease first = releases.get(0);
Integer firstQtyObj = first.getReleaseQty(); Integer firstQtyObj = first.getReleaseQty();
LocalDateTime firstTime = first.getReleaseTime(); LocalDateTime firstTime = first.getReleaseTime();
int firstQty = firstQtyObj != null ? firstQtyObj : 0; int firstQty = firstQtyObj != null ? firstQtyObj : 0;
if (firstQty > 0 && (firstTime == null || !firstTime.isAfter(now))) { // if (firstQty > 0 && (firstTime == null || !firstTime.isAfter(now))) {
if (firstQty > 0) {
first.setStatus(1); first.setStatus(1);
first.setUpdateTime(now); first.setUpdateTime(now);
@@ -376,10 +439,13 @@ public class GltTicketIssueService {
// 首期释放时间 // 首期释放时间
LocalDateTime firstReleaseTime; LocalDateTime firstReleaseTime;
LocalDateTime referenceTime;
if (Objects.equals(template.getFirstReleaseMode(), 1)) { if (Objects.equals(template.getFirstReleaseMode(), 1)) {
firstReleaseTime = nextMonthSameDay(baseTime); firstReleaseTime = nextMonthSameDay(baseTime);
referenceTime = firstReleaseTime.withDayOfMonth(1).toLocalDate().atStartOfDay();
} else { } else {
firstReleaseTime = baseTime; firstReleaseTime = baseTime;
referenceTime = firstReleaseTime.withDayOfMonth(1).toLocalDate().atStartOfDay();
} }
// 每期释放数量计算 // 每期释放数量计算
@@ -393,7 +459,11 @@ public class GltTicketIssueService {
if (qty <= 0) { if (qty <= 0) {
continue; continue;
} }
list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i), now)); if(i == 0){
list.add(buildRelease(userTicket, i, qty, firstReleaseTime, now));
}else {
list.add(buildRelease(userTicket, i, qty, referenceTime.plusMonths(i), now));
}
} }
return list; return list;
} }
@@ -410,7 +480,11 @@ public class GltTicketIssueService {
break; break;
} }
remaining -= qty; remaining -= qty;
list.add(buildRelease(userTicket, i, qty, firstReleaseTime.plusMonths(i), now)); if(i == 0){
list.add(buildRelease(userTicket, i, qty, firstReleaseTime, now));
}else {
list.add(buildRelease(userTicket, i, qty, referenceTime.plusMonths(i), now));
}
} }
return list; return list;
@@ -422,7 +496,7 @@ public class GltTicketIssueService {
LocalDateTime releaseTime, LocalDateTime releaseTime,
LocalDateTime now) { LocalDateTime now) {
GltUserTicketRelease r = new GltUserTicketRelease(); GltUserTicketRelease r = new GltUserTicketRelease();
r.setUserTicketId(userTicket.getId() != null ? userTicket.getId().longValue() : null); r.setUserTicketId(userTicket.getId() != null ? userTicket.getId() : null);
r.setUserId(userTicket.getUserId()); r.setUserId(userTicket.getUserId());
r.setPeriodNo(periodNo); r.setPeriodNo(periodNo);
r.setReleaseQty(releaseQty); r.setReleaseQty(releaseQty);

View File

@@ -133,7 +133,7 @@ public class GltTicketRevokeService {
LambdaUpdateWrapper<GltUserTicketRelease> uw = new LambdaUpdateWrapper<GltUserTicketRelease>() LambdaUpdateWrapper<GltUserTicketRelease> uw = new LambdaUpdateWrapper<GltUserTicketRelease>()
.eq(GltUserTicketRelease::getTenantId, tenantId) .eq(GltUserTicketRelease::getTenantId, tenantId)
.eq(GltUserTicketRelease::getDeleted, 0) .eq(GltUserTicketRelease::getDeleted, 0)
.eq(GltUserTicketRelease::getUserTicketId, userTicketId.longValue()) .eq(GltUserTicketRelease::getUserTicketId, userTicketId)
// status 为空时也视为“未完成” // status 为空时也视为“未完成”
.and(w -> w.ne(GltUserTicketRelease::getStatus, RELEASE_STATUS_DONE) .and(w -> w.ne(GltUserTicketRelease::getStatus, RELEASE_STATUS_DONE)
.or().isNull(GltUserTicketRelease::getStatus)) .or().isNull(GltUserTicketRelease::getStatus))

View File

@@ -17,10 +17,8 @@ public interface GltUserTicketAutoReleaseService {
int releaseDue(LocalDateTime now, int batchSize); int releaseDue(LocalDateTime now, int batchSize);
/** /**
* 释放到期的冻结水票(使用系统当前时间) * 释放到期的冻结水票【当次执行1000条】
*/ */
default int releaseDue(int batchSize) { void releaseTask();
return releaseDue(LocalDateTime.now(), batchSize);
}
} }

View File

@@ -4,11 +4,14 @@ import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum;
import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum;
import com.gxwebsoft.common.core.exception.BusinessException; import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.mapper.UserMapper; import com.gxwebsoft.common.system.mapper.UserMapper;
import com.gxwebsoft.common.system.redis.OrderNoUtils;
import com.gxwebsoft.glt.entity.GltTicketOrder; import com.gxwebsoft.glt.entity.GltTicketOrder;
import com.gxwebsoft.glt.entity.GltUserTicket; import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.glt.entity.GltUserTicketLog; import com.gxwebsoft.glt.entity.GltUserTicketLog;
@@ -18,15 +21,15 @@ import com.gxwebsoft.glt.param.GltTicketOrderParam;
import com.gxwebsoft.glt.service.GltTicketOrderService; import com.gxwebsoft.glt.service.GltTicketOrderService;
import com.gxwebsoft.glt.service.GltUserTicketLogService; import com.gxwebsoft.glt.service.GltUserTicketLogService;
import com.gxwebsoft.glt.service.GltUserTicketService; import com.gxwebsoft.glt.service.GltUserTicketService;
import com.gxwebsoft.shop.entity.ShopDealerCapital; import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
import com.gxwebsoft.shop.entity.ShopDealerUser; import com.gxwebsoft.shop.entity.*;
import com.gxwebsoft.shop.entity.ShopOrder; import com.gxwebsoft.shop.mapper.ShopUserAddressMapper;
import com.gxwebsoft.shop.entity.ShopOrderGoods;
import com.gxwebsoft.shop.service.ShopDealerCapitalService; import com.gxwebsoft.shop.service.ShopDealerCapitalService;
import com.gxwebsoft.shop.service.ShopDealerUserService; import com.gxwebsoft.shop.service.ShopDealerUserService;
import com.gxwebsoft.shop.service.ShopOrderGoodsService; import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService; import com.gxwebsoft.shop.service.ShopOrderService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -39,6 +42,7 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/** /**
* 送水订单Service实现 * 送水订单Service实现
@@ -83,11 +87,29 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
@Resource @Resource
private ShopOrderGoodsService shopOrderGoodsService; private ShopOrderGoodsService shopOrderGoodsService;
@Resource
private ShopUserAddressMapper shopUserAddressMapper;
@Resource
private OrderNoUtils orderNoUtils;
@Override @Override
public PageResult<GltTicketOrder> pageRel(GltTicketOrderParam param) { public PageResult<GltTicketOrder> pageRel(GltTicketOrderParam param) {
PageParam<GltTicketOrder, GltTicketOrderParam> page = new PageParam<>(param); PageParam<GltTicketOrder, GltTicketOrderParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc"); page.setDefaultOrder("sort_number asc, create_time desc");
List<GltTicketOrder> list = baseMapper.selectPageRel(page, param); List<GltTicketOrder> list = baseMapper.selectPageRel(page, param);
if(CollectionUtils.isNotEmpty(list)){
List<Integer> addressIdList = list.stream().map(GltTicketOrder::getAddressId).distinct().collect(Collectors.toList());
List<ShopUserAddress> userAddressList = shopUserAddressMapper.selectBatchIds(addressIdList);
if(CollectionUtils.isNotEmpty(userAddressList)){
list.forEach(ticketOrder ->{
ShopUserAddress shopUserAddress = userAddressList.stream().filter(address -> ticketOrder.getAddressId().equals(address.getId())).findFirst().orElse(null);
if(shopUserAddress != null){
ticketOrder.setFullAddress(shopUserAddress.getFullAddress());
}
});
}
}
return new PageResult<>(list, page.getTotal()); return new PageResult<>(list, page.getTotal());
} }
@@ -116,6 +138,10 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
if (userId == null) { if (userId == null) {
throw new BusinessException("请先登录"); throw new BusinessException("请先登录");
} }
String no = orderNoUtils.generate("S");
gltTicketOrder.setNo(no);
Integer userTicketId = gltTicketOrder.getUserTicketId(); Integer userTicketId = gltTicketOrder.getUserTicketId();
if (userTicketId == null) { if (userTicketId == null) {
throw new BusinessException("userTicketId不能为空"); throw new BusinessException("userTicketId不能为空");
@@ -742,6 +768,25 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
} }
} }
//查询未完成订单,完成资金解冻
LambdaQueryWrapper<ShopOrder> orderLambdaQueryWrapper;
if(shopOrderId != null){
orderLambdaQueryWrapper = new LambdaQueryWrapper<ShopOrder>().eq(ShopOrder::getOrderId, shopOrderId).eq(ShopOrder::getOrderStatus, 0);
}else{
orderLambdaQueryWrapper = new LambdaQueryWrapper<ShopOrder>().eq(ShopOrder::getOrderId, shopOrderNo).eq(ShopOrder::getOrderStatus, 0);
}
ShopOrder order = shopOrderService.getOne(orderLambdaQueryWrapper);
if(order != null){
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
reduceDto.setTypeEnum(ShopDealerTypeEnum.DEFROST);
reduceDto.setOrderUserId(order.getUserId());
reduceDto.setOrderNo(order.getOrderNo());
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.FREEZE_MONEY_THAW);
//按订单号资金解冻
shopDealerUserService.reduceBalance(reduceDto);
}
LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>() LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
.eq(ShopOrder::getTenantId, tenantId) .eq(ShopOrder::getTenantId, tenantId)
.eq(ShopOrder::getDeleted, 0) .eq(ShopOrder::getDeleted, 0)
@@ -753,7 +798,6 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
} else { } else {
uw.eq(ShopOrder::getOrderNo, shopOrderNo); uw.eq(ShopOrder::getOrderNo, shopOrderNo);
} }
boolean updated = shopOrderService.update(uw); boolean updated = shopOrderService.update(uw);
if (updated) { if (updated) {
return; return;

View File

@@ -1,5 +1,6 @@
package com.gxwebsoft.glt.service.impl; package com.gxwebsoft.glt.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.glt.entity.GltUserTicket; import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.glt.entity.GltUserTicketLog; import com.gxwebsoft.glt.entity.GltUserTicketLog;
import com.gxwebsoft.glt.entity.GltUserTicketRelease; import com.gxwebsoft.glt.entity.GltUserTicketRelease;
@@ -7,13 +8,20 @@ import com.gxwebsoft.glt.mapper.GltUserTicketLogMapper;
import com.gxwebsoft.glt.mapper.GltUserTicketMapper; import com.gxwebsoft.glt.mapper.GltUserTicketMapper;
import com.gxwebsoft.glt.mapper.GltUserTicketReleaseMapper; import com.gxwebsoft.glt.mapper.GltUserTicketReleaseMapper;
import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService; import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService;
import lombok.RequiredArgsConstructor; import com.gxwebsoft.glt.service.GltUserTicketLogService;
import com.gxwebsoft.glt.service.GltUserTicketReleaseService;
import com.gxwebsoft.glt.service.GltUserTicketService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/** /**
* 冻结水票自动释放实现: * 冻结水票自动释放实现:
@@ -23,7 +31,7 @@ import java.util.List;
*/ */
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @AllArgsConstructor
public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoReleaseService { public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoReleaseService {
/** /**
@@ -44,6 +52,13 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel
private final GltUserTicketMapper userTicketMapper; private final GltUserTicketMapper userTicketMapper;
private final GltUserTicketLogMapper userTicketLogMapper; private final GltUserTicketLogMapper userTicketLogMapper;
private GltUserTicketReleaseService gltUserTicketReleaseService;
private GltUserTicketService gltUserTicketService;
private GltUserTicketLogService gltUserTicketLogService;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public int releaseDue(LocalDateTime now, int batchSize) { public int releaseDue(LocalDateTime now, int batchSize) {
@@ -71,12 +86,7 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel
continue; continue;
} }
long userTicketIdLong = rel.getUserTicketId(); Integer userTicketId = rel.getUserTicketId();
if (userTicketIdLong > Integer.MAX_VALUE || userTicketIdLong < 1) {
markFailed(rel.getId(), now, "userTicketId超范围");
continue;
}
Integer userTicketId = (int) userTicketIdLong;
// 先释放冻结数量(条件更新,确保 frozen_qty >= qty // 先释放冻结数量(条件更新,确保 frozen_qty >= qty
int updated = userTicketMapper.releaseFrozenQty( int updated = userTicketMapper.releaseFrozenQty(
@@ -129,4 +139,96 @@ public class GltUserTicketAutoReleaseServiceImpl implements GltUserTicketAutoRel
releaseMapper.updateStatus(releaseId, RELEASE_STATUS_FAILED, now); releaseMapper.updateStatus(releaseId, RELEASE_STATUS_FAILED, now);
log.warn("冻结水票释放标记失败 - releaseId={}, reason={}", releaseId, reason); log.warn("冻结水票释放标记失败 - releaseId={}, reason={}", releaseId, reason);
} }
@Transactional
public void releaseTask() {
LocalDateTime now = LocalDateTime.now();
log.info("***执行水票释放任务***-执行时间:{}", now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
long start = System.currentTimeMillis();
List<GltUserTicketRelease> releaseList = releaseMapper.getThisMonthReleaseList(1000);
if(CollectionUtils.isEmpty(releaseList)){
log.info("***本轮任务无待释放水票数据***");
return;
}
//查询用户水票信息
List<Integer> userTickIdList = releaseList.stream().map(GltUserTicketRelease::getUserTicketId).distinct().collect(Collectors.toList());
LambdaQueryWrapper<GltUserTicket> userTicketLambdaQueryWrapper = new LambdaQueryWrapper<GltUserTicket>().select(GltUserTicket::getId, GltUserTicket::getAvailableQty,
GltUserTicket::getFrozenQty, GltUserTicket::getReleasedQty).in(GltUserTicket::getId, userTickIdList);
List<GltUserTicket> userTicketList = userTicketMapper.selectList(userTicketLambdaQueryWrapper);
//创建修改用户水票集合、水票释放计划记录集合
List<GltUserTicket> updateUserTicketList = new ArrayList<>();
List<GltUserTicketLog> userTicketLogList = new ArrayList<>();
//遍历水票释放数据执行释放任务
releaseList.forEach(release ->{
//1.缺少水票ID、用户ID、租户ID记录错误记录
if(release.getUserTicketId() == null || release.getUserId() == null || release.getTenantId() == null){
release.setRemark("缺少userTicketId/userId/tenantId");
release.setStatus(2);
release.setUpdateTime(now);
}
GltUserTicket userTicket = userTicketList.stream().filter(gltUserTicket -> release.getUserTicketId().equals(gltUserTicket.getId())).findFirst().orElse(null);
if(userTicket != null){
Integer releaseQty = release.getReleaseQty();
if(userTicket.getFrozenQty() > releaseQty){
//更改用户水票可用数量、冻结数量、已释放数量
userTicket.setAvailableQty(userTicket.getAvailableQty() + releaseQty);
userTicket.setFrozenQty(userTicket.getFrozenQty() - releaseQty);
userTicket.setReleasedQty(userTicket.getReleasedQty() + releaseQty);
userTicket.setUpdateTime(now);
updateUserTicketList.add(userTicket);
//记录水票释放计划为成功释放状态
release.setStatus(1);
release.setRemark("success");
release.setUpdateTime(now);
//生成水票释放记录
GltUserTicketLog ticketLog = new GltUserTicketLog();
ticketLog.setUserTicketId(release.getUserTicketId());
ticketLog.setChangeType(CHANGE_TYPE_RELEASE);
ticketLog.setChangeAvailable(releaseQty);
ticketLog.setChangeFrozen(-releaseQty);
ticketLog.setChangeUsed(0);
ticketLog.setAvailableAfter(userTicket.getAvailableQty());
ticketLog.setFrozenAfter(userTicket.getFrozenQty());
ticketLog.setUsedAfter(userTicket.getUsedQty());
ticketLog.setOrderId(Integer.valueOf(String.valueOf(release.getId())));
ticketLog.setUserId(release.getUserId());
ticketLog.setComments("冻结水票到期释放");
ticketLog.setTenantId(release.getTenantId());
ticketLog.setCreateTime(now);
ticketLog.setUpdateTime(now);
userTicketLogList.add(ticketLog);
}else {
release.setRemark("释放数量大于冻结数量,释放:" + releaseQty + ",冻结:" + userTicket.getFrozenQty());
release.setStatus(2);
release.setUpdateTime(LocalDateTime.now());
}
}else {
release.setRemark("查询不到水票数据");
release.setStatus(2);
release.setUpdateTime(LocalDateTime.now());
}
});
if(CollectionUtils.isNotEmpty(releaseList)){
gltUserTicketReleaseService.updateBatchById(releaseList);
}
if(CollectionUtils.isNotEmpty(updateUserTicketList)){
gltUserTicketService.updateBatchById(updateUserTicketList);
}
if(CollectionUtils.isNotEmpty(userTicketLogList)){
gltUserTicketLogService.saveBatch(userTicketLogList);
}
long end = System.currentTimeMillis();
long costMs = end - start;
log.info("***执行水票释放任务完成*** 执行耗时:{} ms", costMs);
}
} }

View File

@@ -1,41 +1,38 @@
package com.gxwebsoft.glt.task; package com.gxwebsoft.glt.task;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.gxwebsoft.common.core.annotation.IgnoreTenant; import com.gxwebsoft.common.core.annotation.IgnoreTenant;
import com.gxwebsoft.glt.entity.GltTicketTemplate; import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum;
import com.gxwebsoft.glt.service.GltTicketTemplateService; import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum;
import com.gxwebsoft.shop.entity.ShopDealerCapital;
import com.gxwebsoft.shop.entity.ShopDealerOrder;
import com.gxwebsoft.shop.entity.ShopDealerReferee;
import com.gxwebsoft.shop.entity.ShopDealerSetting;
import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.entity.ShopGoods;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.entity.ShopOrderGoods;
import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.mapper.UserMapper; import com.gxwebsoft.common.system.mapper.UserMapper;
import com.gxwebsoft.shop.service.ShopDealerCapitalService; import com.gxwebsoft.glt.entity.GltTicketTemplate;
import com.gxwebsoft.shop.service.ShopDealerOrderService; import com.gxwebsoft.glt.service.GltTicketTemplateService;
import com.gxwebsoft.shop.service.ShopDealerRefereeService; import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
import com.gxwebsoft.shop.service.ShopDealerSettingService; import com.gxwebsoft.shop.entity.*;
import com.gxwebsoft.shop.service.ShopDealerUserService; import com.gxwebsoft.shop.mapper.ShopDealerRefereeMapper;
import com.gxwebsoft.shop.service.ShopGoodsService; import com.gxwebsoft.shop.mapper.ShopOrderMapper;
import com.gxwebsoft.shop.service.ShopOrderService; import com.gxwebsoft.shop.service.*;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.util.UpstreamUserFinder; import com.gxwebsoft.shop.util.UpstreamUserFinder;
import com.alibaba.fastjson.JSONObject; import com.gxwebsoft.shop.vo.ShopDealerRefereeVO;
import com.gxwebsoft.shop.vo.ShopOrderGoodsVO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* 租户10584分销订单结算任务 * 租户10584分销订单结算任务
@@ -50,7 +47,6 @@ public class DealerOrderSettlement10584Task {
private static final BigDecimal RATE_0_10 = new BigDecimal("0.10"); private static final BigDecimal RATE_0_10 = new BigDecimal("0.10");
private static final BigDecimal RATE_0_05 = new BigDecimal("0.05"); private static final BigDecimal RATE_0_05 = new BigDecimal("0.05");
private static final BigDecimal RATE_0_03 = new BigDecimal("0.03");
private static final BigDecimal RATE_0_02 = new BigDecimal("0.02"); private static final BigDecimal RATE_0_02 = new BigDecimal("0.02");
private static final BigDecimal RATE_0_01 = new BigDecimal("0.01"); private static final BigDecimal RATE_0_01 = new BigDecimal("0.01");
private static final BigDecimal TOTAL_DEALER_DIVIDEND_RATE = RATE_0_01; private static final BigDecimal TOTAL_DEALER_DIVIDEND_RATE = RATE_0_01;
@@ -92,14 +88,23 @@ public class DealerOrderSettlement10584Task {
@Resource @Resource
private GltTicketTemplateService gltTicketTemplateService; private GltTicketTemplateService gltTicketTemplateService;
@Resource
private ShopOrderMapper shopOrderMapper;
@Resource
private ShopDealerRefereeMapper shopDealerRefereeMapper;
/** /**
* 每10秒执行一次。 * 每10秒执行一次。
*/ */
@Scheduled(cron = "0/10 * * * * ?") // @Scheduled(cron = "0/10 * * * * ?")
@IgnoreTenant("该定时任务仅处理租户10584但需要显式按tenantId过滤避免定时任务线程无租户上下文导致查询异常") @IgnoreTenant("该定时任务仅处理租户10584但需要显式按tenantId过滤避免定时任务线程无租户上下文导致查询异常")
public void settleTenant10584Orders() { public void settleTenant10584Orders() {
try { try {
//获取水票模板对应的商品信息列表
Set<Integer> waterFormIds = loadWaterFormIds(); Set<Integer> waterFormIds = loadWaterFormIds();
//查询商品列表存在已支付未核销订单数据
List<ShopOrder> orders = findUnsettledPaidOrders(waterFormIds); List<ShopOrder> orders = findUnsettledPaidOrders(waterFormIds);
if (orders.isEmpty()) { if (orders.isEmpty()) {
return; return;
@@ -108,7 +113,11 @@ public class DealerOrderSettlement10584Task {
// Per-run caches to reduce DB chatter across orders. // Per-run caches to reduce DB chatter across orders.
Map<Integer, Integer> level1ParentCache = new HashMap<>(); Map<Integer, Integer> level1ParentCache = new HashMap<>();
Map<Integer, Boolean> shopRoleCache = new HashMap<>(); Map<Integer, Boolean> shopRoleCache = new HashMap<>();
//获取系统设置分销等级
DealerBasicSetting dealerBasicSetting = findDealerBasicSetting(); DealerBasicSetting dealerBasicSetting = findDealerBasicSetting();
//获取分销员type=2第一个分销人作为平台总分红人
ShopDealerUser totalDealerUser = findTotalDealerUser(); ShopDealerUser totalDealerUser = findTotalDealerUser();
if (totalDealerUser == null || totalDealerUser.getUserId() == null) { if (totalDealerUser == null || totalDealerUser.getUserId() == null) {
log.warn("未找到分红账号,订单仍可结算但不会发放分红 - tenantId={}", TENANT_ID); log.warn("未找到分红账号,订单仍可结算但不会发放分红 - tenantId={}", TENANT_ID);
@@ -124,6 +133,7 @@ public class DealerOrderSettlement10584Task {
try { try {
transactionTemplate.executeWithoutResult(status -> { transactionTemplate.executeWithoutResult(status -> {
// 先“认领”订单:并发/多实例下避免重复结算update=0 表示被其他线程/实例处理) // 先“认领”订单:并发/多实例下避免重复结算update=0 表示被其他线程/实例处理)
//更新商品订单为已结算状态
if (!claimOrderToSettle(order.getOrderId(), waterFormIds)) { if (!claimOrderToSettle(order.getOrderId(), waterFormIds)) {
return; return;
} }
@@ -138,6 +148,88 @@ public class DealerOrderSettlement10584Task {
} }
} }
/**
* 每10分钟执行一次。
*/
@Scheduled(cron = "0 0/10 * * * ?")
@IgnoreTenant("该定时任务仅处理租户10584但需要显式按tenantId过滤避免定时任务线程无租户上下文导致查询异常")
public void settleTenant10584OrdersV2() {
try {
//获取水票模板对应的商品信息列表
Set<Integer> waterFormIds = loadWaterFormIds();
//查询商品列表存在已支付未核销订单数据【isSettled = 0 payStatus= 1 orderStatus 不在列表2, 3, 4, 5, 6, 7
List<ShopOrder> orders = findUnsettledPaidOrders(waterFormIds);
if (orders.isEmpty()) {
return;
}
// Per-run caches to reduce DB chatter across orders.
Map<Integer, Integer> level1ParentCache = new HashMap<>();
Map<Integer, Boolean> shopRoleCache = new HashMap<>();
//获取系统设置分销等级
DealerBasicSetting dealerBasicSetting = findDealerBasicSetting();
//获取分销员type=2第一个分销人作为平台总分红人
ShopDealerUser totalDealerUser = findTotalDealerUser();
if (totalDealerUser == null || totalDealerUser.getUserId() == null) {
log.warn("未找到分红账号,订单仍可结算但不会发放分红 - tenantId={}", TENANT_ID);
}
log.debug("租户{}分销设置 - level={}", TENANT_ID, dealerBasicSetting.level);
log.info("租户{}待结算订单数: {}, orderNos(sample)={}",
TENANT_ID,
orders.size(),
orders.stream().limit(10).map(ShopOrder::getOrderNo).toList());
for (ShopOrder order : orders) {
try {
transactionTemplate.executeWithoutResult(status -> {
// 先“认领”订单:并发/多实例下避免重复结算update=0 表示被其他线程/实例处理)
//更新商品订单为已结算状态
if (!claimOrderToSettle(order.getOrderId(), waterFormIds)) {
return;
}
settleOneOrderV2(order, level1ParentCache, shopRoleCache, totalDealerUser, dealerBasicSetting.level);
});
} catch (Exception e) {
log.error("订单结算失败,将回滚本订单并在下次任务重试 - orderId={}, orderNo={}", order.getOrderId(), order.getOrderNo(), e);
}
}
} catch (Exception e) {
log.error("租户{}分销订单结算任务执行失败", TENANT_ID, e);
}
}
/**
* 订单分销金、分润金结算
*/
@Transactional
public void orderSettlement(String orderNo){
LambdaQueryWrapper<ShopOrder> orderLambdaQueryWrapper = new LambdaQueryWrapper<ShopOrder>().eq(ShopOrder::getOrderNo, orderNo).eq(ShopOrder::getIsSettled, 0).eq(ShopOrder::getPayStatus, 1);
ShopOrder order = shopOrderService.getOne(orderLambdaQueryWrapper);
if(order != null){
//获取系统设置分销等级
DealerBasicSetting dealerBasicSetting = findDealerBasicSetting();
//获取分销员type=2第一个分销人作为平台总分红人
ShopDealerUser totalDealerUser = findTotalDealerUser();
Map<Integer, Integer> level1ParentCache = new HashMap<>();
Map<Integer, Boolean> shopRoleCache = new HashMap<>();
transactionTemplate.executeWithoutResult(status -> {
// 先“认领”订单:并发/多实例下避免重复结算update=0 表示被其他线程/实例处理)
//更新商品订单为已结算状态
if (!claimOrderToSettleV2(order.getOrderId())) {
return;
}
settleOneOrderV2(order, level1ParentCache, shopRoleCache, totalDealerUser, dealerBasicSetting.level);
});
}
}
private List<ShopOrder> findUnsettledPaidOrders(Set<Integer> waterFormIds) { private List<ShopOrder> findUnsettledPaidOrders(Set<Integer> waterFormIds) {
// 租户10584约定 // 租户10584约定
// - 普通订单以发货为准deliveryStatus=20才结算 // - 普通订单以发货为准deliveryStatus=20才结算
@@ -148,7 +240,7 @@ public class DealerOrderSettlement10584Task {
.eq(ShopOrder::getPayStatus, true) .eq(ShopOrder::getPayStatus, true)
.eq(ShopOrder::getIsSettled, 0) .eq(ShopOrder::getIsSettled, 0)
// 退款/取消订单不结算,避免“退款后仍发放分红/分润/佣金” // 退款/取消订单不结算,避免“退款后仍发放分红/分润/佣金”
.and(w -> w.notIn(ShopOrder::getOrderStatus, 2, 4, 5, 6, 7).or().isNull(ShopOrder::getOrderStatus)); .and(w -> w.notIn(ShopOrder::getOrderStatus, 2, 3, 4, 5, 6, 7).or().isNull(ShopOrder::getOrderStatus));
if (waterFormIds != null && !waterFormIds.isEmpty()) { if (waterFormIds != null && !waterFormIds.isEmpty()) {
qw.and(w -> w.eq(ShopOrder::getDeliveryStatus, 20).or().in(ShopOrder::getFormId, waterFormIds)); qw.and(w -> w.eq(ShopOrder::getDeliveryStatus, 20).or().in(ShopOrder::getFormId, waterFormIds));
@@ -178,6 +270,17 @@ public class DealerOrderSettlement10584Task {
return shopOrderService.update(uw); return shopOrderService.update(uw);
} }
private boolean claimOrderToSettleV2(Integer orderId) {
LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
.eq(ShopOrder::getOrderId, orderId)
.eq(ShopOrder::getTenantId, TENANT_ID)
.eq(ShopOrder::getIsSettled, 0)
// 二次防御:退款/取消订单不允许被“认领结算”
.and(w -> w.notIn(ShopOrder::getOrderStatus, 2, 3, 4, 5, 6, 7).or().isNull(ShopOrder::getOrderStatus));
uw.set(ShopOrder::getIsSettled, 1);
return shopOrderService.update(uw);
}
private Set<Integer> loadWaterFormIds() { private Set<Integer> loadWaterFormIds() {
try { try {
return gltTicketTemplateService.list( return gltTicketTemplateService.list(
@@ -252,6 +355,45 @@ public class DealerOrderSettlement10584Task {
log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), baseAmount); log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), baseAmount);
} }
private void settleOneOrderV2(ShopOrder order, Map<Integer, Integer> level1ParentCache, Map<Integer, Boolean> shopRoleCache,
ShopDealerUser totalDealerUser, int dealerLevel) {
if (order.getUserId() == null || order.getOrderNo() == null) {
throw new IllegalStateException("订单关键信息缺失,无法结算 - orderId=" + order.getOrderId());
}
BigDecimal totalPrice = order.getTotalPrice();
BigDecimal payPrice = order.getPayPrice();
BigDecimal rate = payPrice.divide(totalPrice, 2, RoundingMode.HALF_UP);
if(payPrice.compareTo(BigDecimal.ZERO) <= 0){
log.info("订单号:{}实付金额为0无需执行分销逻辑" + order.getOrderNo());
return;
}
//查询订单号订单所有已开启分销的商品分润信息
List<ShopOrderGoodsVO> orderGoodsVOList = shopOrderMapper.getOrderGoodsInfo(order.getOrderNo());
if(CollectionUtils.isNotEmpty(orderGoodsVOList)){
// 1) 直推/间推(直接增加冻结账户余额)
DealerRefereeCommissionV2 dealerRefereeCommission = settleDealerRefereeCommissionV2(order, rate, orderGoodsVOList, dealerLevel);
// 2) 门店分润上级:从下单用户开始逐级向上找,命中 ShopDealerUser.type=1 的最近两级(直推门店/间推门店)【只统计数据,不对分销账户进行处理,
// 已日结形式统计分销记录表shop_dealer_order 做对应一级二级管理津贴结算】
ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommissionV2(order, rate, orderGoodsVOList, level1ParentCache, shopRoleCache);
// 3) 分红:固定比率,每个订单都分 TODO 总分红未开发,还按原逻辑走
int goodsQty = orderGoodsVOList.stream().mapToInt(ShopOrderGoodsVO::getTotalNum).sum();
TotalDealerCommission totalDealerCommission = settleTotalDealerCommissionV2(order, goodsQty, totalDealerUser);
// 4) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准)
createDealerOrderRecordV2(order, dealerRefereeCommission, shopRoleCommission, totalDealerCommission);
log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), payPrice);
}else {
log.error("订单号:{},未找到下单分销商品数据!", order.getOrderNo());
return;
}
}
private DealerRefereeCommission settleDealerRefereeCommission( private DealerRefereeCommission settleDealerRefereeCommission(
ShopOrder order, ShopOrder order,
BigDecimal baseAmount, BigDecimal baseAmount,
@@ -329,6 +471,92 @@ public class DealerOrderSettlement10584Task {
return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney); return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney);
} }
/**
* 获取分销员分销霍金数据
* @param order
* @param orderGoodsVOList
* @param dealerLevel
* @return
*/
private DealerRefereeCommissionV2 settleDealerRefereeCommissionV2(ShopOrder order, BigDecimal rate, List<ShopOrderGoodsVO> orderGoodsVOList, int dealerLevel) {
Integer directDealerId = null;
Integer simpleDealerId = null;
AtomicReference<BigDecimal> directMoney = new AtomicReference<>(BigDecimal.ZERO);
AtomicReference<BigDecimal> simpleMoney = new AtomicReference<>(BigDecimal.ZERO);
ShopDealerRefereeVO dealerRefereeVO = shopDealerRefereeMapper.getDealerIdByUserId(order.getUserId());
if (dealerLevel == 1) {
Integer directUserId = dealerRefereeVO.getDirectUserId();
Integer directUserType = dealerRefereeVO.getDirectUserType();
if(directUserId != null && directUserType != null && directUserType == 0){
directDealerId = dealerRefereeVO.getDirectUserId();
}
}else {
Integer directUserId = dealerRefereeVO.getDirectUserId();
Integer directUserType = dealerRefereeVO.getDirectUserType();
Integer simpleUserId = dealerRefereeVO.getSimpleUserId();
Integer simpleUserType = dealerRefereeVO.getSimpleUserType();
if(directUserId != null && directUserType != null && directUserType == 0){
directDealerId = directUserId;
}
if(simpleUserId != null && simpleUserType != null && simpleUserType == 0){
simpleDealerId = simpleUserId;
}
}
if(directDealerId != null || simpleDealerId != null){
Integer finalDirectDealerId = directDealerId;
Integer finalSimpleDealerId = simpleDealerId;
orderGoodsVOList.forEach(orderGoodsVO -> {
//获取商品分润比例/金额
BigDecimal firstMoney = orderGoodsVO.getFirstMoney();
BigDecimal secondMoney = orderGoodsVO.getSecondMoney();
//按实付比例计算单项应参与分润金额
BigDecimal itemRatePrice = orderGoodsVO.getPrice().multiply(BigDecimal.valueOf(orderGoodsVO.getTotalNum())).multiply(rate);
//一级分销员存在(type = 0)且单项实付金额大于0及商品设置了一级分销比例/金额
if(finalDirectDealerId != null && itemRatePrice.compareTo(BigDecimal.ZERO) > 0 && firstMoney.compareTo(BigDecimal.ZERO) > 0){
BigDecimal one = calcMoneyByCommissionType(itemRatePrice, firstMoney, orderGoodsVO.getTotalNum(), 2, orderGoodsVO.getCommissionType());
directMoney.accumulateAndGet(one, BigDecimal::add);
}
//一级分销员存在(type = 0)且单项实付金额大于0及商品设置了一级分销比例/金额
if(finalSimpleDealerId != null && itemRatePrice.compareTo(BigDecimal.ZERO) > 0 && secondMoney.compareTo(BigDecimal.ZERO) > 0 ){
BigDecimal two = calcMoneyByCommissionType(itemRatePrice, secondMoney, orderGoodsVO.getTotalNum(), 2, orderGoodsVO.getCommissionType());
simpleMoney.accumulateAndGet(two, BigDecimal::add);
}
});
//一级分销员账户增加冻结金额
if (directDealerId != null && directMoney.get().compareTo(BigDecimal.ZERO) > 0) {
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
reduceDto.setTypeEnum(ShopDealerTypeEnum.FREEZE_ACCOUNT);
reduceDto.setUserId(directDealerId);
reduceDto.setOrderUserId(order.getUserId());
reduceDto.setOrderNo(order.getOrderNo());
reduceDto.setPrice(directMoney.get());
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DISTRIBUTION_INCOME);
shopDealerUserService.reduceBalance(reduceDto);
}
//二级分销员账户增加冻结金额
if (simpleDealerId != null && simpleMoney.get().compareTo(BigDecimal.ZERO) > 0) {
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
reduceDto.setTypeEnum(ShopDealerTypeEnum.FREEZE_ACCOUNT);
reduceDto.setUserId(simpleDealerId);
reduceDto.setOrderUserId(order.getUserId());
reduceDto.setOrderNo(order.getOrderNo());
reduceDto.setPrice(simpleMoney.get());
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DISTRIBUTION_INCOME);
shopDealerUserService.reduceBalance(reduceDto);
}
return new DealerRefereeCommissionV2(directDealerId, directMoney.get(), simpleDealerId, simpleMoney.get());
}
return null;
}
private Integer getDealerRefereeId(Integer userId) { private Integer getDealerRefereeId(Integer userId) {
return getDealerRefereeId(userId, 1); return getDealerRefereeId(userId, 1);
} }
@@ -412,6 +640,54 @@ public class DealerOrderSettlement10584Task {
return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney); return new ShopRoleCommission(shopRoleReferees.get(0), storeDirectMoney, shopRoleReferees.get(1), storeSimpleMoney);
} }
private ShopRoleCommission settleShopRoleRefereeCommissionV2(ShopOrder order, BigDecimal rate, List<ShopOrderGoodsVO> orderGoodsVOList, Map<Integer, Integer> level1ParentCache, Map<Integer, Boolean> shopRoleCache) {
List<Integer> shopRoleReferees = findFirstTwoShopRoleReferees(order.getUserId(), level1ParentCache, shopRoleCache);
log.info("门店分润命中结果(type=1门店角色取前两级) - orderNo={}, buyerUserId={}, shopRoleReferees={}",
order.getOrderNo(), order.getUserId(), shopRoleReferees);
if (shopRoleReferees.isEmpty()) {
return ShopRoleCommission.empty();
}
if(CollectionUtils.isNotEmpty(shopRoleReferees)){
Integer storeDirectUserId;
Integer storeSimpleUserId = null;
AtomicReference<BigDecimal> storeDirectMoney = new AtomicReference<>(BigDecimal.ZERO);
AtomicReference<BigDecimal> storeSimpleMoney = new AtomicReference<>(BigDecimal.ZERO);
if(shopRoleReferees.size() == 1){
storeDirectUserId = shopRoleReferees.get(0);
}else {
storeDirectUserId = shopRoleReferees.get(0);
storeSimpleUserId = shopRoleReferees.get(1);
}
Integer finalStoreDirectUserId = storeDirectUserId;
Integer finalStoreSimpleUserId = storeSimpleUserId;
orderGoodsVOList.forEach(orderGoodsVO ->{
//获取商品对应服务商管理费分润比例/金额
BigDecimal firstMoney = orderGoodsVO.getFirstDividend();
BigDecimal secondMoney = orderGoodsVO.getSecondDividend();
//按实付比例计算单项应参与分润金额
BigDecimal itemRatePrice = orderGoodsVO.getPrice().multiply(BigDecimal.valueOf(orderGoodsVO.getTotalNum())).multiply(rate);
if(finalStoreDirectUserId != null && itemRatePrice.compareTo(BigDecimal.ZERO) > 0){
BigDecimal one = calcMoneyByCommissionType(itemRatePrice, firstMoney, orderGoodsVO.getTotalNum(), 2, orderGoodsVO.getCommissionType());
storeDirectMoney.accumulateAndGet(one, BigDecimal::add);
}
if(finalStoreSimpleUserId != null && itemRatePrice.compareTo(BigDecimal.ZERO) > 0){
BigDecimal two = calcMoneyByCommissionType(itemRatePrice, secondMoney, orderGoodsVO.getTotalNum(), 2, orderGoodsVO.getCommissionType());
storeSimpleMoney.accumulateAndGet(two, BigDecimal::add);
}
});
return new ShopRoleCommission(storeDirectUserId, storeDirectMoney.get(), storeSimpleUserId, storeSimpleMoney.get());
}else {
return null;
}
}
private TotalDealerCommission settleTotalDealerCommission( private TotalDealerCommission settleTotalDealerCommission(
ShopOrder order, ShopOrder order,
BigDecimal baseAmount, BigDecimal baseAmount,
@@ -438,6 +714,32 @@ public class DealerOrderSettlement10584Task {
return new TotalDealerCommission(totalDealerUser.getUserId(), money); return new TotalDealerCommission(totalDealerUser.getUserId(), money);
} }
private TotalDealerCommission settleTotalDealerCommissionV2(ShopOrder order, int goodsQty, ShopDealerUser totalDealerUser) {
if (totalDealerUser == null || totalDealerUser.getUserId() == null) {
return TotalDealerCommission.empty();
}
BigDecimal rate = safePositive(totalDealerUser.getRate());
if (rate.signum() <= 0) {
rate = TOTAL_DEALER_DIVIDEND_RATE;
}
BigDecimal money = calcMoneyByCommissionType(order.getPayPrice(), rate, goodsQty, DIVIDEND_SCALE, 20);
//一级分销员账户增加冻结金额
if (money.compareTo(BigDecimal.ZERO) > 0) {
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
reduceDto.setTypeEnum(ShopDealerTypeEnum.FREEZE_ACCOUNT);
reduceDto.setUserId(totalDealerUser.getUserId());
reduceDto.setOrderUserId(order.getUserId());
reduceDto.setOrderNo(order.getOrderNo());
reduceDto.setPrice(money);
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.DIVIDEND_INCOME);
shopDealerUserService.reduceBalance(reduceDto);
return new TotalDealerCommission(totalDealerUser.getUserId(), money);
}
return TotalDealerCommission.empty();
}
private ShopDealerUser findTotalDealerUser() { private ShopDealerUser findTotalDealerUser() {
return shopDealerUserService.getOne( return shopDealerUserService.getOne(
new LambdaQueryWrapper<ShopDealerUser>() new LambdaQueryWrapper<ShopDealerUser>()
@@ -756,6 +1058,92 @@ public class DealerOrderSettlement10584Task {
order.getOrderNo(), dealerOrder.getFirstUserId(), dealerOrder.getSecondUserId(), dealerOrder.getFirstDividendUser(), dealerOrder.getSecondDividendUser()); order.getOrderNo(), dealerOrder.getFirstUserId(), dealerOrder.getSecondUserId(), dealerOrder.getFirstDividendUser(), dealerOrder.getSecondDividendUser());
} }
/**
* 记录订单分销业务
* @param order 商品订单
* @param dealerRefereeCommission 一级、二级分销员分销数据
* @param shopRoleCommission 门店/服务商一级、二级管理津贴数据
*/
private void createDealerOrderRecordV2(ShopOrder order, DealerRefereeCommissionV2 dealerRefereeCommission, ShopRoleCommission shopRoleCommission, TotalDealerCommission totalDealerCommission) {
// 幂等:同一订单只写一条(依赖 order_no + tenant_id 作为业务唯一)
ShopDealerOrder existed = shopDealerOrderService.getOne(
new LambdaQueryWrapper<ShopDealerOrder>()
.eq(ShopDealerOrder::getTenantId, TENANT_ID)
.eq(ShopDealerOrder::getOrderNo, order.getOrderNo())
.last("limit 1")
);
if (existed != null) {
// 允许“补发”门店分润时回填分润字段,避免订单已结算但分润字段一直为空,影响排查/对账。
LambdaUpdateWrapper<ShopDealerOrder> uw = new LambdaUpdateWrapper<ShopDealerOrder>()
.eq(ShopDealerOrder::getTenantId, TENANT_ID)
.eq(ShopDealerOrder::getOrderNo, order.getOrderNo());
boolean needUpdate = false;
if (shopRoleCommission != null && shopRoleCommission.storeDirectUserId != null) {
Integer existedUser = existed.getFirstDividendUser();
boolean needSetUser = existedUser == null;
boolean needSetMoney = existed.getFirstDividend() == null || existed.getFirstDividend().signum() == 0;
if (needSetUser) {
uw.set(ShopDealerOrder::getFirstDividendUser, shopRoleCommission.storeDirectUserId);
needUpdate = true;
}
boolean sameUser = existedUser == null || Objects.equals(existedUser, shopRoleCommission.storeDirectUserId);
if (sameUser && needSetMoney && shopRoleCommission.storeDirectMoney != null && shopRoleCommission.storeDirectMoney.signum() > 0) {
uw.set(ShopDealerOrder::getFirstDividend, shopRoleCommission.storeDirectMoney);
needUpdate = true;
}
}
if (shopRoleCommission != null && shopRoleCommission.storeSimpleUserId != null) {
Integer existedUser = existed.getSecondDividendUser();
boolean needSetUser = existedUser == null;
boolean needSetMoney = existed.getSecondDividend() == null || existed.getSecondDividend().signum() == 0;
if (needSetUser) {
uw.set(ShopDealerOrder::getSecondDividendUser, shopRoleCommission.storeSimpleUserId);
needUpdate = true;
}
boolean sameUser = existedUser == null || Objects.equals(existedUser, shopRoleCommission.storeSimpleUserId);
if (sameUser && needSetMoney && shopRoleCommission.storeSimpleMoney != null && shopRoleCommission.storeSimpleMoney.signum() > 0) {
uw.set(ShopDealerOrder::getSecondDividend, shopRoleCommission.storeSimpleMoney);
needUpdate = true;
}
}
if (needUpdate) {
shopDealerOrderService.update(uw);
log.info("ShopDealerOrder已存在回填门店分润字段 - orderNo={}, firstDividendUser={}, secondDividendUser={}",
order.getOrderNo(), shopRoleCommission.storeDirectUserId, shopRoleCommission.storeSimpleUserId);
} else {
log.info("ShopDealerOrder已存在跳过写入 - orderNo={}", order.getOrderNo());
}
return;
}else {
ShopDealerOrder dealerOrder = new ShopDealerOrder();
dealerOrder.setUserId(order.getUserId()); // 买家用户ID
dealerOrder.setOrderNo(order.getOrderNo());
dealerOrder.setOrderPrice(order.getTotalPrice());
dealerOrder.setPayPrice(order.getPayPrice());
//一级、二级分销员分销佣金统计
dealerOrder.setFirstUserId(dealerRefereeCommission != null ? dealerRefereeCommission.directDealerId : null);
dealerOrder.setFirstMoney(dealerRefereeCommission != null ? dealerRefereeCommission.directMoney : BigDecimal.ZERO);
dealerOrder.setSecondUserId(dealerRefereeCommission != null ? dealerRefereeCommission.simpleDealerId : null);
dealerOrder.setSecondMoney(dealerRefereeCommission != null ? dealerRefereeCommission.simpleMoney : BigDecimal.ZERO);
//门店(角色shop)两级分润单独落字段(详细以 ShopDealerCapital 为准)
dealerOrder.setFirstDividendUser(shopRoleCommission != null ? shopRoleCommission.storeDirectUserId : null);
dealerOrder.setFirstDividend(shopRoleCommission != null ? shopRoleCommission.storeDirectMoney : BigDecimal.ZERO);
dealerOrder.setSecondDividendUser(shopRoleCommission != null ? shopRoleCommission.storeSimpleUserId : null);
dealerOrder.setSecondDividend(shopRoleCommission != null ? shopRoleCommission.storeSimpleMoney : BigDecimal.ZERO);
dealerOrder.setIsSettled(1);
dealerOrder.setSettleTime(LocalDateTime.now());
dealerOrder.setMonth(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM")));
dealerOrder.setTenantId(TENANT_ID);
dealerOrder.setComments(buildCommissionTraceCommentV2(dealerRefereeCommission, shopRoleCommission, totalDealerCommission));
shopDealerOrderService.save(dealerOrder);
}
}
private String buildCommissionTraceComment( private String buildCommissionTraceComment(
DealerRefereeCommission dealerRefereeCommission, DealerRefereeCommission dealerRefereeCommission,
ShopRoleCommission shopRoleCommission, ShopRoleCommission shopRoleCommission,
@@ -770,6 +1158,30 @@ public class DealerOrderSettlement10584Task {
+ ",totalDealer=" + totalDealerCommission.userId + ":" + totalDealerCommission.money; + ",totalDealer=" + totalDealerCommission.userId + ":" + totalDealerCommission.money;
} }
private String buildCommissionTraceCommentV2(
DealerRefereeCommissionV2 dealerRefereeCommission,
ShopRoleCommission shopRoleCommission,
TotalDealerCommission totalDealerCommission
) {
// 轻量“过程”留痕,方便排查;详细分佣以 ShopDealerCapital 为准。
Integer direct = dealerRefereeCommission != null ? dealerRefereeCommission.directDealerId : null;
BigDecimal directMoney = dealerRefereeCommission != null ? dealerRefereeCommission.directMoney : BigDecimal.ZERO;
Integer simpleDealerId = dealerRefereeCommission != null ? dealerRefereeCommission.simpleDealerId : null;
BigDecimal simpleMoney = dealerRefereeCommission != null ? dealerRefereeCommission.simpleMoney : BigDecimal.ZERO;
Integer storeDirectUserId = shopRoleCommission != null ? shopRoleCommission.storeDirectUserId : null;
BigDecimal storeDirectMoney = shopRoleCommission != null ? shopRoleCommission.storeDirectMoney : BigDecimal.ZERO;
Integer storeSimpleUserId = shopRoleCommission != null ? shopRoleCommission.storeSimpleUserId : null;
BigDecimal storeSimpleMoney = shopRoleCommission != null ? shopRoleCommission.storeSimpleMoney : BigDecimal.ZERO;
Integer userId = totalDealerCommission != null ? totalDealerCommission.userId : null;
BigDecimal money = totalDealerCommission != null ? totalDealerCommission.money : BigDecimal.ZERO;
return "direct=" + direct + ":" + directMoney
+ ",simple=" + simpleDealerId + ":" + simpleMoney
+ ",dividend1=" + storeDirectUserId + ":" + storeDirectMoney
+ ",dividend2=" + storeSimpleUserId + ":" + storeSimpleMoney
+ ",totalDealer=" + userId + ":" + money;
}
private BigDecimal getOrderBaseAmount(ShopOrder order) { private BigDecimal getOrderBaseAmount(ShopOrder order) {
if (order == null) { if (order == null) {
return null; return null;
@@ -962,6 +1374,25 @@ public class DealerOrderSettlement10584Task {
} }
} }
private static class DealerRefereeCommissionV2 {
private final Integer directDealerId;
private final BigDecimal directMoney;
private final Integer simpleDealerId;
private final BigDecimal simpleMoney;
private DealerRefereeCommissionV2(
Integer directDealerId,
BigDecimal directMoney,
Integer simpleDealerId,
BigDecimal simpleMoney
) {
this.directDealerId = directDealerId;
this.directMoney = directMoney != null ? directMoney : BigDecimal.ZERO;
this.simpleDealerId = simpleDealerId;
this.simpleMoney = simpleMoney != null ? simpleMoney : BigDecimal.ZERO;
}
}
private static class ShopRoleCommission { private static class ShopRoleCommission {
private final Integer storeDirectUserId; private final Integer storeDirectUserId;
private final BigDecimal storeDirectMoney; private final BigDecimal storeDirectMoney;

View File

@@ -33,7 +33,7 @@ public class GltTicketIssue10584Task {
private final AtomicBoolean running = new AtomicBoolean(false); private final AtomicBoolean running = new AtomicBoolean(false);
@Scheduled(cron = "${glt.ticket.issue10584.cron:0/15 * * * * ?}") // @Scheduled(cron = "${glt.ticket.issue10584.cron:0/15 * * * * ?}")
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤") @IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
public void run() { public void run() {
if (!running.compareAndSet(false, true)) { if (!running.compareAndSet(false, true)) {

View File

@@ -4,18 +4,19 @@ import com.gxwebsoft.common.core.annotation.IgnoreTenant;
import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService; import com.gxwebsoft.glt.service.GltUserTicketAutoReleaseService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* 冻结水票自动释放任务: * 冻结水票自动释放任务:每月1日凌晨1-3点每2分钟执行一次任务
* - 扫描 glt_user_ticket_release 中到期且待释放status=0的记录 * - 扫描 glt_user_ticket_release 中本月到期且待释放status=0的记录
* - 释放成功:frozen -> available并将 release.status 置为 1 * - 释放成功:
* 1.修改水票释放任务为已释放状态 glt_user_ticket_release
* 2.修改个人水票中:可用数量、冻结数量、已释放数量 glt_user_ticket
* 3.同步生产水票释放记录数据 glt_user_ticket_log
*/ */
@Slf4j @Slf4j
@Component @Component
@@ -25,25 +26,17 @@ public class GltUserTicketAutoReleaseTask {
private final GltUserTicketAutoReleaseService autoReleaseService; private final GltUserTicketAutoReleaseService autoReleaseService;
@Value("${glt.ticket.auto-release.batch-size:200}")
private int batchSize;
private final AtomicBoolean running = new AtomicBoolean(false); private final AtomicBoolean running = new AtomicBoolean(false);
@Scheduled(cron = "${glt.ticket.auto-release.cron:0 */10 * * * ?}") @Scheduled(cron = "${glt.ticket.auto-release.cron:0 */2 1-3 1 * ?}")
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;释放记录自带 tenantId更新时会校验 tenantId") @IgnoreTenant("定时任务无登录态,需忽略租户隔离;释放记录自带 tenantId更新时会校验 tenantId")
public void run() { public void run() {
if (!running.compareAndSet(false, true)) { if (!running.compareAndSet(false, true)) {
log.warn("冻结水票自动释放任务仍在执行中,本轮跳过"); log.warn("冻结水票自动释放任务仍在执行中,本轮跳过");
return; return;
} }
try { try {
LocalDateTime now = LocalDateTime.now(); autoReleaseService.releaseTask();
int released = autoReleaseService.releaseDue(now, Math.max(batchSize, 1));
if (released > 0) {
log.info("冻结水票自动释放完成 - released={}, now={}", released, now);
}
} finally { } finally {
running.set(false); running.set(false);
} }

View File

@@ -0,0 +1,127 @@
package com.gxwebsoft.shop.controller;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.shop.service.ShopActiveImageService;
import com.gxwebsoft.shop.entity.ShopActiveImage;
import com.gxwebsoft.shop.param.ShopActiveImageParam;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.annotation.OperationLog;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
/**
* 推广码底图控制器
*
* @author xm
* @since 2026-04-27 18:02:18
*/
@Tag(name = "推广码底图管理")
@RestController
@RequestMapping("/api/shop/shop-active-image")
public class ShopActiveImageController extends BaseController {
@Resource
private ShopActiveImageService shopActiveImageService;
// @PreAuthorize("hasAuthority('shop:shopActiveImage:list')")
@Operation(summary = "分页查询推广码底图")
@GetMapping("/page")
public ApiResult<PageResult<ShopActiveImage>> page(ShopActiveImageParam param) {
// 使用关联查询
return success(shopActiveImageService.pageRel(param));
}
// @PreAuthorize("hasAuthority('shop:shopActiveImage:list')")
@Operation(summary = "查询全部推广码底图")
@GetMapping()
public ApiResult<List<ShopActiveImage>> list(ShopActiveImageParam param) {
// 使用关联查询
return success(shopActiveImageService.listRel(param));
}
// @PreAuthorize("hasAuthority('shop:shopActiveImage:list')")
@Operation(summary = "根据id查询推广码底图")
@GetMapping("/{id}")
public ApiResult<ShopActiveImage> get(@PathVariable("id") Integer id) {
// 使用关联查询
return success(shopActiveImageService.getByIdRel(id));
}
// @PreAuthorize("hasAuthority('shop:shopActiveImage:save')")
@OperationLog
@Operation(summary = "添加推广码底图")
@PostMapping()
public ApiResult<?> save(@RequestBody ShopActiveImage shopActiveImage) {
shopActiveImage.setCreator(String.valueOf(getLoginUserId()));
shopActiveImage.setCreateTime(LocalDateTime.now());
if (shopActiveImageService.save(shopActiveImage)) {
return success("添加成功");
}
return fail("添加失败");
}
// @PreAuthorize("hasAuthority('shop:shopActiveImage:update')")
@OperationLog
@Operation(summary = "修改推广码底图")
@PutMapping()
public ApiResult<?> update(@RequestBody ShopActiveImage shopActiveImage) {
shopActiveImage.setUpdater(String.valueOf(getLoginUserId()));
shopActiveImage.setUpdateTime(LocalDateTime.now());
if (shopActiveImageService.updateById(shopActiveImage)) {
return success("修改成功");
}
return fail("修改失败");
}
// @PreAuthorize("hasAuthority('shop:shopActiveImage:remove')")
@OperationLog
@Operation(summary = "删除推广码底图")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (shopActiveImageService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
@PreAuthorize("hasAuthority('shop:shopActiveImage:save')")
@OperationLog
@Operation(summary = "批量添加推广码底图")
@PostMapping("/batch")
public ApiResult<?> saveBatch(@RequestBody List<ShopActiveImage> list) {
if (shopActiveImageService.saveBatch(list)) {
return success("添加成功");
}
return fail("添加失败");
}
@PreAuthorize("hasAuthority('shop:shopActiveImage:update')")
@OperationLog
@Operation(summary = "批量修改推广码底图")
@PutMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody BatchParam<ShopActiveImage> batchParam) {
if (batchParam.update(shopActiveImageService, "id")) {
return success("修改成功");
}
return fail("修改失败");
}
// @PreAuthorize("hasAuthority('shop:shopActiveImage:remove')")
@OperationLog
@Operation(summary = "批量删除推广码底图")
@DeleteMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
if (shopActiveImageService.removeByIds(ids)) {
return success("删除成功");
}
return fail("删除失败");
}
}

View File

@@ -1,17 +1,17 @@
package com.gxwebsoft.shop.controller; package com.gxwebsoft.shop.controller;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.shop.service.ShopDealerCapitalService; import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.entity.ShopDealerCapital; import com.gxwebsoft.shop.entity.ShopDealerCapital;
import com.gxwebsoft.shop.param.ShopDealerCapitalParam; import com.gxwebsoft.shop.param.ShopDealerCapitalParam;
import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.shop.service.ShopDealerCapitalService;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.system.entity.User;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -34,7 +34,7 @@ public class ShopDealerCapitalController extends BaseController {
@PreAuthorize("hasAuthority('shop:shopDealerCapital:list')") @PreAuthorize("hasAuthority('shop:shopDealerCapital:list')")
@Operation(summary = "分页查询分销商资金明细表") @Operation(summary = "分页查询分销商资金明细表")
@GetMapping("/page") @GetMapping("/page")
public ApiResult<PageResult<ShopDealerCapital>> page(ShopDealerCapitalParam param) { public ApiResult<PageResult<ShopDealerCapital>> page(@ParameterObject ShopDealerCapitalParam param) {
// 使用关联查询 // 使用关联查询
return success(shopDealerCapitalService.pageRel(param)); return success(shopDealerCapitalService.pageRel(param));
} }

View File

@@ -1,19 +1,19 @@
package com.gxwebsoft.shop.controller; package com.gxwebsoft.shop.controller;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.Constants; import com.gxwebsoft.common.core.Constants;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.exception.BusinessException; import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.shop.service.ShopDealerRefereeService; import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.entity.ShopDealerReferee; import com.gxwebsoft.shop.entity.ShopDealerReferee;
import com.gxwebsoft.shop.param.ShopDealerRefereeParam; import com.gxwebsoft.shop.param.ShopDealerRefereeParam;
import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.shop.service.ShopDealerRefereeService;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.system.entity.User;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -41,6 +41,13 @@ public class ShopDealerRefereeController extends BaseController {
return success(shopDealerRefereeService.pageRel(param)); return success(shopDealerRefereeService.pageRel(param));
} }
@PreAuthorize("hasAuthority('shop:shopDealerReferee:list')")
@Operation(summary = "分页查询分销商推荐关系表")
@GetMapping("/appPage")
public ApiResult<PageResult<ShopDealerReferee>> appPage(@ParameterObject ShopDealerRefereeParam param) {
return success(shopDealerRefereeService.appPage(param));
}
@PreAuthorize("hasAuthority('shop:shopDealerReferee:list')") @PreAuthorize("hasAuthority('shop:shopDealerReferee:list')")
@Operation(summary = "查询全部分销商推荐关系表") @Operation(summary = "查询全部分销商推荐关系表")
@GetMapping() @GetMapping()

View File

@@ -4,18 +4,19 @@ import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams; import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.common.core.utils.JSONUtil;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.shop.service.ShopDealerUserService;
import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.param.ShopDealerUserParam;
import com.gxwebsoft.shop.param.ShopDealerUserImportParam;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.annotation.OperationLog; import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.utils.JSONUtil;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.dto.ShopDealerRefundDto;
import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.param.ShopDealerUserImportParam;
import com.gxwebsoft.shop.param.ShopDealerUserParam;
import com.gxwebsoft.shop.service.ShopDealerUserService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@@ -178,4 +179,18 @@ public class ShopDealerUserController extends BaseController {
return fail("导入失败", null); return fail("导入失败", null);
} }
@Operation(summary = "分销结算")
@PostMapping("/dealerCapital")
@Deprecated
public ApiResult<?> dealerCapital(@RequestBody ShopDealerUserReduceDto reduceDto) {
return success(shopDealerUserService.reduceBalance(reduceDto));
}
@Operation(summary = "分销退单")
@PostMapping("/refundOrder")
@Deprecated
public ApiResult<?> refundOrder(@RequestBody ShopDealerRefundDto refundDto) {
return success(shopDealerUserService.refundOrder(refundDto));
}
} }

View File

@@ -2,6 +2,7 @@ package com.gxwebsoft.shop.controller;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.system.redis.OrderNoUtils;
import com.gxwebsoft.shop.entity.ShopDealerUser; import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.service.ShopDealerUserService; import com.gxwebsoft.shop.service.ShopDealerUserService;
import com.gxwebsoft.shop.service.ShopDealerWithdrawService; import com.gxwebsoft.shop.service.ShopDealerWithdrawService;
@@ -43,6 +44,8 @@ public class ShopDealerWithdrawController extends BaseController {
private ShopDealerUserService shopDealerUserService; private ShopDealerUserService shopDealerUserService;
@Resource @Resource
private WxTransferService wxTransferService; private WxTransferService wxTransferService;
@Resource
private OrderNoUtils orderNoUtils;
@PreAuthorize("hasAuthority('shop:shopDealerWithdraw:list')") @PreAuthorize("hasAuthority('shop:shopDealerWithdraw:list')")
@Operation(summary = "分页查询分销商提现明细表") @Operation(summary = "分页查询分销商提现明细表")
@@ -89,6 +92,8 @@ public class ShopDealerWithdrawController extends BaseController {
return fail("tenantId为空无法发起提现"); return fail("tenantId为空无法发起提现");
} }
String orderNo = orderNoUtils.generate("WD");
shopDealerWithdraw.setOrderNo(orderNo);
shopDealerWithdraw.setTenantId(tenantId); shopDealerWithdraw.setTenantId(tenantId);
shopDealerWithdraw.setUserId(loginUser.getUserId()); shopDealerWithdraw.setUserId(loginUser.getUserId());
@@ -251,7 +256,8 @@ public class ShopDealerWithdrawController extends BaseController {
} }
// 使用提现记录ID构造单号保持幂等微信要求 5-32 且仅字母/数字 // 使用提现记录ID构造单号保持幂等微信要求 5-32 且仅字母/数字
String outBillNo = String.format("WD%03d", db.getId()); String outBillNo = db.getOrderNo();
String remark = "分销商提现"; String remark = "分销商提现";
String userName = db.getRealName(); String userName = db.getRealName();

View File

@@ -0,0 +1,156 @@
package com.gxwebsoft.shop.controller;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.entity.ShopFlashSaleActivity;
import com.gxwebsoft.shop.param.ShopFlashSaleActivityParam;
import com.gxwebsoft.shop.service.ShopFlashSaleActivityService;
import com.gxwebsoft.shop.vo.ShopFlashSaleActivityVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* 秒杀活动控制器
*
* @author xm
* @since 2026-04-22 17:18:18
*/
@Tag(name = "秒杀活动管理")
@RestController
@RequestMapping("/api/shop/shop-flash-sale-activity")
public class ShopFlashSaleActivityController extends BaseController {
@Resource
private ShopFlashSaleActivityService shopFlashSaleActivityService;
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:list')")
@Operation(summary = "后台分页查询秒杀活动")
@GetMapping("/page")
public ApiResult<PageResult<ShopFlashSaleActivityVO>> page(ShopFlashSaleActivityParam param) {
// 使用关联查询
return success(shopFlashSaleActivityService.pageRel(param));
}
@Operation(summary = "个人获取秒杀活动数据")
@GetMapping("/getMyActive")
public ApiResult<List<ShopFlashSaleActivityVO>> getMyActive(@RequestParam("tenantId") Integer tenantId) {
// 使用关联查询
return success(shopFlashSaleActivityService.getMyActive(tenantId));
}
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:list')")
@Operation(summary = "查询全部秒杀活动")
@GetMapping()
public ApiResult<List<ShopFlashSaleActivity>> list(ShopFlashSaleActivityParam param) {
// 使用关联查询
return success(shopFlashSaleActivityService.listRel(param));
}
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:list')")
@Operation(summary = "根据id查询秒杀活动")
@GetMapping("/{id}")
public ApiResult<ShopFlashSaleActivity> get(@PathVariable("id") Integer id) {
// 使用关联查询
return success(shopFlashSaleActivityService.getByIdRel(id));
}
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:save')")
@OperationLog
@Operation(summary = "添加秒杀活动")
@PostMapping()
public ApiResult<?> save(@RequestBody ShopFlashSaleActivity shopFlashSaleActivity) {
// 记录当前登录用户id
User loginUser = getLoginUser();
if (loginUser != null) {
shopFlashSaleActivity.setCreator(loginUser.getUserId().toString());
}
if (shopFlashSaleActivityService.save(shopFlashSaleActivity)) {
return success("添加成功");
}
return fail("添加失败");
}
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:update')")
@OperationLog
@Operation(summary = "修改秒杀活动")
@PutMapping()
public ApiResult<?> update(@RequestBody ShopFlashSaleActivity shopFlashSaleActivity) {
shopFlashSaleActivity.setUpdater(String.valueOf(getLoginUserId()));
if (shopFlashSaleActivityService.updateById(shopFlashSaleActivity)) {
return success("修改成功");
}
return fail("修改失败");
}
@OperationLog
@Operation(summary = "开启/关闭秒杀活动状态")
@PutMapping("/updateStatus")
public ApiResult<?> updateStatus(@RequestParam("id") Integer id) {
return success(shopFlashSaleActivityService.updateStatus(id));
}
@Operation(summary = "修改秒杀活动排序")
@PutMapping("/updateSortNumber")
@Parameters({
@Parameter(name = "id", description = "活动ID", required = true, example = "1"),
@Parameter(name = "sortNumber", description = "排序", required = true, example = "2")
})
public ApiResult<?> updateSortNumber(@RequestParam("id") Integer id, @RequestParam("sortNumber") Integer sortNumber) {
return success(shopFlashSaleActivityService.updateSortNumber(id, sortNumber));
}
// @PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:remove')")
@OperationLog
@Operation(summary = "删除秒杀活动")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (shopFlashSaleActivityService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
@PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:save')")
@OperationLog
@Operation(summary = "批量添加秒杀活动")
@PostMapping("/batch")
public ApiResult<?> saveBatch(@RequestBody List<ShopFlashSaleActivity> list) {
if (shopFlashSaleActivityService.saveBatch(list)) {
return success("添加成功");
}
return fail("添加失败");
}
@PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:update')")
@OperationLog
@Operation(summary = "批量修改秒杀活动")
@PutMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody BatchParam<ShopFlashSaleActivity> batchParam) {
if (batchParam.update(shopFlashSaleActivityService, "id")) {
return success("修改成功");
}
return fail("修改失败");
}
@PreAuthorize("hasAuthority('shop:shopFlashSaleActivity:remove')")
@OperationLog
@Operation(summary = "批量删除秒杀活动")
@DeleteMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
if (shopFlashSaleActivityService.removeByIds(ids)) {
return success("删除成功");
}
return fail("删除失败");
}
}

View File

@@ -5,40 +5,40 @@ import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.config.CertificateProperties; import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.utils.RedisUtil; import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.utils.CertificateLoader; import com.gxwebsoft.common.core.utils.CertificateLoader;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.utils.WechatCertAutoConfig; import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
import com.gxwebsoft.common.core.utils.WechatPayConfigValidator; import com.gxwebsoft.common.core.utils.WechatPayConfigValidator;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.Payment; import com.gxwebsoft.common.system.entity.Payment;
import com.gxwebsoft.shop.entity.ShopOrderDelivery; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.entity.ShopUserAddress; import com.gxwebsoft.common.system.redis.OrderNoUtils;
import com.gxwebsoft.shop.service.*; import com.gxwebsoft.glt.service.GltTicketIssueService;
import com.gxwebsoft.glt.service.GltTicketRevokeService; import com.gxwebsoft.glt.service.GltTicketRevokeService;
import com.gxwebsoft.shop.service.impl.KuaiDi100Impl;
import com.gxwebsoft.shop.task.OrderAutoCancelTask;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.param.ShopOrderParam;
import com.gxwebsoft.shop.dto.OrderCreateRequest;
import com.gxwebsoft.shop.dto.OrderPrepayRequest;
import com.gxwebsoft.shop.dto.UpdatePaymentStatusRequest;
import com.gxwebsoft.payment.service.PaymentService;
import com.gxwebsoft.payment.dto.PaymentResponse; import com.gxwebsoft.payment.dto.PaymentResponse;
import com.gxwebsoft.payment.enums.PaymentType; import com.gxwebsoft.payment.enums.PaymentType;
import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.payment.service.PaymentService;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.shop.dto.*;
import com.gxwebsoft.common.core.web.BatchParam; import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.shop.entity.ShopOrderDelivery;
import com.gxwebsoft.shop.entity.ShopUserAddress;
import com.gxwebsoft.shop.param.ShopOrderParam;
import com.gxwebsoft.shop.service.*;
import com.gxwebsoft.shop.service.impl.KuaiDi100Impl;
import com.gxwebsoft.shop.task.OrderAutoCancelTask;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig; import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam; import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.model.Transaction; import com.wechat.pay.java.service.payments.model.Transaction;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@@ -110,6 +110,10 @@ public class ShopOrderController extends BaseController {
private GltTicketRevokeService gltTicketRevokeService; private GltTicketRevokeService gltTicketRevokeService;
@Resource @Resource
private ShopDealerCommissionRollbackService shopDealerCommissionRollbackService; private ShopDealerCommissionRollbackService shopDealerCommissionRollbackService;
@Resource
private GltTicketIssueService gltTicketIssueService;
@Resource
private OrderNoUtils orderNoUtils;
@Operation(summary = "分页查询订单") @Operation(summary = "分页查询订单")
@GetMapping("/page") @GetMapping("/page")
@@ -133,14 +137,13 @@ public class ShopOrderController extends BaseController {
return success(shopOrderService.getByIdRel(id)); return success(shopOrderService.getByIdRel(id));
} }
@Operation(summary = "添加订单") @Operation(summary = "添加订单【单商品添加】")
@PostMapping() @PostMapping()
public ApiResult<?> save(@RequestBody OrderCreateRequest request) { public ApiResult<?> save(@RequestBody OrderCreateRequest request) {
User loginUser = getLoginUser(); User loginUser = getLoginUser();
if (loginUser == null) { if (loginUser == null) {
return fail("用户未登录"); return fail("用户未登录");
} }
try { try {
Map<String, String> wxOrderInfo = orderBusinessService.createOrder(request, loginUser); Map<String, String> wxOrderInfo = orderBusinessService.createOrder(request, loginUser);
return success("下单成功", wxOrderInfo); return success("下单成功", wxOrderInfo);
@@ -494,12 +497,12 @@ public class ShopOrderController extends BaseController {
if (!Boolean.TRUE.equals(current.getPayStatus())) { if (!Boolean.TRUE.equals(current.getPayStatus())) {
return fail("订单未支付,无法退款"); return fail("订单未支付,无法退款");
} }
if (StrUtil.isNotBlank(current.getRefundOrder())) { if (StrUtil.isNotBlank(current.getRefundOrder()) || current.getOrderStatus() == 6) {
logger.warn("订单已经退款过,订单号: {}, 退款单号: {}", current.getOrderNo(), current.getRefundOrder()); logger.warn("订单已经退款过,订单号: {}, 退款单号: {}", current.getOrderNo(), current.getRefundOrder() != null ? current.getRefundOrder() : "");
return fail("订单已退款,请勿重复操作"); return fail("订单已退款,请勿重复操作");
} }
String refundNo = "RF" + IdUtil.getSnowflakeNextId(); String refundNo = orderNoUtils.generate("RF");
BigDecimal refundAmount = req.getRefundMoney(); BigDecimal refundAmount = req.getRefundMoney();
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) { if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
@@ -573,7 +576,7 @@ public class ShopOrderController extends BaseController {
rollbackOrder.setOrderNo(current.getOrderNo()); rollbackOrder.setOrderNo(current.getOrderNo());
rollbackOrder.setPayPrice(current.getPayPrice()); rollbackOrder.setPayPrice(current.getPayPrice());
rollbackOrder.setTotalPrice(current.getTotalPrice()); rollbackOrder.setTotalPrice(current.getTotalPrice());
boolean rollbackOk = shopDealerCommissionRollbackService.rollbackOnOrderRefund(rollbackOrder, refundAmount); boolean rollbackOk = shopDealerCommissionRollbackService.rollbackOnOrderRefundV2(rollbackOrder, refundAmount);
if (!rollbackOk) { if (!rollbackOk) {
logger.error("退款成功但回退分红/分润/佣金失败 - tenantId={}, orderId={}, orderNo={}", logger.error("退款成功但回退分红/分润/佣金失败 - tenantId={}, orderId={}, orderNo={}",
tenantId, current.getOrderId(), current.getOrderNo()); tenantId, current.getOrderId(), current.getOrderNo());
@@ -764,7 +767,7 @@ public class ShopOrderController extends BaseController {
@Schema(description = "异步通知11") @Schema(description = "异步通知11")
@PostMapping("/notify/{tenantId}") @PostMapping("/notify/{tenantId}")
public String wxNotify(@RequestHeader Map<String, String> header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) { public String wxNotify(@RequestHeader Map<String, String> header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) {
logger.info("异步通知*************** = " + tenantId); logger.info("异步通知*************** = " + body + ",租户:" +tenantId);
// 获取支付配置信息用于解密 // 获取支付配置信息用于解密
String key = "Payment:1:".concat(tenantId.toString()); String key = "Payment:1:".concat(tenantId.toString());
@@ -900,7 +903,7 @@ public class ShopOrderController extends BaseController {
logger.info("开始解析微信支付异步通知..."); logger.info("开始解析微信支付异步通知...");
Transaction transaction = parser.parse(requestParam, Transaction.class); Transaction transaction = parser.parse(requestParam, Transaction.class);
logger.info("✅ 异步通知解析成功 - 交易状态: {}, 商户订单号: {}", logger.info("✅ 异步通知解析成功 - 交易状态: {}, 商户订单号: {}",
transaction.getTradeStateDesc(), transaction.getOutTradeNo()); transaction.getTradeState(), transaction.getOutTradeNo());
// 使用枚举值判断支付状态,避免依赖状态描述字符串 // 使用枚举值判断支付状态,避免依赖状态描述字符串
if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) { if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
@@ -935,6 +938,10 @@ public class ShopOrderController extends BaseController {
System.out.println("实际付款金额 = " + order.getPayPrice()); System.out.println("实际付款金额 = " + order.getPayPrice());
// 更新订单状态并处理支付成功后的业务逻辑(包括累加商品销量) // 更新订单状态并处理支付成功后的业务逻辑(包括累加商品销量)
shopOrderService.updateByOutTradeNo(order); shopOrderService.updateByOutTradeNo(order);
//支付成功执行异步任务
gltTicketIssueService.paySuccessTask(order.getOrderNo(), tenantId);
return "SUCCESS"; return "SUCCESS";
} }
} }
@@ -955,6 +962,19 @@ public class ShopOrderController extends BaseController {
return "fail"; return "fail";
} }
@Operation(summary = "核销订单", description = "核销员扫码核销用户订单")
@PutMapping("/verifyOrder")
public ApiResult<Boolean> verifyOrder(@RequestBody VerifyShopOrderDto verifyDto){
return success(shopOrderService.verifyOrder(verifyDto));
}
@Operation(summary = "支付成功任务", description = "用户支付成功后执行任务")
@PutMapping("/paySuccessTask")
public ApiResult<Boolean> paySuccessTask(@RequestBody PaySuccessTaskDto taskDto){
gltTicketIssueService.paySuccessTask(taskDto.getOrderNo(), taskDto.getTenantId());
return success(Boolean.TRUE);
}
@Operation(summary = "更新订单支付状态", description = "用户支付成功后主动同步订单状态") @Operation(summary = "更新订单支付状态", description = "用户支付成功后主动同步订单状态")
@PutMapping("/payment-status") @PutMapping("/payment-status")
public ApiResult<?> updateOrderPaymentStatus(@RequestBody UpdatePaymentStatusRequest request) { public ApiResult<?> updateOrderPaymentStatus(@RequestBody UpdatePaymentStatusRequest request) {

View File

@@ -0,0 +1,88 @@
package com.gxwebsoft.shop.controller;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.shop.entity.ShopSurchargeConfig;
import com.gxwebsoft.shop.param.ShopSurchargeConfigParam;
import com.gxwebsoft.shop.service.ShopSurchargeConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* 步梯费用设置控制器
*
* @author xm
* @since 2026-04-28 16:30:00
*/
@Tag(name = "步梯费用设置管理")
@RestController
@RequestMapping("/api/shop/shop-surcharge-config")
public class ShopSurchargeConfigController extends BaseController {
@Resource
private ShopSurchargeConfigService shopSurchargeConfigService;
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:list')")
@Operation(summary = "分页查询步梯费用设置")
@GetMapping("/page")
public ApiResult<PageResult<ShopSurchargeConfig>> page(ShopSurchargeConfigParam param) {
// 使用关联查询
return success(shopSurchargeConfigService.pageRel(param));
}
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:list')")
@Operation(summary = "根据id查询步梯费用设置")
@GetMapping("/{id}")
public ApiResult<ShopSurchargeConfig> get(@PathVariable("id") Integer id) {
// 使用关联查询
return success(shopSurchargeConfigService.getByIdRel(id));
}
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:list')")
@Operation(summary = "根据类型查询步梯费用设置")
@GetMapping("/getInfoByType")
public ApiResult<ShopSurchargeConfig> getInfoByType(@RequestParam Integer type) {
// 使用关联查询
return success(shopSurchargeConfigService.getInfoByType(type));
}
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:save')")
@OperationLog
@Operation(summary = "添加步梯费用设置")
@PostMapping()
public ApiResult<Integer> save(@RequestBody ShopSurchargeConfig shopSurchargeConfig) {
return success(shopSurchargeConfigService.saveInfo(shopSurchargeConfig));
}
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:update')")
@OperationLog
@Operation(summary = "修改步梯费用设置")
@PutMapping()
public ApiResult<Boolean> update(@RequestBody ShopSurchargeConfig shopSurchargeConfig) {
return success(shopSurchargeConfigService.updateInfo(shopSurchargeConfig));
}
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:update')")
@OperationLog
@Operation(summary = "修改步梯费用设置")
@PutMapping("/updateStatus")
public ApiResult<Boolean> updateStatus(@RequestParam("id") Integer id) {
return success(shopSurchargeConfigService.updateStatus(id));
}
// @PreAuthorize("hasAuthority('shop:shopSurchargeConfig:remove')")
@OperationLog
@Operation(summary = "删除步梯费用设置")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (shopSurchargeConfigService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
}

View File

@@ -79,11 +79,8 @@
@OperationLog @OperationLog
@Operation(summary = "修改收货地址") @Operation(summary = "修改收货地址")
@PutMapping() @PutMapping()
public ApiResult<?> update(@RequestBody ShopUserAddress shopUserAddress) { public ApiResult<Boolean> update(@RequestBody ShopUserAddress shopUserAddress) {
if (shopUserAddressService.updateById(shopUserAddress)) { return success(shopUserAddressService.updateInfo(shopUserAddress));
return success("修改成功");
}
return fail("修改失败");
} }
@PreAuthorize("hasAuthority('shop:shopUserAddress:remove')") @PreAuthorize("hasAuthority('shop:shopUserAddress:remove')")

View File

@@ -27,6 +27,9 @@ public class OrderCreateRequest {
@Max(value = 2, message = "订单类型值无效") @Max(value = 2, message = "订单类型值无效")
private Integer type; private Integer type;
@Schema(description = "订单类型 1-及时自配送 2-自提 3-预约自配送 4-发快递 5-配送【系统自动识别电子围栏内转及时配送,电子围栏外发快递】")
private Integer orderType;
@Size(max = 60, message = "备注长度不能超过60个字符") @Size(max = 60, message = "备注长度不能超过60个字符")
@Schema(description = "订单标题") @Schema(description = "订单标题")
private String title; private String title;
@@ -147,6 +150,9 @@ public class OrderCreateRequest {
@NotNull(message = "租户ID不能为空") @NotNull(message = "租户ID不能为空")
private Integer tenantId; private Integer tenantId;
@Schema(description = "秒杀活动ID")
private Integer activityId;
@Schema(description = "订单商品列表") @Schema(description = "订单商品列表")
@Valid @Valid
@NotEmpty(message = "订单商品列表不能为空") @NotEmpty(message = "订单商品列表不能为空")
@@ -158,6 +164,9 @@ public class OrderCreateRequest {
@Data @Data
@Schema(name = "OrderGoodsItem", description = "订单商品项") @Schema(name = "OrderGoodsItem", description = "订单商品项")
public static class OrderGoodsItem { public static class OrderGoodsItem {
@Schema(description = "秒杀活动ID")
private Integer activityId;
@Schema(description = "商品ID", required = true) @Schema(description = "商品ID", required = true)
@NotNull(message = "商品ID不能为空") @NotNull(message = "商品ID不能为空")
private Integer goodsId; private Integer goodsId;

View File

@@ -0,0 +1,25 @@
package com.gxwebsoft.shop.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 支付成功执行请求类
*
* @author xm
* @since 2026-05-04
*/
@Data
@Schema(name = "PaySuccessTaskDto", description = "支付成功执行请求类")
public class PaySuccessTaskDto {
@Schema(description = "订单号")
@NotBlank(message = "订单号不能为空")
private String orderNo;
@Schema(description = "租户ID", example = "10584")
private Integer tenantId;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,25 @@
package com.gxwebsoft.shop.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 更新订单支付状态请求DTO
*
* @author xm
* @since 2026-05-04
*/
@Data
@Schema(name = "VerifyShopOrderDto", description = "更新订单支付状态请求")
public class VerifyShopOrderDto {
@Schema(description = "核销码")
@NotBlank(message = "核销码不能为空")
private String verifyCode;
@Schema(description = "核销码类型 1-普通核销【无推广佣金】 2-推广结算【有订单佣金】", example = "2")
private Integer verifyType;
}

View File

@@ -0,0 +1,71 @@
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 = "图片地址集合")
@TableField(exist = false)
private List<String> imgUrlList;
@Schema(description = "启用状态 0-启用 1-禁用")
private Integer status;
@Schema(description = "排序")
private Integer sortNumber;
@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;
}

View File

@@ -1,10 +1,11 @@
package com.gxwebsoft.shop.entity; package com.gxwebsoft.shop.entity;
import java.math.BigDecimal; import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@@ -20,6 +21,7 @@ import lombok.EqualsAndHashCode;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopDealerCapital对象", description = "分销商资金明细表") @Schema(name = "ShopDealerCapital对象", description = "分销商资金明细表")
@TableName("shop_dealer_capital")
public class ShopDealerCapital implements Serializable { public class ShopDealerCapital implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -27,26 +29,41 @@ public class ShopDealerCapital implements Serializable {
@TableId(value = "id", type = IdType.AUTO) @TableId(value = "id", type = IdType.AUTO)
private Integer id; private Integer id;
@Schema(description = "订单编号")
private String no;
@Schema(description = "分销商用户ID") @Schema(description = "分销商用户ID")
private Integer userId; private Integer userId;
@Schema(description = "变动类型 1-操作冻结账户余额 2-操作提现账户余额【直接结算】 3-解冻 4-退款")
private Integer type;
@Schema(description = "分销商昵称") @Schema(description = "分销商昵称")
@TableField(exist = false) @TableField(exist = false)
private String nickName; private String nickName;
@Schema(description = "订单编号") @Schema(description = "关联订单编号")
private String orderNo; private String orderNo;
@Schema(description = "订单状态") @Schema(description = "订单状态")
@TableField(exist = false) @TableField(exist = false)
private Integer orderStatus; private Integer orderStatus;
@Schema(description = "资金流动类型 (10佣金收入 20提现支出 30转账支出 40转账收入 50佣金解冻 60配送奖励)") @Schema(description = "资金流动类型 (10分销收入 11团队管理津贴收入 12分红收入 13现场推广收入 20提现支出 30转账支出 40转账收入 50佣金解冻 60配送奖励 70佣金退回【退单】)")
private Integer flowType; private Integer flowType;
@Schema(description = "金额") @Schema(description = "变更金额")
private BigDecimal money; private BigDecimal money;
@Schema(description = "变更后金额")
private BigDecimal moneyAfter;
@Schema(description = "变更冻结金额")
private BigDecimal freezeMoney;
@Schema(description = "变更冻结后金额")
private BigDecimal freezeMoneyAfter;
@Schema(description = "描述") @Schema(description = "描述")
private String comments; private String comments;
@@ -63,6 +80,9 @@ public class ShopDealerCapital implements Serializable {
@Schema(description = "商城ID") @Schema(description = "商城ID")
private Integer tenantId; private Integer tenantId;
@Schema(description = "创建人")
private Integer creator;
@Schema(description = "创建时间") @Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime; private LocalDateTime createTime;
@@ -71,4 +91,8 @@ public class ShopDealerCapital implements Serializable {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime; private LocalDateTime updateTime;
@Schema(description = "是否删除 0-未删 1-已删")
@TableLogic
private Integer deleted;
} }

View File

@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable; import java.io.Serializable;
@@ -23,6 +25,7 @@ import lombok.EqualsAndHashCode;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopDealerOrder对象", description = "分销商订单记录表") @Schema(name = "ShopDealerOrder对象", description = "分销商订单记录表")
@TableName("shop_dealer_order")
public class ShopDealerOrder implements Serializable { public class ShopDealerOrder implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -66,6 +69,9 @@ public class ShopDealerOrder implements Serializable {
@TableField(exist = false) @TableField(exist = false)
private String firstNickname; private String firstNickname;
@Schema(description = "分销佣金(一级)")
private BigDecimal firstMoney;
@Schema(description = "分销商用户id(二级)") @Schema(description = "分销商用户id(二级)")
private Integer secondUserId; private Integer secondUserId;
@@ -73,42 +79,57 @@ public class ShopDealerOrder implements Serializable {
@TableField(exist = false) @TableField(exist = false)
private String secondNickname; private String secondNickname;
@Schema(description = "分销商用户id(三级)")
private Integer thirdUserId;
@Schema(description = "分销商用户昵称(三级)")
@TableField(exist = false)
private String thirdNickname;
@Schema(description = "分销佣金(一级)")
private BigDecimal firstMoney;
@Schema(description = "分销佣金(二级)") @Schema(description = "分销佣金(二级)")
private BigDecimal secondMoney; private BigDecimal secondMoney;
@Schema(description = "分销佣金(三级)") @Schema(description = "分销商用户id(弃用)")
private Integer thirdUserId;
@Schema(description = "分销商用户昵称(弃用)")
@TableField(exist = false)
private String thirdNickname;
@Schema(description = "分销佣金(弃用)")
private BigDecimal thirdMoney; private BigDecimal thirdMoney;
@Schema(description = "门店(一级)") @Schema(description = "一级服务商/门店")
private Integer firstDividendUser; private Integer firstDividendUser;
@Schema(description = "门店名称(一级)") @Schema(description = "一级服务商/门店名称")
@TableField(exist = false) @TableField(exist = false)
private String firstDividendUserName; private String firstDividendUserName;
@Schema(description = "分红(一级)") @Schema(description = "一级服务商/门店管理津贴")
private BigDecimal firstDividend; private BigDecimal firstDividend;
@Schema(description = "门店(二级)") @Schema(description = "一级服务商/门店结算标识 0-否 1-是")
private Integer firstDividendFlag;
@Schema(description = "一级服务商/门店结算单号")
private String firstDividendNo;
@Schema(description = "一级服务商/门店结算时间")
private LocalDateTime firstDividendTime;
@Schema(description = "二级服务商/门店")
private Integer secondDividendUser; private Integer secondDividendUser;
@Schema(description = "门店名称(二级)") @Schema(description = "二级服务商/门店名称")
@TableField(exist = false) @TableField(exist = false)
private String secondDividendUserName; private String secondDividendUserName;
@Schema(description = "分红(二级)") @Schema(description = "二级服务商/门店管理津贴")
private BigDecimal secondDividend; private BigDecimal secondDividend;
@Schema(description = "二级服务商/门店结算标识 0-否 1-是")
private Integer secondDividendFlag;
@Schema(description = "二级服务商/门店结算单号")
private String secondDividendNo;
@Schema(description = "二级服务商/门店结算时间")
private LocalDateTime secondDividendTime;
@Schema(description = "佣金比例") @Schema(description = "佣金比例")
private BigDecimal rate; private BigDecimal rate;

View File

@@ -3,6 +3,8 @@ package com.gxwebsoft.shop.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable; import java.io.Serializable;
@@ -87,4 +89,16 @@ public class ShopDealerReferee implements Serializable {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime; private LocalDateTime updateTime;
@Schema(description = "团队成员")
@TableField(exist = false)
private Integer teamNum;
@Schema(description = "订单数")
@TableField(exist = false)
private Integer orderNum;
@Schema(description = "订单金额")
@TableField(exist = false)
private BigDecimal orderAmount;
} }

View File

@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@@ -20,6 +22,7 @@ import lombok.EqualsAndHashCode;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopDealerUser对象", description = "分销商用户记录表") @Schema(name = "ShopDealerUser对象", description = "分销商用户记录表")
@TableName("shop_dealer_user")
public class ShopDealerUser implements Serializable { public class ShopDealerUser implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@@ -27,6 +27,9 @@ public class ShopDealerWithdraw implements Serializable {
@TableId(value = "id", type = IdType.AUTO) @TableId(value = "id", type = IdType.AUTO)
private Integer id; private Integer id;
@Schema(description = "订单号")
private String orderNo;
@Schema(description = "分销商用户ID") @Schema(description = "分销商用户ID")
private Integer userId; private Integer userId;

View File

@@ -0,0 +1,101 @@
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 = "租户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;
}

View File

@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable; import java.io.Serializable;
@@ -21,6 +23,7 @@ import lombok.EqualsAndHashCode;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopGoods对象", description = "商品") @Schema(name = "ShopGoods对象", description = "商品")
@TableName("shop_goods")
public class ShopGoods implements Serializable { public class ShopGoods implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -104,6 +107,9 @@ public class ShopGoods implements Serializable {
@Schema(description = "配送奖金") @Schema(description = "配送奖金")
private BigDecimal deliveryMoney; private BigDecimal deliveryMoney;
@Schema(description = "推广核验佣金比率")
private BigDecimal verifyRate;
@Schema(description = "库存计算方式(10下单减库存 20付款减库存)") @Schema(description = "库存计算方式(10下单减库存 20付款减库存)")
@JsonAlias({"cdeductStockType"}) @JsonAlias({"cdeductStockType"})
private Integer deductStockType; private Integer deductStockType;
@@ -171,6 +177,15 @@ public class ShopGoods implements Serializable {
@Schema(description = "租户id") @Schema(description = "租户id")
private Integer tenantId; private Integer tenantId;
@Schema(description = "配送方式:1-自配送 2-自提 4-发快递 多属性用','隔开")
private String deliveryType;
@Schema(description = "水票标识 0-否 1-是")
private Integer waterTicketFlag;
@Schema(description = "水票ID")
private Integer waterTickerId;
@Schema(description = "创建时间") @Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime; private LocalDateTime createTime;

View File

@@ -1,12 +1,12 @@
package com.gxwebsoft.shop.entity; package com.gxwebsoft.shop.entity;
import java.math.BigDecimal; import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
@@ -25,6 +25,7 @@ import javax.validation.constraints.*;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopOrder对象", description = "订单") @Schema(name = "ShopOrder对象", description = "订单")
@TableName("shop_order")
public class ShopOrder implements Serializable { public class ShopOrder implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -32,6 +33,9 @@ public class ShopOrder implements Serializable {
@TableId(value = "order_id", type = IdType.AUTO) @TableId(value = "order_id", type = IdType.AUTO)
private Integer orderId; private Integer orderId;
@Schema(description = "订单类型 1-及时自配送 2-自提 3-预约自配送 4-发快递")
private Integer orderType;
@Schema(description = "订单编号") @Schema(description = "订单编号")
private String orderNo; private String orderNo;
@@ -306,6 +310,30 @@ public class ShopOrder implements Serializable {
@NotNull(message = "租户ID不能为空") @NotNull(message = "租户ID不能为空")
private Integer tenantId; private Integer tenantId;
@Schema(description = "秒杀活动ID")
private Integer activityId;
@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 = "推广核销佣金")
private BigDecimal verifyMoney;
@Schema(description = "修改时间") @Schema(description = "修改时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime; private LocalDateTime updateTime;
@@ -350,4 +378,8 @@ public class ShopOrder implements Serializable {
@TableField(exist = false) @TableField(exist = false)
private ShopOrderDelivery shopOrderDelivery; private ShopOrderDelivery shopOrderDelivery;
@Schema(description = "详细地址")
@TableField(exist = false)
private String fullAddress;
} }

View File

@@ -0,0 +1,66 @@
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 = "价格(元)")
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;
}

View File

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@@ -20,6 +22,7 @@ import lombok.EqualsAndHashCode;
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@Schema(name = "ShopUserAddress对象", description = "收货地址") @Schema(name = "ShopUserAddress对象", description = "收货地址")
@TableName("shop_user_address")
public class ShopUserAddress implements Serializable { public class ShopUserAddress implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -48,7 +51,7 @@ public class ShopUserAddress implements Serializable {
@Schema(description = "收货地址") @Schema(description = "收货地址")
private String address; private String address;
@Schema(description = "收货地址") @Schema(description = "详细地址")
private String fullAddress; private String fullAddress;
private String lat; private String lat;

View File

@@ -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<ShopActiveImage> {
/**
* 分页查询
*
* @param page 分页对象
* @param param 查询参数
* @return List<ShopActiveImage>
*/
List<ShopActiveImage> selectPageRel(@Param("page") IPage<ShopActiveImage> page,
@Param("param") ShopActiveImageParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return List<User>
*/
List<ShopActiveImage> selectListRel(@Param("param") ShopActiveImageParam param);
}

View File

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.shop.entity.ShopDealerReferee; import com.gxwebsoft.shop.entity.ShopDealerReferee;
import com.gxwebsoft.shop.param.ShopDealerRefereeParam; import com.gxwebsoft.shop.param.ShopDealerRefereeParam;
import com.gxwebsoft.shop.vo.ShopDealerRefereeVO;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@@ -34,4 +35,11 @@ public interface ShopDealerRefereeMapper extends BaseMapper<ShopDealerReferee> {
*/ */
List<ShopDealerReferee> selectListRel(@Param("param") ShopDealerRefereeParam param); List<ShopDealerReferee> selectListRel(@Param("param") ShopDealerRefereeParam param);
/**
* 通过用户ID查询上一级分销人信息【type = 0】
* @param userId 用户ID
* @return
*/
ShopDealerRefereeVO getDealerIdByUserId(@Param("userId") Integer userId);
} }

View File

@@ -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<ShopFlashSaleActivity> {
/**
* 分页查询
*
* @param page 分页对象
* @param param 查询参数
* @return List<ShopFlashSaleActivity>
*/
List<ShopFlashSaleActivityVO> selectPageRel(@Param("page") IPage<ShopFlashSaleActivity> page,
@Param("param") ShopFlashSaleActivityParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return List<User>
*/
List<ShopFlashSaleActivity> selectListRel(@Param("param") ShopFlashSaleActivityParam param);
}

View File

@@ -5,9 +5,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.shop.entity.ShopOrder; import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.param.ShopOrderParam; import com.gxwebsoft.shop.param.ShopOrderParam;
import com.gxwebsoft.shop.vo.ShopOrderGoodsVO;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
@@ -60,4 +60,11 @@ public interface ShopOrderMapper extends BaseMapper<ShopOrder> {
Map<String, Object> selectUserOrderStats(@Param("userId") Integer userId, Map<String, Object> selectUserOrderStats(@Param("userId") Integer userId,
@Param("tenantId") Integer tenantId, @Param("tenantId") Integer tenantId,
@Param("type") Integer type); @Param("type") Integer type);
/**
* 通过订单号查询订单所有商品分润信息
* @param orderNo 订单号
* @return
*/
List<ShopOrderGoodsVO> getOrderGoodsInfo(@Param("orderNo") String orderNo);
} }

View File

@@ -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<ShopSurchargeConfig> {
/**
* 分页查询
*
* @param page 分页对象
* @param param 查询参数
* @return List<ShopSurchargeConfig>
*/
List<ShopSurchargeConfig> selectPageRel(@Param("page") IPage<ShopSurchargeConfig> page,
@Param("param") ShopSurchargeConfigParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return List<User>
*/
List<ShopSurchargeConfig> selectListRel(@Param("param") ShopSurchargeConfigParam param);
}

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gxwebsoft.shop.mapper.ShopActiveImageMapper">
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*
FROM shop_active_image a
<where>
<if test="param.id != null">
AND a.id = #{param.id}
</if>
<if test="param.name != null">
AND a.name LIKE CONCAT('%', #{param.name}, '%')
</if>
<if test="param.type != null">
AND a.type = #{param.type}
</if>
<if test="param.imgUrl != null">
AND a.img_url LIKE CONCAT('%', #{param.imgUrl}, '%')
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.sortNumber != null">
AND a.sort_number = #{param.sortNumber}
</if>
<if test="param.creator != null">
AND a.creator LIKE CONCAT('%', #{param.creator}, '%')
</if>
<if test="param.createTimeStart != null">
AND a.create_time &gt;= #{param.createTimeStart}
</if>
<if test="param.createTimeEnd != null">
AND a.create_time &lt;= #{param.createTimeEnd}
</if>
<if test="param.updater != null">
AND a.updater LIKE CONCAT('%', #{param.updater}, '%')
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.keywords != null">
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
)
</if>
</where>
</sql>
<!-- 分页查询 -->
<select id="selectPageRel" resultType="com.gxwebsoft.shop.entity.ShopActiveImage">
<include refid="selectSql"></include>
</select>
<!-- 查询全部 -->
<select id="selectListRel" resultType="com.gxwebsoft.shop.entity.ShopActiveImage">
<include refid="selectSql"></include>
</select>
</mapper>

View File

@@ -40,7 +40,6 @@
</if> </if>
<if test="param.keywords != null"> <if test="param.keywords != null">
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
OR a.user_id = #{param.keywords}
OR a.order_no = #{param.keywords} OR a.order_no = #{param.keywords}
) )
</if> </if>

View File

@@ -57,5 +57,21 @@
<select id="selectListRel" resultType="com.gxwebsoft.shop.entity.ShopDealerReferee"> <select id="selectListRel" resultType="com.gxwebsoft.shop.entity.ShopDealerReferee">
<include refid="selectSql"></include> <include refid="selectSql"></include>
</select> </select>
<select id="getDealerIdByUserId" resultType="com.gxwebsoft.shop.vo.ShopDealerRefereeVO">
SELECT
b.user_id directUserId,
b.type directUserType,
d.user_id simpleUserId,
d.type simpleUserType
FROM
shop_dealer_referee a
LEFT JOIN shop_dealer_user b ON a.dealer_id = b.user_id
LEFT JOIN shop_dealer_referee c ON b.user_id = c.user_id
LEFT JOIN shop_dealer_user d ON d.user_id = c.dealer_id
WHERE
b.is_delete = 0
and d.is_delete = 0
AND a.user_id = #{userId}
</select>
</mapper> </mapper>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gxwebsoft.shop.mapper.ShopFlashSaleActivityMapper">
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*
FROM shop_flash_sale_activity a
<where>
<if test="param.id != null">
AND a.id = #{param.id}
</if>
<if test="param.name != null">
AND a.name LIKE CONCAT('%', #{param.name}, '%')
</if>
<if test="param.goodsId != null">
AND a.goods_id LIKE CONCAT('%', #{param.goodsId}, '%')
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.startTime != null">
AND a.start_time LIKE CONCAT('%', #{param.startTime}, '%')
</if>
<if test="param.endTime != null">
AND a.end_time LIKE CONCAT('%', #{param.endTime}, '%')
</if>
<if test="param.stock != null">
AND a.stock = #{param.stock}
</if>
<if test="param.displayType != null">
AND a.display_type = #{param.displayType}
</if>
<if test="param.remark != null">
AND a.remark LIKE CONCAT('%', #{param.remark}, '%')
</if>
<if test="param.sortNumber != null">
AND a.sort_number = #{param.sortNumber}
</if>
<if test="param.creator != null">
AND a.creator LIKE CONCAT('%', #{param.creator}, '%')
</if>
<if test="param.createTimeStart != null">
AND a.create_time &gt;= #{param.createTimeStart}
</if>
<if test="param.createTimeEnd != null">
AND a.create_time &lt;= #{param.createTimeEnd}
</if>
<if test="param.updater != null">
AND a.updater LIKE CONCAT('%', #{param.updater}, '%')
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.keywords != null">
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
)
</if>
</where>
</sql>
<!-- 分页查询 -->
<select id="selectPageRel" resultType="com.gxwebsoft.shop.vo.ShopFlashSaleActivityVO">
<include refid="selectSql"></include>
</select>
<!-- 查询全部 -->
<select id="selectListRel" resultType="com.gxwebsoft.shop.entity.ShopFlashSaleActivity">
<include refid="selectSql"></include>
</select>
</mapper>

View File

@@ -233,10 +233,6 @@
OR b.nickname LIKE CONCAT('%', #{param.keywords}, '%') OR b.nickname LIKE CONCAT('%', #{param.keywords}, '%')
) )
</if> </if>
<if test="param.statusFilter != 8">
<!-- 默认过滤已关闭订单 -->
AND a.order_status != 2
</if>
<!-- 订单状态筛选:-1全部0待支付1待发货2待核销3待收货4待评价5已完成6已退款7已删除 --> <!-- 订单状态筛选:-1全部0待支付1待发货2待核销3待收货4待评价5已完成6已退款7已删除 -->
<if test="param.statusFilter != null and param.statusFilter != -1"> <if test="param.statusFilter != null and param.statusFilter != -1">
<if test="param.statusFilter == 0"> <if test="param.statusFilter == 0">
@@ -245,27 +241,27 @@
</if> </if>
<if test="param.statusFilter == 1"> <if test="param.statusFilter == 1">
<!-- 1待发货已付款但未发货 --> <!-- 1待发货已付款但未发货 -->
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)
</if> </if>
<if test="param.statusFilter == 2"> <if test="param.statusFilter == 2">
<!-- 2待核销已付款但订单状态为未使用 --> <!-- 2待核销已付款但订单状态为未使用 -->
AND a.pay_status = 1 AND a.order_status = 0 AND a.pay_status = 1 AND a.order_status = 0 and order_type = 2
</if> </if>
<if test="param.statusFilter == 3"> <if test="param.statusFilter == 3">
<!-- 3待收货已发货但订单状态不是已完成 --> <!-- 3待收货已发货但订单状态不是已完成 -->
AND a.delivery_status = 20 AND a.order_status != 1 AND a.pay_status = 1 AND a.delivery_status = 20 AND a.order_status = 0
</if> </if>
<if test="param.statusFilter == 4"> <if test="param.statusFilter == 4">
<!-- 4待评价订单已完成但可能需要评价 --> <!-- 4待评价订单已完成但可能需要评价 -->
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
</if> </if>
<if test="param.statusFilter == 5"> <if test="param.statusFilter == 5">
<!-- 5已完成订单状态为已完成 --> <!-- 5已完成订单状态为已完成 -->
AND a.order_status = 1 AND a.pay_status = 1 AND a.order_status = 1
</if> </if>
<if test="param.statusFilter == 6"> <if test="param.statusFilter == 6">
<!-- 6退款/售后:订单状态为退款成功 --> <!-- 6退款/售后:订单状态为退款成功 -->
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)
</if> </if>
<if test="param.statusFilter == 7"> <if test="param.statusFilter == 7">
<!-- 7已删除订单被删除 --> <!-- 7已删除订单被删除 -->
@@ -301,13 +297,13 @@
SELECT SELECT
-- “全部”默认也会过滤已关闭(取消)订单statusFilter != 8 时会加 a.order_status != 2 -- “全部”默认也会过滤已关闭(取消)订单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 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.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.pay_status = 1 AND a.order_status = 0 AND a.order_type in(1, 3, 4) AND a.delivery_status = 10 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.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.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.pay_status = 1 AND a.order_status = 0 AND a.delivery_status = 20 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.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.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.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.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 = 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 = 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 COALESCE(SUM(CASE WHEN a.deleted = 0 AND a.order_status = 2 THEN 1 ELSE 0 END), 0) AS canceled
FROM shop_order a FROM shop_order a
@@ -324,6 +320,28 @@
<select id="getByOutTradeNo" resultType="com.gxwebsoft.shop.entity.ShopOrder"> <select id="getByOutTradeNo" resultType="com.gxwebsoft.shop.entity.ShopOrder">
SELECT * FROM shop_order WHERE order_no = #{outTradeNo} SELECT * FROM shop_order WHERE order_no = #{outTradeNo}
</select> </select>
<select id="getOrderGoodsInfo" resultType="com.gxwebsoft.shop.vo.ShopOrderGoodsVO">
SELECT
a.order_no,
b.goods_id,
c.`name`,
b.total_num,
b.price,
c.first_money,
c.second_money,
c.first_dividend,
c.second_dividend,
c.commission_type
FROM
shop_order a
LEFT JOIN shop_order_goods b ON a.order_id = b.order_id
LEFT JOIN shop_goods c ON b.goods_id = c.goods_id
WHERE
a.deleted = 0
AND c.deleted = 0
AND c.is_open_commission = 1
AND a.order_no = #{orderNo}
</select>
<!-- 根据订单号修改订单 --> <!-- 根据订单号修改订单 -->
<update id="updateByOutTradeNo" parameterType="com.gxwebsoft.cms.entity.CmsWebsite"> <update id="updateByOutTradeNo" parameterType="com.gxwebsoft.cms.entity.CmsWebsite">

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gxwebsoft.shop.mapper.ShopSurchargeConfigMapper">
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*
FROM shop_surcharge_config a
<where>
<if test="param.id != null">
AND a.id = #{param.id}
</if>
<if test="param.name != null">
AND a.name LIKE CONCAT('%', #{param.name}, '%')
</if>
<if test="param.type != null">
AND a.type = #{param.type}
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.sortNumber != null">
AND a.sort_number = #{param.sortNumber}
</if>
<if test="param.creator != null">
AND a.creator LIKE CONCAT('%', #{param.creator}, '%')
</if>
<if test="param.createTimeStart != null">
AND a.create_time &gt;= #{param.createTimeStart}
</if>
<if test="param.createTimeEnd != null">
AND a.create_time &lt;= #{param.createTimeEnd}
</if>
<if test="param.updater != null">
AND a.updater LIKE CONCAT('%', #{param.updater}, '%')
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.keywords != null">
AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%')
)
</if>
</where>
</sql>
<!-- 分页查询 -->
<select id="selectPageRel" resultType="com.gxwebsoft.shop.entity.ShopSurchargeConfig">
<include refid="selectSql"></include>
</select>
<!-- 查询全部 -->
<select id="selectListRel" resultType="com.gxwebsoft.shop.entity.ShopSurchargeConfig">
<include refid="selectSql"></include>
</select>
</mapper>

View File

@@ -0,0 +1,56 @@
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 = "启用状态 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;
}

View File

@@ -0,0 +1,82 @@
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 = "创建者")
private String creator;
@Schema(description = "更新者")
private String updater;
@Schema(description = "是否删除")
@QueryField(type = QueryType.EQ)
private Integer deleted;
}

View File

@@ -1,15 +1,15 @@
package com.gxwebsoft.shop.param; 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.QueryField;
import com.gxwebsoft.common.core.annotation.QueryType; import com.gxwebsoft.common.core.annotation.QueryType;
import com.gxwebsoft.common.core.web.BaseParam; 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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
/** /**
* 订单查询参数 * 订单查询参数
* *

View File

@@ -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;
}

View File

@@ -1,13 +1,16 @@
package com.gxwebsoft.shop.service; package com.gxwebsoft.shop.service;
import cn.hutool.core.util.IdUtil; 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.core.exception.BusinessException;
import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.redis.OrderNoUtils;
import com.gxwebsoft.shop.config.OrderConfigProperties; import com.gxwebsoft.shop.config.OrderConfigProperties;
import com.gxwebsoft.shop.dto.OrderCreateRequest; import com.gxwebsoft.shop.dto.OrderCreateRequest;
import com.gxwebsoft.shop.entity.*; 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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -17,6 +20,7 @@ import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.security.SecureRandom;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -35,9 +39,16 @@ public class OrderBusinessService {
private static final int DEDUCT_STOCK_TYPE_ORDER = 10; // 下单减库存 private static final int DEDUCT_STOCK_TYPE_ORDER = 10; // 下单减库存
private static final int DEDUCT_STOCK_TYPE_PAY = 20; // 付款减库存 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 @Resource
private ShopOrderService shopOrderService; private ShopOrderService shopOrderService;
@Resource
private ShopOrderMapper shopOrderMapper;
@Resource @Resource
private ShopOrderGoodsService shopOrderGoodsService; private ShopOrderGoodsService shopOrderGoodsService;
@@ -52,11 +63,19 @@ public class OrderBusinessService {
@Resource @Resource
private ShopUserAddressService shopUserAddressService; private ShopUserAddressService shopUserAddressService;
@Resource @Resource
private ShopUserCouponService shopUserCouponService; private ShopUserCouponService shopUserCouponService;
@Resource @Resource
private ShopStoreFenceService shopStoreFenceService; private ShopStoreFenceService shopStoreFenceService;
@Resource
private ShopFlashSaleActivityMapper shopFlashSaleActivityMapper;
@Resource
private OrderNoUtils orderNoUtils;
/** /**
* 创建订单 * 创建订单
* *
@@ -67,41 +86,38 @@ public class OrderBusinessService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Map<String, String> createOrder(OrderCreateRequest request, User loginUser) { public Map<String, String> createOrder(OrderCreateRequest request, User loginUser) {
// 1. 参数校验 //1. 参数校验
validateOrderRequest(request, loginUser); validateOrderRequest(request, loginUser);
// 2. 构建订单对象 //2. 构建订单对象
ShopOrder shopOrder = buildShopOrder(request, loginUser); ShopOrder shopOrder = buildShopOrder(request, loginUser);
// 3. 处理收货地址信息 //3. 处理收货地址信息
processDeliveryAddress(shopOrder, request, loginUser); processDeliveryAddress(shopOrder, request, loginUser);
// 3.1 下单时校验配送范围(电子围栏) //4. 下单时校验配送范围(电子围栏)
validateDeliveryFenceIfNeeded(shopOrder, loginUser); validateDeliveryFenceIfNeeded(shopOrder, loginUser);
// 4. 应用业务规则 //5.如果商品仅有一个则更新formId
applyBusinessRules(shopOrder, loginUser);
// 如果商品仅有一个则更新formId
if (request.getGoodsItems().size() == 1) { if (request.getGoodsItems().size() == 1) {
shopOrder.setFormId(request.getGoodsItems().get(0).getGoodsId()); shopOrder.setFormId(request.getGoodsItems().get(0).getGoodsId());
} }
// 5. 保存订单 //6.保存订单
boolean saved = shopOrderService.save(shopOrder); boolean saved = shopOrderService.save(shopOrder);
if (!saved) { if (!saved) {
throw new BusinessException("订单保存失败"); throw new BusinessException("订单保存失败");
} }
// 6. 保存订单商品 //7.保存订单商品
saveOrderGoods(request, shopOrder); saveOrderGoods(request, shopOrder);
// 7. 标记优惠券为已使用 //8.标记优惠券为已使用
if (shopOrder.getCouponId() != null && shopOrder.getCouponId() > 0) { if (shopOrder.getCouponId() != null && shopOrder.getCouponId() > 0) {
markCouponAsUsed(shopOrder.getCouponId(), shopOrder.getOrderId()); markCouponAsUsed(shopOrder.getCouponId(), shopOrder.getOrderId());
} }
// 8. 创建微信支付订单 //9.创建微信支付订单
try { try {
return shopOrderService.createWxOrder(shopOrder); return shopOrderService.createWxOrder(shopOrder);
} catch (Exception e) { } catch (Exception e) {
@@ -167,6 +183,8 @@ public class OrderBusinessService {
BigDecimal total = BigDecimal.ZERO; BigDecimal total = BigDecimal.ZERO;
for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) { for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) {
Integer activityId = item.getActivityId();
// 验证商品ID // 验证商品ID
if (item.getGoodsId() == null) { if (item.getGoodsId() == null) {
throw new BusinessException("商品ID不能为空"); throw new BusinessException("商品ID不能为空");
@@ -216,6 +234,25 @@ public class OrderBusinessService {
productName = goods.getName() + "(" + (item.getSpecInfo() != null ? item.getSpecInfo() : sku.getSku()) + ")"; 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) { if (actualPrice == null || actualPrice.compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException("商品价格异常:" + productName); throw new BusinessException("商品价格异常:" + productName);
@@ -263,6 +300,10 @@ public class OrderBusinessService {
} }
shopOrder.setExpirationTime(shopOrder.getCreateTime().plusMinutes(10)); shopOrder.setExpirationTime(shopOrder.getCreateTime().plusMinutes(10));
//设置核销码及过期时间
shopOrder.setVerifyCode(getVerifyCode());
shopOrder.setVerifyExpTime(shopOrder.getCreateTime().plusMinutes(10));
// 确保租户ID正确设置关键字段影响微信支付证书路径 // 确保租户ID正确设置关键字段影响微信支付证书路径
shopOrder.setTenantId(loginUser.getTenantId()); shopOrder.setTenantId(loginUser.getTenantId());
@@ -279,13 +320,8 @@ public class OrderBusinessService {
log.debug("构建订单对象 - 租户ID{}用户ID{}", shopOrder.getTenantId(), shopOrder.getUserId()); log.debug("构建订单对象 - 租户ID{}用户ID{}", shopOrder.getTenantId(), shopOrder.getUserId());
// 生成订单号(如果请求中没有提供) // 生成订单号(如果请求中没有提供)
if (shopOrder.getOrderNo() == null || shopOrder.getOrderNo().trim().isEmpty()) { String orderNo = orderNoUtils.generate("L");
String generatedOrderNo = Long.toString(IdUtil.getSnowflakeNextId()); shopOrder.setOrderNo(orderNo);
shopOrder.setOrderNo(generatedOrderNo);
log.info("生成新订单号: {}", generatedOrderNo);
} else {
log.info("使用请求中的订单号: {}", shopOrder.getOrderNo());
}
// 设置默认备注 // 设置默认备注
if (shopOrder.getComments() == null) { if (shopOrder.getComments() == null) {
@@ -328,9 +364,76 @@ public class OrderBusinessService {
processCoupon(shopOrder, loginUser); processCoupon(shopOrder, loginUser);
} }
//订单类型、水票判断 TODO 目前没有购物车业务,每笔订单只下一个商品,后期加购物车多商品同时下单业务需调整
Integer orderType = request.getOrderType();
List<OrderCreateRequest.OrderGoodsItem> 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);
}
//单一配送方式商品如用户没选择配送方式按商品默认配送属性设定订单类型
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 = 1;
}
}
}
}
}
shopOrder.setOrderType(orderType);
return shopOrder; 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<ShopOrder> orderLambdaQueryWrapper = new LambdaQueryWrapper<ShopOrder>().select(ShopOrder::getOrderId).eq(ShopOrder::getVerifyCode, verifyCode);
List<ShopOrder> shopOrders = shopOrderMapper.selectList(orderLambdaQueryWrapper);
if(!CollectionUtils.isEmpty(shopOrders)){
getVerifyCode();
}
return verifyCode;
}
/** /**
* 处理优惠券 * 处理优惠券
*/ */
@@ -528,6 +631,12 @@ public class OrderBusinessService {
return; return;
} }
//自提订单类型不用校验电子围栏
Integer orderType = shopOrder.getOrderType();
if(orderType != null && orderType == 2){
return;
}
// 若已配置围栏,则必须有 addressId 才能从地址表取坐标进行校验 // 若已配置围栏,则必须有 addressId 才能从地址表取坐标进行校验
if (shopOrder.getAddressId() == null) { if (shopOrder.getAddressId() == null) {
if (shopStoreFenceService.hasEnabledFences(shopOrder.getTenantId())) { if (shopStoreFenceService.hasEnabledFences(shopOrder.getTenantId())) {
@@ -558,9 +667,18 @@ public class OrderBusinessService {
throw new BusinessException("收货地址坐标异常,请重新选择收货地址"); throw new BusinessException("收货地址坐标异常,请重新选择收货地址");
} }
// 只做校验;具体围栏列表/points 异常处理由围栏服务统一处理 //订单类型5-配送【系统自动识别电子围栏内转及时配送,电子围栏外发快递】
if(orderType != null && orderType == 5){
Boolean exit = shopStoreFenceService.validatePointInEnabled(shopOrder.getTenantId(), lng, lat);
if(exit){
shopOrder.setOrderType(1);
}else {
shopOrder.setOrderType(4);
}
}else {
shopStoreFenceService.validatePointInEnabledFences(shopOrder.getTenantId(), lng, lat); shopStoreFenceService.validatePointInEnabledFences(shopOrder.getTenantId(), lng, lat);
} }
}
/** /**
* 应用业务规则 * 应用业务规则
@@ -651,6 +769,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()) { if (actualStock == null || actualStock < item.getQuantity()) {
String stockMsg = sku != null ? "商品规格库存不足" : "商品库存不足"; String stockMsg = sku != null ? "商品规格库存不足" : "商品库存不足";

View File

@@ -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<ShopActiveImage> {
/**
* 分页关联查询
*
* @param param 查询参数
* @return PageResult<ShopActiveImage>
*/
PageResult<ShopActiveImage> pageRel(ShopActiveImageParam param);
/**
* 关联查询全部
*
* @param param 查询参数
* @return List<ShopActiveImage>
*/
List<ShopActiveImage> listRel(ShopActiveImageParam param);
/**
* 根据id查询
*
* @param id 主键ID
* @return ShopActiveImage
*/
ShopActiveImage getByIdRel(Integer id);
}

View File

@@ -17,5 +17,7 @@ public interface ShopDealerCommissionRollbackService {
* @return true=执行成功或无可回退数据false=执行失败 * @return true=执行成功或无可回退数据false=执行失败
*/ */
boolean rollbackOnOrderRefund(ShopOrder order, BigDecimal refundAmount); boolean rollbackOnOrderRefund(ShopOrder order, BigDecimal refundAmount);
boolean rollbackOnOrderRefundV2(ShopOrder order, BigDecimal refundAmount);
} }

View File

@@ -23,6 +23,13 @@ public interface ShopDealerRefereeService extends IService<ShopDealerReferee> {
*/ */
PageResult<ShopDealerReferee> pageRel(ShopDealerRefereeParam param); PageResult<ShopDealerReferee> pageRel(ShopDealerRefereeParam param);
/**
* app端查询用户层级关系
* @param param
* @return
*/
PageResult<ShopDealerReferee> appPage(ShopDealerRefereeParam param);
/** /**
* 关联查询全部 * 关联查询全部
* *

View File

@@ -2,6 +2,8 @@ package com.gxwebsoft.shop.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.shop.dto.ShopDealerRefundDto;
import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
import com.gxwebsoft.shop.entity.ShopDealerUser; import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.param.ShopDealerUserParam; import com.gxwebsoft.shop.param.ShopDealerUserParam;
@@ -42,4 +44,18 @@ public interface ShopDealerUserService extends IService<ShopDealerUser> {
ShopDealerUser getByUserIdRel(Integer userId); ShopDealerUser getByUserIdRel(Integer userId);
boolean updateByUserId(ShopDealerUser shopDealerUser); boolean updateByUserId(ShopDealerUser shopDealerUser);
/**
* 更新分销用户资金
* @param reduceDto
* @return
*/
Boolean reduceBalance(ShopDealerUserReduceDto reduceDto);
/**
* 分销订单退款
* @param refundDto
* @return
*/
Boolean refundOrder(ShopDealerRefundDto refundDto);
} }

View File

@@ -0,0 +1,65 @@
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<ShopFlashSaleActivity> {
/**
* 分页关联查询
*
* @param param 查询参数
* @return PageResult<ShopFlashSaleActivity>
*/
PageResult<ShopFlashSaleActivityVO> pageRel(ShopFlashSaleActivityParam param);
/**
* 关联查询全部
*
* @param param 查询参数
* @return List<ShopFlashSaleActivity>
*/
List<ShopFlashSaleActivity> listRel(ShopFlashSaleActivityParam param);
/**
* 根据id查询
*
* @param id 秒杀活动编号
* @return ShopFlashSaleActivity
*/
ShopFlashSaleActivity getByIdRel(Integer id);
/**
* 查询个人可参与的活动数据
* @param tenantId 租户ID
* @return
*/
List<ShopFlashSaleActivityVO> getMyActive(Integer tenantId);
/**
* 修改秒杀活动状态
* @param id
* @return
*/
Boolean updateStatus(Integer id);
/**
* 修改秒杀活动排序
* @param id
* @param sortNumber
* @return
*/
Boolean updateSortNumber(Integer id, Integer sortNumber);
}

View File

@@ -3,6 +3,7 @@ package com.gxwebsoft.shop.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.shop.dto.UserOrderStats; import com.gxwebsoft.shop.dto.UserOrderStats;
import com.gxwebsoft.shop.dto.VerifyShopOrderDto;
import com.gxwebsoft.shop.entity.ShopOrder; import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.param.ShopOrderParam; import com.gxwebsoft.shop.param.ShopOrderParam;
@@ -86,4 +87,11 @@ public interface ShopOrderService extends IService<ShopOrder> {
* @param type 订单类型(可为空) * @param type 订单类型(可为空)
*/ */
UserOrderStats getUserOrderStats(Integer userId, Integer tenantId, Integer type); UserOrderStats getUserOrderStats(Integer userId, Integer tenantId, Integer type);
/**
* 核销订单
* @param verifyDto 核销码
* @return
*/
Boolean verifyOrder(VerifyShopOrderDto verifyDto);
} }

View File

@@ -59,4 +59,19 @@ public interface ShopStoreFenceService extends IService<ShopStoreFence> {
*/ */
void validatePointInEnabledFences(Integer tenantId, double lng, double lat); void validatePointInEnabledFences(Integer tenantId, double lng, double lat);
/**
* 校验坐标是否落在任一启用围栏内。
* <p>
* 约定:
* - 围栏按 tenantId + status=0 过滤;
* - 支持多个围栏:命中任意一个即通过;
* - 无围栏配置:直接放行;
* - 围栏 points 异常:抛出异常(避免误送)。
*
* @param tenantId 租户ID
* @param lng 经度
* @param lat 纬度
*/
Boolean validatePointInEnabled(Integer tenantId, double lng, double lat);
} }

View File

@@ -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<ShopSurchargeConfig> {
/**
* 分页关联查询
*
* @param param 查询参数
* @return PageResult<ShopSurchargeConfig>
*/
PageResult<ShopSurchargeConfig> pageRel(ShopSurchargeConfigParam param);
/**
* 关联查询全部
*
* @param param 查询参数
* @return List<ShopSurchargeConfig>
*/
List<ShopSurchargeConfig> 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);
}

View File

@@ -55,4 +55,11 @@ public interface ShopUserAddressService extends IService<ShopUserAddress> {
*/ */
List<ShopUserAddress> getUserAddresses(Integer userId); List<ShopUserAddress> getUserAddresses(Integer userId);
/**
* 修改用户信息
* @param shopUserAddress
* @return
*/
Boolean updateInfo(ShopUserAddress shopUserAddress);
} }

View File

@@ -0,0 +1,70 @@
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.mapper.ShopActiveImageMapper;
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;
/**
* 推广码底图Service实现
*
* @author xm
* @since 2026-04-27 17:53:00
*/
@Service
@AllArgsConstructor
public class ShopActiveImageServiceImpl extends ServiceImpl<ShopActiveImageMapper, ShopActiveImage> implements ShopActiveImageService {
@Override
public PageResult<ShopActiveImage> pageRel(ShopActiveImageParam param) {
PageParam<ShopActiveImage, ShopActiveImageParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
List<ShopActiveImage> list = baseMapper.selectPageRel(page, param);
if(CollectionUtils.isNotEmpty(list)){
list.forEach(shopActiveImage -> {
if(!StringUtils.isEmpty(shopActiveImage.getImgUrl())){
shopActiveImage.setImgUrlList(Arrays.asList(shopActiveImage.getImgUrl().split(",")));
}
});
}
return new PageResult<>(list, page.getTotal());
}
@Override
public List<ShopActiveImage> listRel(ShopActiveImageParam param) {
List<ShopActiveImage> list = baseMapper.selectListRel(param);
if(CollectionUtils.isNotEmpty(list)){
list.forEach(shopActiveImage -> {
if(!StringUtils.isEmpty(shopActiveImage.getImgUrl())){
shopActiveImage.setImgUrlList(Arrays.asList(shopActiveImage.getImgUrl().split(",")));
}
});
}
// 排序
PageParam<ShopActiveImage, ShopActiveImageParam> page = new PageParam<>();
page.setDefaultOrder("sort_number asc, create_time desc");
return page.sortRecords(list);
}
@Override
public ShopActiveImage getByIdRel(Integer id) {
ShopActiveImageParam param = new ShopActiveImageParam();
param.setId(id);
ShopActiveImage activeImage = param.getOne(baseMapper.selectListRel(param));
if(activeImage != null && !StringUtils.isEmpty(activeImage.getImgUrl())){
activeImage.setImgUrlList(Arrays.asList(activeImage.getImgUrl().split(",")));
}
return activeImage;
}
}

View File

@@ -1,8 +1,10 @@
package com.gxwebsoft.shop.service.impl; package com.gxwebsoft.shop.service.impl;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.gxwebsoft.shop.dto.ShopDealerRefundDto;
import com.gxwebsoft.shop.entity.ShopDealerCapital; import com.gxwebsoft.shop.entity.ShopDealerCapital;
import com.gxwebsoft.shop.entity.ShopDealerOrder; import com.gxwebsoft.shop.entity.ShopDealerOrder;
import com.gxwebsoft.shop.entity.ShopDealerUser; import com.gxwebsoft.shop.entity.ShopDealerUser;
@@ -137,6 +139,59 @@ public class ShopDealerCommissionRollbackServiceImpl implements ShopDealerCommis
return true; return true;
} }
@Override
@Transactional(rollbackFor = Exception.class)
public boolean rollbackOnOrderRefundV2(ShopOrder order, BigDecimal refundAmount) {
if (order == null || order.getTenantId() == null || StrUtil.isEmpty(order.getOrderNo())) {
return true;
}
Integer tenantId = order.getTenantId();
String orderNo = order.getOrderNo();
//判断退款金额不能大于实付金额
if(refundAmount.compareTo(order.getPayPrice()) > 0){
throw new RuntimeException("退款金额不能大于订单实付金额!");
}
//执行分销订单退款业务
ShopDealerRefundDto refundDto = new ShopDealerRefundDto();
refundDto.setOrderNo(orderNo);
refundDto.setTenantId(tenantId);
refundDto.setRefundAmount(refundAmount);
Boolean refundResult = shopDealerUserService.refundOrder(refundDto);
if(refundResult){
//退款金额与实付金额一致,标记分销订单失效
if (order.getPayPrice().compareTo(refundAmount) == 0) {
markDealerOrderInvalid(tenantId, orderNo);
}else {
BigDecimal rate = refundAmount.divide(order.getPayPrice(), 3, RoundingMode.HALF_UP);
BigDecimal remainRate = BigDecimal.ONE.subtract(rate);
LambdaQueryWrapper<ShopDealerOrder> orderLambdaQueryWrapper = new LambdaQueryWrapper<ShopDealerOrder>().eq(ShopDealerOrder::getOrderNo, orderNo);
ShopDealerOrder shopDealerOrder = shopDealerOrderService.getOne(orderLambdaQueryWrapper);
if(shopDealerOrder != null){
if(shopDealerOrder.getFirstMoney().compareTo(BigDecimal.ZERO) > 0){
shopDealerOrder.setFirstMoney(shopDealerOrder.getFirstMoney().multiply(remainRate));
}
if(shopDealerOrder.getSecondMoney().compareTo(BigDecimal.ZERO) > 0){
shopDealerOrder.setSecondMoney(shopDealerOrder.getSecondMoney().multiply(remainRate));
}
if(shopDealerOrder.getFirstDividend().compareTo(BigDecimal.ZERO) > 0){
shopDealerOrder.setFirstDividend(shopDealerOrder.getFirstDividend().multiply(remainRate));
}
if(shopDealerOrder.getSecondDividend().compareTo(BigDecimal.ZERO) > 0){
shopDealerOrder.setSecondDividend(shopDealerOrder.getSecondDividend().multiply(remainRate));
}
shopDealerOrder.setUpdateTime(LocalDateTime.now());
shopDealerOrderService.updateById(shopDealerOrder);
}
}
return Boolean.TRUE;
}else {
return Boolean.FALSE;
}
}
private boolean hasUnfreezeMarker(Integer tenantId, ShopDealerCapital cap) { private boolean hasUnfreezeMarker(Integer tenantId, ShopDealerCapital cap) {
if (tenantId == null || cap == null || cap.getId() == null || cap.getUserId() == null || cap.getOrderNo() == null) { if (tenantId == null || cap == null || cap.getId() == null || cap.getUserId() == null || cap.getOrderNo() == null) {
return false; return false;
@@ -166,7 +221,7 @@ public class ShopDealerCommissionRollbackServiceImpl implements ShopDealerCommis
if (refundAmount.compareTo(orderBaseAmount) >= 0) { if (refundAmount.compareTo(orderBaseAmount) >= 0) {
return BigDecimal.ONE; return BigDecimal.ONE;
} }
return refundAmount.divide(orderBaseAmount, 10, RoundingMode.HALF_UP); return refundAmount.divide(orderBaseAmount, 3, RoundingMode.HALF_UP);
} }
private void markDealerOrderInvalid(Integer tenantId, String orderNo) { private void markDealerOrderInvalid(Integer tenantId, String orderNo) {

View File

@@ -3,22 +3,30 @@ package com.gxwebsoft.shop.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.exception.BusinessException; import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.mapper.ShopDealerRefereeMapper; import com.gxwebsoft.shop.mapper.ShopDealerRefereeMapper;
import com.gxwebsoft.shop.entity.ShopDealerUser; import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.mapper.ShopOrderMapper;
import com.gxwebsoft.shop.service.ShopDealerUserService; import com.gxwebsoft.shop.service.ShopDealerUserService;
import com.gxwebsoft.shop.service.ShopDealerRefereeService; import com.gxwebsoft.shop.service.ShopDealerRefereeService;
import com.gxwebsoft.shop.entity.ShopDealerReferee; import com.gxwebsoft.shop.entity.ShopDealerReferee;
import com.gxwebsoft.shop.param.ShopDealerRefereeParam; import com.gxwebsoft.shop.param.ShopDealerRefereeParam;
import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.PageResult;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/** /**
* 分销商推荐关系表Service实现 * 分销商推荐关系表Service实现
@@ -32,6 +40,9 @@ public class ShopDealerRefereeServiceImpl extends ServiceImpl<ShopDealerRefereeM
@Resource @Resource
private ShopDealerUserService shopDealerUserService; private ShopDealerUserService shopDealerUserService;
@Resource
private ShopOrderMapper shopOrderMapper;
@Override @Override
public PageResult<ShopDealerReferee> pageRel(ShopDealerRefereeParam param) { public PageResult<ShopDealerReferee> pageRel(ShopDealerRefereeParam param) {
PageParam<ShopDealerReferee, ShopDealerRefereeParam> page = new PageParam<>(param); PageParam<ShopDealerReferee, ShopDealerRefereeParam> page = new PageParam<>(param);
@@ -40,6 +51,57 @@ public class ShopDealerRefereeServiceImpl extends ServiceImpl<ShopDealerRefereeM
return new PageResult<>(list, page.getTotal()); return new PageResult<>(list, page.getTotal());
} }
@Override
public PageResult<ShopDealerReferee> appPage(ShopDealerRefereeParam param) {
PageParam<ShopDealerReferee, ShopDealerRefereeParam> page = new PageParam<>(param);
page.setDefaultOrder("create_time desc");
List<ShopDealerReferee> list = baseMapper.selectPageRel(page, param);
if(CollectionUtils.isNotEmpty(list)){
List<Integer> userIdList = list.stream().map(ShopDealerReferee::getUserId).distinct().collect(Collectors.toList());
Map<Integer, Long> refereeMap = lambdaQuery().select(ShopDealerReferee::getDealerId, ShopDealerReferee::getUserId).in(ShopDealerReferee::getDealerId, userIdList).list()
.stream().collect(Collectors.groupingBy(ShopDealerReferee::getDealerId, Collectors.counting()));
LambdaQueryWrapper<ShopOrder> orderLambdaQueryWrapper = new LambdaQueryWrapper<ShopOrder>().select(ShopOrder::getOrderId, ShopOrder::getUserId, ShopOrder::getMoney).in(ShopOrder::getUserId, userIdList)
.in(ShopOrder::getOrderStatus, Arrays.asList(0, 1, 3, 4, 5, 7));
List<ShopOrder> shopOrderList = shopOrderMapper.selectList(orderLambdaQueryWrapper);
Map<Integer, Long> userOrderCountMap = new HashMap<>();
Map<Integer, BigDecimal> userAmountMap = new HashMap<>();
if(CollectionUtils.isNotEmpty(shopOrderList)){
userOrderCountMap = shopOrderList.stream().collect(Collectors.groupingBy(ShopOrder::getUserId, Collectors.counting()));
userAmountMap = shopOrderList.stream().collect(Collectors.groupingBy(ShopOrder::getUserId, Collectors.reducing(BigDecimal.ZERO, ShopOrder::getMoney, BigDecimal::add)));
}
Map<Integer, Long> finalUserOrderCountMap = userOrderCountMap;
Map<Integer, BigDecimal> finalUserAmountMap = userAmountMap;
list.forEach(shopDealerReferee -> {
Long teamNum = refereeMap.get(shopDealerReferee.getUserId());
if(teamNum != null){
shopDealerReferee.setTeamNum(teamNum.intValue());
}else {
shopDealerReferee.setTeamNum(0);
}
Long orderNum = finalUserOrderCountMap.get(shopDealerReferee.getUserId());
if(orderNum != null){
shopDealerReferee.setOrderNum(orderNum.intValue());
}else {
shopDealerReferee.setOrderNum(0);
}
BigDecimal orderAmount = finalUserAmountMap.get(shopDealerReferee.getUserId());
if(orderAmount != null){
shopDealerReferee.setOrderAmount(orderAmount);
}else {
shopDealerReferee.setOrderAmount(BigDecimal.ZERO);
}
});
}
return new PageResult<>(list, page.getTotal());
}
@Override @Override
public List<ShopDealerReferee> listRel(ShopDealerRefereeParam param) { public List<ShopDealerReferee> listRel(ShopDealerRefereeParam param) {
List<ShopDealerReferee> list = baseMapper.selectListRel(param); List<ShopDealerReferee> list = baseMapper.selectListRel(param);

View File

@@ -2,15 +2,38 @@ package com.gxwebsoft.shop.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.shop.mapper.ShopDealerUserMapper; import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum;
import com.gxwebsoft.shop.service.ShopDealerUserService; import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum;
import com.gxwebsoft.shop.entity.ShopDealerUser; import com.gxwebsoft.common.core.utils.LoginUserUtil;
import com.gxwebsoft.shop.param.ShopDealerUserParam;
import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.mapper.UserMapper;
import com.gxwebsoft.common.system.redis.OrderNoUtils;
import com.gxwebsoft.shop.dto.ShopDealerRefundDto;
import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
import com.gxwebsoft.shop.entity.ShopDealerCapital;
import com.gxwebsoft.shop.entity.ShopDealerUser;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.mapper.ShopDealerCapitalMapper;
import com.gxwebsoft.shop.mapper.ShopDealerUserMapper;
import com.gxwebsoft.shop.mapper.ShopOrderMapper;
import com.gxwebsoft.shop.param.ShopDealerUserParam;
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
import com.gxwebsoft.shop.service.ShopDealerUserService;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/** /**
* 分销商用户记录表Service实现 * 分销商用户记录表Service实现
@@ -19,8 +42,21 @@ import java.util.List;
* @since 2025-08-11 23:51:41 * @since 2025-08-11 23:51:41
*/ */
@Service @Service
@AllArgsConstructor
public class ShopDealerUserServiceImpl extends ServiceImpl<ShopDealerUserMapper, ShopDealerUser> implements ShopDealerUserService { public class ShopDealerUserServiceImpl extends ServiceImpl<ShopDealerUserMapper, ShopDealerUser> implements ShopDealerUserService {
private UserMapper userMapper;
private OrderNoUtils orderNoUtils;
private ShopDealerCapitalMapper shopDealerCapitalMapper;
private ShopDealerCapitalService shopDealerCapitalService;
private ShopOrderMapper shopOrderMapper;
private static final int TENANT_ID = 10584;
@Override @Override
public PageResult<ShopDealerUser> pageRel(ShopDealerUserParam param) { public PageResult<ShopDealerUser> pageRel(ShopDealerUserParam param) {
PageParam<ShopDealerUser, ShopDealerUserParam> page = new PageParam<>(param); PageParam<ShopDealerUser, ShopDealerUserParam> page = new PageParam<>(param);
@@ -58,4 +94,303 @@ public class ShopDealerUserServiceImpl extends ServiceImpl<ShopDealerUserMapper,
return update > 0; return update > 0;
} }
@Override
@Transactional
public Boolean reduceBalance(ShopDealerUserReduceDto entity) {
//1.获取入参
log.error("分销账户资金变更明细:" + entity);
ShopDealerTypeEnum typeEnum = entity.getTypeEnum();
ShopDealerCapitalUpdateEnum updateEnum = entity.getUpdateEnum();
//2.变动类型为 1-操作冻结账户余额 2-操作提现账户余额【直接结算】是需判断金额值问题
if(ShopDealerTypeEnum.FREEZE_ACCOUNT.equals(typeEnum) || ShopDealerTypeEnum.WITHDRAW_ACCOUNT.equals(typeEnum)){
//查询分销用户信息
ShopDealerUser dealerUser = getDealerUser(entity.getUserId());
BigDecimal price = entity.getPrice();
switch (updateEnum){
case DISTRIBUTION_INCOME:
case MANAGEMENT_INCOME:
case DIVIDEND_INCOME:
case PROMOTION_INCOME:
case TRANSFER_INCOME:
case DELIVERY_INCOME:{
break;
}
case WITHDRAW_PAYMENT:
case TRANSFER_PAYMENT:
case ORDER_REFUND:{
price.negate();
break;
}
default:{
throw new RuntimeException("暂不支持此类型!");
}
}
String no = orderNoUtils.generate("C");
//3.操作冻结账户余额
if(ShopDealerTypeEnum.FREEZE_ACCOUNT.equals(typeEnum)){ //操作冻结账户余额
//3.1 账户变更冻结金额
dealerUser.setFreezeMoney(dealerUser.getFreezeMoney().add(price));
dealerUser.setTotalMoney(dealerUser.getTotalMoney().add(price));
baseMapper.updateById(dealerUser);
//3.2 生成流水
ShopDealerCapital dealerCapital = new ShopDealerCapital();
dealerCapital.setNo(no);
dealerCapital.setUserId(entity.getUserId());
dealerCapital.setType(typeEnum.getCode());
dealerCapital.setOrderNo(entity.getOrderNo());
dealerCapital.setFlowType(updateEnum.getType());
dealerCapital.setFreezeMoney(price);
dealerCapital.setFreezeMoneyAfter(dealerUser.getFreezeMoney());
dealerCapital.setMoneyAfter(dealerUser.getMoney());
dealerCapital.setComments(updateEnum.getDescription());
dealerCapital.setToUserId(entity.getOrderUserId());
dealerCapital.setTenantId(TENANT_ID);
dealerCapital.setCreateTime(LocalDateTime.now());
User loginUser = LoginUserUtil.getLoginUser();
if(loginUser != null){
dealerCapital.setCreator(loginUser.getUserId());
}
shopDealerCapitalMapper.insert(dealerCapital);
}
//4.操作提现账户余额【直接结算】
else if(ShopDealerTypeEnum.WITHDRAW_ACCOUNT.equals(typeEnum)){
//4.1 账户变更冻结金额
dealerUser.setMoney(dealerUser.getMoney().add(price));
dealerUser.setTotalMoney(dealerUser.getTotalMoney().add(price));
baseMapper.updateById(dealerUser);
//4.2 生成流水
ShopDealerCapital dealerCapital = new ShopDealerCapital();
dealerCapital.setNo(no);
dealerCapital.setUserId(entity.getUserId());
dealerCapital.setType(typeEnum.getCode());
dealerCapital.setOrderNo(entity.getOrderNo());
dealerCapital.setFlowType(updateEnum.getType());
dealerCapital.setMoney(price);
dealerCapital.setMoneyAfter(dealerUser.getMoney());
dealerCapital.setFreezeMoneyAfter(dealerUser.getFreezeMoney());
dealerCapital.setComments(updateEnum.getDescription());
dealerCapital.setToUserId(entity.getOrderUserId());
dealerCapital.setTenantId(TENANT_ID);
dealerCapital.setCreateTime(LocalDateTime.now());
User loginUser = LoginUserUtil.getLoginUser();
if(loginUser != null){
dealerCapital.setCreator(loginUser.getUserId());
}
shopDealerCapitalMapper.insert(dealerCapital);
}
}
//5.操作解冻业务【按订单号结算可能一个订单号存在2笔待解冻金额】
else if(ShopDealerTypeEnum.DEFROST.equals(typeEnum)){
//5.1 判断是否为指定的解冻业务
if(!ShopDealerCapitalUpdateEnum.FREEZE_MONEY_THAW.equals(entity.getUpdateEnum())){
throw new RuntimeException("暂不支持此类型!");
}
//5.2 查询订单结算流水
LambdaQueryWrapper<ShopDealerCapital> capitalLambdaQueryWrapper = new LambdaQueryWrapper<ShopDealerCapital>().eq(ShopDealerCapital::getOrderNo, entity.getOrderNo());
List<ShopDealerCapital> dealerCapitalList = shopDealerCapitalMapper.selectList(capitalLambdaQueryWrapper);
//5.3 根据用户遍历查询及执行解冻业务
if(CollectionUtils.isNotEmpty(dealerCapitalList)){
List<Integer> userIdList = dealerCapitalList.stream().map(ShopDealerCapital::getUserId).distinct().collect(Collectors.toList());
List<ShopDealerUser> shopDealerUserList = lambdaQuery().in(ShopDealerUser::getUserId, userIdList).list();
Map<Integer, List<ShopDealerCapital>> userCapitalMap = dealerCapitalList.stream().collect(Collectors.groupingBy(ShopDealerCapital::getUserId));
userCapitalMap.forEach((k, value) ->{
//5.4 查询是否存在待解冻订单数据
ShopDealerCapital userCapital = value.stream().filter(capital -> ShopDealerTypeEnum.FREEZE_ACCOUNT.getCode().equals(capital.getType())).findFirst().orElse(null);
if(userCapital != null){
//5.5 查询此订单是否已结算
ShopDealerCapital alreadyCapital = value.stream().filter(capital -> ShopDealerTypeEnum.DEFROST.getCode().equals(capital.getType())).findFirst().orElse(null);
if(alreadyCapital == null){
ShopDealerUser dealerUser = shopDealerUserList.stream().filter(shopDealerUser -> k.equals(shopDealerUser.getUserId())).findFirst().orElse(null);
//5.6 判断分销账户分销金额是否大于解冻金额
BigDecimal freezeMoney = userCapital.getFreezeMoney();
if(dealerUser != null && dealerUser.getFreezeMoney().compareTo(freezeMoney) >= 0){
//5.7 账户解冻金额、可提现增加金额
dealerUser.setFreezeMoney(dealerUser.getFreezeMoney().subtract(freezeMoney));
dealerUser.setMoney(dealerUser.getMoney().add(freezeMoney));
baseMapper.updateById(dealerUser);
//5.8 生成流水
ShopDealerCapital dealerCapital = new ShopDealerCapital();
String no = orderNoUtils.generate("C");
dealerCapital.setNo(no);
dealerCapital.setUserId(k);
dealerCapital.setType(typeEnum.getCode());
dealerCapital.setOrderNo(entity.getOrderNo());
dealerCapital.setFlowType(updateEnum.getType());
dealerCapital.setFreezeMoney(freezeMoney.negate());
dealerCapital.setFreezeMoneyAfter(dealerUser.getFreezeMoney());
dealerCapital.setMoney(freezeMoney);
dealerCapital.setMoneyAfter(dealerUser.getMoney());
dealerCapital.setComments(updateEnum.getDescription());
dealerCapital.setToUserId(entity.getOrderUserId());
dealerCapital.setTenantId(TENANT_ID);
dealerCapital.setCreateTime(LocalDateTime.now());
User loginUser = LoginUserUtil.getLoginUser();
if (loginUser != null) {
dealerCapital.setCreator(loginUser.getUserId());
}
shopDealerCapitalMapper.insert(dealerCapital);
}
}
}
});
}else {
log.error("未查询到待解冻订单信息:" + entity.getOrderNo());
}
}else {
throw new RuntimeException("暂不支持此变动类型业务!");
}
return Boolean.TRUE;
}
@Override
@Transactional
public Boolean refundOrder(ShopDealerRefundDto entity) {
//1.查询订单分销数据
List<ShopDealerCapital> dealerCapitalList = shopDealerCapitalMapper.selectList(
new LambdaQueryWrapper<ShopDealerCapital>()
.eq(ShopDealerCapital::getTenantId, entity.getTenantId())
.eq(ShopDealerCapital::getOrderNo, entity.getOrderNo())
.in(ShopDealerCapital::getFlowType, Arrays.asList(ShopDealerCapitalUpdateEnum.DISTRIBUTION_INCOME.getType(),
ShopDealerCapitalUpdateEnum.MANAGEMENT_INCOME.getType(), ShopDealerCapitalUpdateEnum.DIVIDEND_INCOME.getType(),
ShopDealerCapitalUpdateEnum.PROMOTION_INCOME.getType(), ShopDealerCapitalUpdateEnum.FREEZE_MONEY_THAW.getType(),
ShopDealerCapitalUpdateEnum.DELIVERY_INCOME.getType()))
.isNotNull(ShopDealerCapital::getUserId)
);
if(CollectionUtils.isNotEmpty(dealerCapitalList)){
//2.查询订单信息
ShopOrder order = shopOrderMapper.selectOne(new LambdaQueryWrapper<ShopOrder>().select(ShopOrder::getOrderId, ShopOrder::getPayPrice, ShopOrder::getUserId)
.eq(ShopOrder::getOrderNo, entity.getOrderNo()));
//3.订单不存在则回退
if(order == null){
throw new RuntimeException("退款查询订单信息失败!");
}
//4.订单实付金额为0则不执行退款业务
if(order.getPayPrice().compareTo(BigDecimal.ZERO) <= 0){
return Boolean.TRUE;
}
//5.退款金额不能大于实付金额
if(entity.getRefundAmount().compareTo(order.getPayPrice()) > 0){
throw new RuntimeException("退款金额不能大于实付金额!");
}
//6.查询分销用户信息
List<ShopDealerUser> shopDealerUserList = new ArrayList<>();
List<Integer> userIdList = dealerCapitalList.stream().map(ShopDealerCapital::getUserId).distinct().collect(Collectors.toList());
if(CollectionUtils.isNotEmpty(userIdList)){
shopDealerUserList = lambdaQuery().in(ShopDealerUser::getUserId, userIdList).list();
}
//7.创建批量更新用户钱包、批量信息分销明细集合对象
List<ShopDealerUser> shopDealerUserUpdateList = new ArrayList<>();
List<ShopDealerCapital> newCapitalList = new ArrayList<>();
//8.以用户分组,对结算订单进行业务操作
Map<Integer, List<ShopDealerCapital>> userDealerMap = dealerCapitalList.stream().collect(Collectors.groupingBy(ShopDealerCapital::getUserId));
//9.计算退款比率【退款金额/实付金额】
BigDecimal rate = entity.getRefundAmount().divide(order.getPayPrice(), 3, RoundingMode.HALF_UP);;
//10.遍历用户分销对象数据执行业务
List<ShopDealerUser> finalShopDealerUserList = shopDealerUserList;
BigDecimal finalRate = rate;
userDealerMap.forEach((k, value) ->{
BigDecimal money = value.stream().map(ShopDealerCapital::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add).multiply(finalRate);
BigDecimal freezeMoney = value.stream().map(ShopDealerCapital::getFreezeMoney).reduce(BigDecimal.ZERO, BigDecimal::add).multiply(finalRate);
ShopDealerUser dealerUser = finalShopDealerUserList.stream().filter(shopDealerUser -> k.equals(shopDealerUser.getUserId())).findFirst().orElse(null);
if(dealerUser != null){
//11.修改分销用户账户信息
dealerUser.setMoney(dealerUser.getMoney().subtract(money));
dealerUser.setFreezeMoney(dealerUser.getFreezeMoney().subtract(freezeMoney));
dealerUser.setTotalMoney(dealerUser.getTotalMoney().subtract(money.add(freezeMoney)));
dealerUser.setUpdateTime(LocalDateTime.now());
shopDealerUserUpdateList.add(dealerUser);
//12.新增退单分销记录
ShopDealerCapital capital = new ShopDealerCapital();
String no = orderNoUtils.generate("R");
capital.setNo(no);
capital.setUserId(k);
capital.setType(ShopDealerTypeEnum.ORDER_REFUND.getCode());
capital.setOrderNo(entity.getOrderNo());
capital.setFlowType(ShopDealerCapitalUpdateEnum.ORDER_REFUND.getType());
capital.setMoney(money.negate());
capital.setMoneyAfter(dealerUser.getMoney());
capital.setFreezeMoney(freezeMoney.negate());
capital.setFreezeMoneyAfter(dealerUser.getFreezeMoney());
capital.setComments(ShopDealerCapitalUpdateEnum.ORDER_REFUND.getDescription());
capital.setToUserId(order.getUserId());
capital.setTenantId(entity.getTenantId());
User loginUser = LoginUserUtil.getLoginUser();
if(loginUser != null){
capital.setCreator(loginUser.getUserId());
}
capital.setCreateTime(LocalDateTime.now());
newCapitalList.add(capital);
}
});
//13.批量修改分销用户钱包余额
if(CollectionUtils.isNotEmpty(shopDealerUserUpdateList)){
updateBatchById(shopDealerUserUpdateList);
}
//14.批量生成分销记录数据
if(CollectionUtils.isNotEmpty(newCapitalList)){
shopDealerCapitalService.saveBatch(newCapitalList);
}
return Boolean.TRUE;
}else {
return Boolean.TRUE;
}
}
public ShopDealerUser getDealerUser(Integer userId){
ShopDealerUser shopDealerUser = baseMapper.selectOne(new LambdaQueryWrapper<ShopDealerUser>().eq(ShopDealerUser::getUserId, userId));
if(shopDealerUser == null){
User user = userMapper.getById(userId);
if(user == null){
throw new RuntimeException("用户信息不存在!");
}
ShopDealerUser dealerUser = new ShopDealerUser();
dealerUser.setType(0);
dealerUser.setUserId(userId);
dealerUser.setAvatar(user.getAvatar());
dealerUser.setRealName(user.getRealName());
dealerUser.setMobile(user.getMobile());
dealerUser.setMoney(BigDecimal.ZERO);
dealerUser.setFreezeMoney(BigDecimal.ZERO);
dealerUser.setTotalMoney(BigDecimal.ZERO);
dealerUser.setTenantId(TENANT_ID);
dealerUser.setCreateTime(LocalDateTime.now());
dealerUser.setDealerLevel(0);
baseMapper.insert(dealerUser);
shopDealerUser = dealerUser;
}
return shopDealerUser;
}
} }

View File

@@ -0,0 +1,183 @@
package com.gxwebsoft.shop.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
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.shop.entity.ShopFlashSaleActivity;
import com.gxwebsoft.shop.entity.ShopGoods;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.mapper.ShopFlashSaleActivityMapper;
import com.gxwebsoft.shop.mapper.ShopGoodsMapper;
import com.gxwebsoft.shop.mapper.ShopOrderMapper;
import com.gxwebsoft.shop.param.ShopFlashSaleActivityParam;
import com.gxwebsoft.shop.service.ShopFlashSaleActivityService;
import com.gxwebsoft.shop.vo.ShopFlashSaleActivityVO;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* 秒杀活动Service实现
*
* @author xm
* @since 2026-04-22 17:18:17
*/
@Service
@AllArgsConstructor
public class ShopFlashSaleActivityServiceImpl extends ServiceImpl<ShopFlashSaleActivityMapper, ShopFlashSaleActivity> implements ShopFlashSaleActivityService {
private ShopGoodsMapper shopGoodsMapper;
private ShopOrderMapper shopOrderMapper;
@Override
public PageResult<ShopFlashSaleActivityVO> pageRel(ShopFlashSaleActivityParam param) {
PageParam<ShopFlashSaleActivity, ShopFlashSaleActivityParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
List<ShopFlashSaleActivityVO> list = baseMapper.selectPageRel(page, param);
if(CollectionUtils.isNotEmpty(list)){
List<Integer> goodsIdList = list.stream().map(ShopFlashSaleActivityVO::getGoodsId).distinct().collect(Collectors.toList());
List<ShopGoods> shopGoods = shopGoodsMapper.selectBatchIds(goodsIdList);
list.forEach(shopFlashSaleActivityVO -> {
ShopGoods shopGood = shopGoods.stream().filter(goods -> shopFlashSaleActivityVO.getGoodsId().equals(goods.getGoodsId())).findFirst().orElse(null);
if(shopGood != null){
shopFlashSaleActivityVO.setGoodsPrice(shopGood.getPrice());
shopFlashSaleActivityVO.setGoodsTotalPrice(shopGood.getPrice().multiply(new BigDecimal(shopFlashSaleActivityVO.getNum())));
shopFlashSaleActivityVO.setGoodsName(shopGood.getName());
shopFlashSaleActivityVO.setImage(shopGood.getImage());
shopFlashSaleActivityVO.setUnitName(shopGood.getUnitName());
}
});
}
return new PageResult<>(list, page.getTotal());
}
@Override
public List<ShopFlashSaleActivity> listRel(ShopFlashSaleActivityParam param) {
List<ShopFlashSaleActivity> list = baseMapper.selectListRel(param);
// 排序
PageParam<ShopFlashSaleActivity, ShopFlashSaleActivityParam> page = new PageParam<>();
page.setDefaultOrder("sort_number asc, create_time desc");
return page.sortRecords(list);
}
@Override
public ShopFlashSaleActivity getByIdRel(Integer id) {
ShopFlashSaleActivityParam param = new ShopFlashSaleActivityParam();
param.setId(id);
return param.getOne(baseMapper.selectListRel(param));
}
@Override
public List<ShopFlashSaleActivityVO> getMyActive(Integer tenantId) {
List<ShopFlashSaleActivityVO> resultVOList = new ArrayList<>();
User loginUser = LoginUserUtil.getLoginUser();
if(loginUser == null){
throw new BusinessException(GlobalErrorCodeConstants.UNAUTHORIZED.getMsg());
}
Boolean newUser = true;
//判断是否为新用户【只要未成功下单都判定为新用户】
LambdaQueryWrapper<ShopOrder> shopOrderLambdaQueryWrapper = new LambdaQueryWrapper<ShopOrder>().eq(ShopOrder::getUserId, loginUser.getUserId()).eq(ShopOrder::getPayStatus, 1)
.in(ShopOrder::getOrderStatus, Arrays.asList(0, 1));
List<ShopOrder> shopOrderList = shopOrderMapper.selectList(shopOrderLambdaQueryWrapper);
if(CollectionUtils.isNotEmpty(shopOrderList)){
newUser = false;
}
//查询满足条件的活动数据
LambdaQueryChainWrapper<ShopFlashSaleActivity> activityWrapper = lambdaQuery().eq(ShopFlashSaleActivity::getStatus, 0).gt(ShopFlashSaleActivity::getStock, 0).eq(ShopFlashSaleActivity::getTenantId, tenantId)
.apply("NOW() BETWEEN start_time AND end_time");
Map<Integer, Integer> activityMap = new HashMap<>();
if(!newUser){
//查询当前用户是否有下过秒杀活动订单数据【判断下单数量是否超过限制】
List<Integer> activityIdList = activityWrapper.list().stream().map(ShopFlashSaleActivity::getId).collect(Collectors.toList());
LambdaQueryWrapper<ShopOrder> shopOrderWrapper = new LambdaQueryWrapper<ShopOrder>().select(ShopOrder::getOrderId, ShopOrder::getActivityId, ShopOrder::getTotalNum)
.eq(ShopOrder::getUserId, loginUser.getUserId()).in(ShopOrder::getActivityId, activityIdList).in(ShopOrder::getOrderStatus, Arrays.asList(0, 1));
activityMap = shopOrderMapper.selectList(shopOrderWrapper).stream().collect(Collectors.groupingBy(ShopOrder::getActivityId, Collectors.summingInt(ShopOrder::getTotalNum)));
activityWrapper.eq(ShopFlashSaleActivity::getDisplayType, 0);
}
activityWrapper.orderByAsc(ShopFlashSaleActivity::getSortNumber);
List<ShopFlashSaleActivity> activityList = activityWrapper.list();
if(CollectionUtils.isNotEmpty(activityList)){
resultVOList = BeanUtil.copyToList(activityList, ShopFlashSaleActivityVO.class);
List<Integer> goodsIdList = resultVOList.stream().map(ShopFlashSaleActivityVO::getGoodsId).distinct().collect(Collectors.toList());
List<ShopGoods> shopGoods = shopGoodsMapper.selectBatchIds(goodsIdList);
resultVOList.forEach(activity -> {
ShopGoods shopGood = shopGoods.stream().filter(goods -> activity.getGoodsId().equals(goods.getGoodsId())).findFirst().orElse(null);
if(shopGood != null){
activity.setGoodsPrice(shopGood.getPrice());
activity.setGoodsTotalPrice(shopGood.getPrice().multiply(new BigDecimal(activity.getNum())));
activity.setGoodsName(shopGood.getName());
activity.setImage(shopGood.getImage());
activity.setUnitName(shopGood.getUnitName());
}
});
}
//过滤超下单数量活动
if(!activityMap.isEmpty()){
Iterator<ShopFlashSaleActivityVO> iterator = resultVOList.iterator();
while (iterator.hasNext()){
ShopFlashSaleActivityVO vo = iterator.next();
Integer orderTotalNum = activityMap.get(vo.getId());
if(orderTotalNum != null && orderTotalNum >= vo.getSaleLimit()){
iterator.remove();
}
}
}
return resultVOList;
}
@Override
public Boolean updateStatus(Integer id) {
ShopFlashSaleActivity saleActivity = baseMapper.selectById(id);
if(saleActivity != null){
if (saleActivity.getStatus() == 0){
saleActivity.setStatus(1);
}else {
saleActivity.setStatus(0);
}
User loginUser = LoginUserUtil.getLoginUser();
if(loginUser != null){
saleActivity.setUpdater(loginUser.getUserId().toString());
}
saleActivity.setUpdateTime(LocalDateTime.now());
baseMapper.updateById(saleActivity);
return Boolean.TRUE;
}else {
throw new BusinessException(GlobalErrorCodeConstants.NOT_FOUND.getMsg());
}
}
@Override
public Boolean updateSortNumber(Integer id, Integer sortNumber) {
ShopFlashSaleActivity saleActivity = baseMapper.selectById(id);
if(saleActivity != null){
saleActivity.setSortNumber(sortNumber);
saleActivity.setUpdateTime(LocalDateTime.now());
baseMapper.updateById(saleActivity);
return Boolean.TRUE;
}else {
throw new BusinessException(GlobalErrorCodeConstants.NOT_FOUND.getMsg());
}
}
}

View File

@@ -1,40 +1,49 @@
package com.gxwebsoft.shop.service.impl; package com.gxwebsoft.shop.service.impl;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.context.TenantContext;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.config.CertificateProperties; import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.utils.*; import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.service.PaymentCacheService;
import com.gxwebsoft.common.core.service.EnvironmentAwarePaymentService;
import com.gxwebsoft.common.core.config.SpringContextUtil; import com.gxwebsoft.common.core.config.SpringContextUtil;
import com.gxwebsoft.common.core.context.TenantContext;
import com.gxwebsoft.common.core.enums.ShopDealerCapitalUpdateEnum;
import com.gxwebsoft.common.core.enums.ShopDealerTypeEnum;
import com.gxwebsoft.common.core.exception.enums.GlobalErrorCodeConstants;
import com.gxwebsoft.common.core.service.EnvironmentAwarePaymentService;
import com.gxwebsoft.common.core.service.PaymentCacheService;
import com.gxwebsoft.common.core.utils.*;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.system.entity.Payment; import com.gxwebsoft.common.system.entity.Payment;
import com.gxwebsoft.shop.entity.*; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.service.*; import com.gxwebsoft.common.system.mapper.UserMapper;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import lombok.extern.slf4j.Slf4j;
import com.gxwebsoft.common.system.service.PaymentService; import com.gxwebsoft.common.system.service.PaymentService;
import com.gxwebsoft.common.system.service.SettingService; import com.gxwebsoft.common.system.service.SettingService;
import com.gxwebsoft.payment.constants.WechatPayType; import com.gxwebsoft.payment.constants.WechatPayType;
import com.gxwebsoft.shop.dto.ShopDealerUserReduceDto;
import com.gxwebsoft.shop.dto.VerifyShopOrderDto;
import com.gxwebsoft.shop.entity.*;
import com.gxwebsoft.shop.mapper.ShopGoodsMapper;
import com.gxwebsoft.shop.mapper.ShopOrderMapper; import com.gxwebsoft.shop.mapper.ShopOrderMapper;
import com.gxwebsoft.shop.mapper.ShopUserAddressMapper;
import com.gxwebsoft.shop.param.ShopOrderParam; import com.gxwebsoft.shop.param.ShopOrderParam;
import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.shop.service.*;
import com.gxwebsoft.common.core.web.PageResult;
import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.RSAConfig; import com.wechat.pay.java.core.RSAConfig;
import com.wechat.pay.java.core.RSAPublicKeyConfig; import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.exception.ServiceException; import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension; import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*; import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
// Native支付的类将使用完全限定名避免冲突
import com.wechat.pay.java.service.payments.model.Transaction; import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
@@ -44,6 +53,7 @@ import java.time.LocalDateTime;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -91,6 +101,14 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
private ShopExpressService shopExpressService; private ShopExpressService shopExpressService;
@Resource @Resource
private ShopGoodsSkuService shopGoodsSkuService; private ShopGoodsSkuService shopGoodsSkuService;
@Resource
private ShopGoodsMapper shopGoodsMapper;
@Resource
private ShopUserAddressMapper shopUserAddressMapper;
@Resource
private ShopDealerUserService shopDealerUserService;
@Resource
private UserMapper userMapper;
private static final long USER_ORDER_STATS_CACHE_SECONDS = 60L; private static final long USER_ORDER_STATS_CACHE_SECONDS = 60L;
private static final long WECHAT_PREPAY_SNAPSHOT_TTL_MINUTES = 30L; private static final long WECHAT_PREPAY_SNAPSHOT_TTL_MINUTES = 30L;
@@ -320,7 +338,14 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
public ShopOrder getByIdRel(Integer orderId) { public ShopOrder getByIdRel(Integer orderId) {
ShopOrderParam param = new ShopOrderParam(); ShopOrderParam param = new ShopOrderParam();
param.setOrderId(orderId); param.setOrderId(orderId);
return param.getOne(baseMapper.selectListRel(param)); ShopOrder order = param.getOne(baseMapper.selectListRel(param));
if(order != null && order.getAddressId() != null){
ShopUserAddress shopUserAddress = shopUserAddressMapper.selectById(order.getAddressId());
if(shopUserAddress != null && StrUtil.isNotBlank(shopUserAddress.getFullAddress())){
order.setFullAddress(shopUserAddress.getFullAddress());
}
}
return order;
} }
@Override @Override
@@ -372,6 +397,119 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
return stats; return stats;
} }
@Override
@Transactional
public Boolean verifyOrder(VerifyShopOrderDto verifyDto) {
try {
//1.判断用户是否登录
User loginUser = LoginUserUtil.getLoginUser();
if(loginUser == null){
throw new RuntimeException(GlobalErrorCodeConstants.UNAUTHORIZED.getMsg());
}
//2.判断是否有核销权限
Integer verifyType = verifyDto.getVerifyType();
if(verifyType == 2){
User user = userMapper.getById(loginUser.getUserId());
if(!(user != null && user.getVerifyFlag() != null && user.getVerifyFlag() == 1)){
throw new RuntimeException("暂无核销权限!");
}
}
//3.查询订单信息
ShopOrder shopOrder = baseMapper.selectOne(new LambdaQueryWrapper<ShopOrder>().eq(ShopOrder::getVerifyCode, verifyDto.getVerifyCode()));
if(shopOrder == null){
throw new RuntimeException(GlobalErrorCodeConstants.NOT_FOUND.getMsg());
}
//4.判断当前订单状态是否允许核销
if(Arrays.asList(2,3,4,6,7).contains(shopOrder.getOrderStatus())){
throw new RuntimeException("订单当前状态不可核销!");
}
//5.判断订单是否已支付
if(!shopOrder.getPayStatus()){
throw new RuntimeException("订单未支付,不可核销!");
}
//6.判断是否已核销
if(shopOrder.getVerifyStatus() != null && shopOrder.getVerifyStatus() == 1){
throw new RuntimeException("此订单已核销,无需重复核销!");
}
shopOrder.setVerifyStatus(1);
shopOrder.setVerifyTime(LocalDateTime.now());
shopOrder.setVerifyUser(loginUser.getUserId());
//7.只有推广结算才会记佣且判断是否在设定有效期内核销订单【是:计算订单核销佣金 否:不计算核销佣金】
if(verifyType == 2 && shopOrder.getVerifyExpTime().isAfter(LocalDateTime.now())){
List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderId(shopOrder.getOrderId());
if(!CollectionUtils.isEmpty(orderGoodsList)){
List<Integer> goodsIdList = orderGoodsList.stream().map(ShopOrderGoods::getGoodsId).distinct().collect(Collectors.toList());
List<ShopGoods> shopGoods = shopGoodsMapper.selectBatchIds(goodsIdList);
//计算推广佣金
AtomicReference<BigDecimal> verifyMoney = new AtomicReference<>(BigDecimal.ZERO);
orderGoodsList.forEach(orderGoods -> {
ShopGoods shopGood = shopGoods.stream().filter(goods -> orderGoods.getGoodsId().equals(goods.getGoodsId()) &&
goods.getVerifyRate().compareTo(BigDecimal.ZERO) > 0).findFirst().orElse(null);
if (shopGood != null) {
// 计算单商品核价金额
BigDecimal money = orderGoods.getPrice()
.multiply(BigDecimal.valueOf(orderGoods.getTotalNum()))
.multiply(shopGood.getVerifyRate())
.divide(BigDecimal.valueOf(100), 3, RoundingMode.HALF_UP); // 除100保留2位小数
// ✅ 原子更新(关键!)
verifyMoney.set(verifyMoney.get().add(money));
}
});
shopOrder.setVerifyMoney(verifyMoney.get());
//结算推广佣金
if(verifyMoney.get().compareTo(BigDecimal.ZERO) > 0){
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
reduceDto.setTypeEnum(ShopDealerTypeEnum.WITHDRAW_ACCOUNT);
reduceDto.setUserId(loginUser.getUserId());
reduceDto.setOrderUserId(shopOrder.getUserId());
reduceDto.setOrderNo(shopOrder.getOrderNo());
reduceDto.setPrice(verifyMoney.get());
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.PROMOTION_INCOME);
Boolean reduceBalance = shopDealerUserService.reduceBalance(reduceDto);
if(!reduceBalance){
log.error("推广核销失败,结算推广佣金失败:" + reduceDto);
}
}
}
}
//8.判断是否自提订单,如是自提订单需调整为订单完成,用户已收货状态
if(shopOrder.getOrderType() != null && shopOrder.getOrderType() == 2){
shopOrder.setOrderStatus(1);
shopOrder.setDeliveryStatus(20);
//分销佣金结算【团队长管理津贴、股东分红在订单完成后再结算】
ShopDealerUserReduceDto reduceDto = new ShopDealerUserReduceDto();
reduceDto.setTypeEnum(ShopDealerTypeEnum.DEFROST);
reduceDto.setOrderUserId(shopOrder.getUserId());
reduceDto.setOrderNo(shopOrder.getOrderNo());
reduceDto.setUpdateEnum(ShopDealerCapitalUpdateEnum.FREEZE_MONEY_THAW);
Boolean reduceBalance = shopDealerUserService.reduceBalance(reduceDto);
if(!reduceBalance){
log.error("分销核销失败,分销结算失败:" + reduceDto);
}
}
return baseMapper.updateById(shopOrder) > 0;
}catch (Exception e){
throw new RuntimeException("核销失败:" + e.getMessage());
}
}
private static Long getLong(Map<String, Object> raw, String key) { private static Long getLong(Map<String, Object> raw, String key) {
Object value = raw.get(key); Object value = raw.get(key);
if (value == null) { if (value == null) {

View File

@@ -106,4 +106,49 @@ public class ShopStoreFenceServiceImpl extends ServiceImpl<ShopStoreFenceMapper,
throw new BusinessException("收货地址不在配送范围内"); throw new BusinessException("收货地址不在配送范围内");
} }
@Override
public Boolean validatePointInEnabled(Integer tenantId, double lng, double lat) {
if (tenantId == null) {
// tenantId 缺失时不做围栏校验,避免误伤;上层应保证 tenantId 正确传入
return true;
}
List<ShopStoreFence> fences = this.list(new LambdaQueryWrapper<ShopStoreFence>()
.eq(ShopStoreFence::getTenantId, tenantId)
.eq(ShopStoreFence::getStatus, 0)
.orderByAsc(ShopStoreFence::getSortNumber)
.orderByDesc(ShopStoreFence::getCreateTime));
// 无围栏配置:默认放行
if (fences == null || fences.isEmpty()) {
return true;
}
for (ShopStoreFence fence : fences) {
if (fence == null) {
continue;
}
List<GeoFenceUtil.Point> polygon;
try {
polygon = GeoFenceUtil.parsePolygonPoints(fence.getPoints());
} catch (Exception e) {
// points 异常:直接拒单并记录日志,避免误送
log.error("围栏 points 解析失败tenantId={}, fenceId={}, points={}",
tenantId, fence.getId(), fence.getPoints(), e);
throw new BusinessException("配送范围配置异常,请联系商家");
}
if (polygon == null || polygon.size() < 3) {
log.error("围栏 points 点数不足tenantId={}, fenceId={}, points={}",
tenantId, fence.getId(), fence.getPoints());
throw new BusinessException("配送范围配置异常,请联系商家");
}
if (GeoFenceUtil.containsInclusive(polygon, lng, lat)) {
return true; // 命中任一围栏即通过
}
}
return false;
}
} }

View File

@@ -0,0 +1,114 @@
package com.gxwebsoft.shop.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.exception.enums.GlobalErrorCodeConstants;
import com.gxwebsoft.common.core.utils.LoginUserUtil;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.mapper.ShopSurchargeConfigMapper;
import com.gxwebsoft.shop.service.ShopSurchargeConfigService;
import com.gxwebsoft.shop.entity.ShopSurchargeConfig;
import com.gxwebsoft.shop.param.ShopSurchargeConfigParam;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
/**
* 步梯费用设置Service实现
*
* @author xm
* @since 2026-04-28 16:30:00
*/
@Service
@AllArgsConstructor
public class ShopSurchargeConfigServiceImpl extends ServiceImpl<ShopSurchargeConfigMapper, ShopSurchargeConfig> implements ShopSurchargeConfigService {
@Override
public PageResult<ShopSurchargeConfig> pageRel(ShopSurchargeConfigParam param) {
PageParam<ShopSurchargeConfig, ShopSurchargeConfigParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
List<ShopSurchargeConfig> list = baseMapper.selectPageRel(page, param);
return new PageResult<>(list, page.getTotal());
}
@Override
public List<ShopSurchargeConfig> listRel(ShopSurchargeConfigParam param) {
List<ShopSurchargeConfig> list = baseMapper.selectListRel(param);
// 排序
PageParam<ShopSurchargeConfig, ShopSurchargeConfigParam> page = new PageParam<>();
page.setDefaultOrder("sort_number asc, create_time desc");
return page.sortRecords(list);
}
@Override
public ShopSurchargeConfig getByIdRel(Integer id) {
ShopSurchargeConfigParam param = new ShopSurchargeConfigParam();
param.setId(id);
return param.getOne(baseMapper.selectListRel(param));
}
@Override
public ShopSurchargeConfig getInfoByType(Integer type) {
List<ShopSurchargeConfig> list = lambdaQuery().eq(ShopSurchargeConfig::getType, type).list();
if(CollectionUtils.isNotEmpty(list)){
return list.get(0);
}
return null;
}
@Override
public Integer saveInfo(ShopSurchargeConfig entity) {
List<ShopSurchargeConfig> list = lambdaQuery().eq(ShopSurchargeConfig::getType, entity.getType()).list();
if(CollectionUtils.isNotEmpty(list)){
throw new RuntimeException("该类型收费数据已存在!");
}
User loginUser = LoginUserUtil.getLoginUser();
entity.setCreator(String.valueOf(loginUser.getUserId()));
entity.setCreateTime(LocalDateTime.now());
baseMapper.insert(entity);
return entity.getId();
}
@Override
public Boolean updateInfo(ShopSurchargeConfig entity) {
List<ShopSurchargeConfig> list = lambdaQuery().eq(ShopSurchargeConfig::getType, entity.getType()).list();
if(CollectionUtils.isNotEmpty(list)){
throw new RuntimeException("该类型收费数据已存在!");
}
User loginUser = LoginUserUtil.getLoginUser();
entity.setUpdater(String.valueOf(loginUser.getUserId()));
entity.setUpdateTime(LocalDateTime.now());
return baseMapper.updateById(entity) > 0;
}
@Override
public Boolean updateStatus(Integer id) {
User loginUser = LoginUserUtil.getLoginUser();
if(loginUser == null){
throw new RuntimeException(GlobalErrorCodeConstants.UNAUTHORIZED.getMsg());
}
ShopSurchargeConfig config = baseMapper.selectById(id);
if(config == null){
throw new RuntimeException(GlobalErrorCodeConstants.NOT_FOUND.getMsg());
}
if(config.getStatus() == 0){
config.setStatus(1);
}else {
config.setStatus(0);
}
config.setUpdater(String.valueOf(loginUser.getUserId()));
config.setUpdateTime(LocalDateTime.now());
return baseMapper.updateById(config) > 0;
}
}

View File

@@ -8,8 +8,11 @@ import com.gxwebsoft.shop.entity.ShopUserAddress;
import com.gxwebsoft.shop.param.ShopUserAddressParam; import com.gxwebsoft.shop.param.ShopUserAddressParam;
import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.PageResult;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@@ -65,4 +68,25 @@ public class ShopUserAddressServiceImpl extends ServiceImpl<ShopUserAddressMappe
return list(wrapper); return list(wrapper);
} }
@Override
@Transactional
public Boolean updateInfo(ShopUserAddress shopUserAddress) {
ShopUserAddress address = baseMapper.selectById(shopUserAddress.getId());
if(address != null){
if(shopUserAddress.getIsDefault()){
List<ShopUserAddress> list = lambdaQuery().eq(ShopUserAddress::getIsDefault, true).eq(ShopUserAddress::getUserId, address.getUserId())
.ne(ShopUserAddress::getId, address.getId()).list();
if(CollectionUtils.isNotEmpty(list)){
list.forEach(userAddress ->{
userAddress.setIsDefault(false);
});
}
updateBatchById(list);
}
shopUserAddress.setUpdateTime(LocalDateTime.now());
return baseMapper.updateById(shopUserAddress) > 0;
}
return false;
}
} }

View File

@@ -38,7 +38,7 @@ public class OrderAutoCancelTask {
* 生产环境每5分钟执行一次 * 生产环境每5分钟执行一次
* 开发环境每1分钟执行一次便于测试 * 开发环境每1分钟执行一次便于测试
*/ */
@Scheduled(cron = "${shop.order.auto-cancel.cron:0 */1 * * * ?}") @Scheduled(cron = "${shop.order.auto-cancel.cron:0 */5 * * * ?}")
@IgnoreTenant("定时任务需要处理所有租户的超时订单") @IgnoreTenant("定时任务需要处理所有租户的超时订单")
public void cancelExpiredOrders() { public void cancelExpiredOrders() {
if (!orderConfig.getAutoCancel().isEnabled()) { if (!orderConfig.getAutoCancel().isEnabled()) {

View File

@@ -0,0 +1,27 @@
package com.gxwebsoft.shop.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
*
* @author xm
* @since 2026-05-13
*/
@Data
@Schema(description = "两级推荐人数据")
public class ShopDealerRefereeVO {
@Schema(description = "一级推荐人")
private Integer directUserId;
@Schema(description = "一级推荐人类型 0-分销商 1-门店/服务商 2-集团分红")
private Integer directUserType;
@Schema(description = "二级推荐人")
private Integer simpleUserId;
@Schema(description = "二级推荐人类型 0-分销商 1-门店/服务商 2-集团分红")
private Integer simpleUserType;
}

View File

@@ -0,0 +1,96 @@
package com.gxwebsoft.shop.vo;
import com.baomidou.mybatisplus.annotation.TableLogic;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 秒杀活动
*
* @author xm
* @since 2026-04-22 17:18:17
*/
@Data
public class ShopFlashSaleActivityVO implements Serializable {
@Schema(description = "秒杀活动编号")
private Integer id;
@Schema(description = "秒杀活动名称")
private String name;
@Schema(description = "秒杀活动商品")
private Integer goodsId;
@Schema(description = "秒杀活动商品名称")
private String goodsName;
@Schema(description = "商品skuId")
private Integer skuId;
@Schema(description = "商品图片地址")
private String image;
@Schema(description = "单位")
private String unitName;
@Schema(description = "商品数量")
private Integer num;
@Schema(description = "秒杀价")
private BigDecimal price;
@Schema(description = "商品单价")
private BigDecimal goodsPrice;
@Schema(description = "商品总价")
private BigDecimal goodsTotalPrice;
@Schema(description = "活动状态 0-开启 1-关闭")
private Integer status;
@Schema(description = "活动开始时间")
private LocalDateTime startTime;
@Schema(description = "活动结束时间")
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 = "租户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 = "是否删除")
@TableLogic
private Integer deleted;
}

View File

@@ -0,0 +1,47 @@
package com.gxwebsoft.shop.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
/**
* 订单商品信息
* @author xm
* @since 2025-01-12
*/
@Data
@Schema(description = "订单商品信息")
public class ShopOrderGoodsVO {
@Schema(description = "订单号")
private String orderNo;
@Schema(description = "商品ID")
private Integer goodsId;
@Schema(description = "品名")
private String name;
@Schema(description = "购买数量")
private Integer totalNum;
@Schema(description = "单价")
private BigDecimal price;
@Schema(description = "结算方式 10按金额 20按比率")
private Integer commissionType;
@Schema(description = "一级分销员佣金比例/金额")
private BigDecimal firstMoney;
@Schema(description = "二级分销员佣金比例/金额")
private BigDecimal secondMoney;
@Schema(description = "一级服务商管理津贴比例/金额")
private BigDecimal firstDividend;
@Schema(description = "二级服务商管理津贴比例/金额")
private BigDecimal secondDividend;
}

View File

@@ -7,18 +7,21 @@ server:
# 数据源配置 # 数据源配置
spring: spring:
datasource: datasource:
url: jdbc:mysql://8.134.55.105:13306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 url: jdbc:mysql://47.107.249.41:13306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: modules username: modules
password: tYmmMGh5wpwXR3ae password: tYmmMGh5wpwXR3ae
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource type: com.alibaba.druid.pool.DruidDataSource
# redis
redis: redis:
database: 0 database: 0
host: 8.134.55.105 host: localhost
port: 16379 port: 6379
password: redis_t74P8C
# database: 0
# host: 8.134.55.105
# port: 16379
# password: redis_t74P8C
# 日志配置 # 日志配置
logging: logging:

View File

@@ -51,6 +51,7 @@ config:
server-url: https://glt-server.websoft.top/api server-url: https://glt-server.websoft.top/api
# 业务模块接口 # 业务模块接口
api-url: https://glt-api.websoft.top/api api-url: https://glt-api.websoft.top/api
# api-url: https://glt-dev-api.websoft.top/api
upload-path: /www/wwwroot/file.ws upload-path: /www/wwwroot/file.ws
# 阿里云OSS云存储 # 阿里云OSS云存储

View File

@@ -5,6 +5,8 @@ server:
spring: spring:
profiles: profiles:
active: dev active: dev
# active: glt
# active: prod
application: application:
name: server name: server
@@ -254,7 +256,7 @@ payment:
# 开发环境配置 # 开发环境配置
dev: dev:
# 开发环境回调地址(本地调试用) # 开发环境回调地址(本地调试用)
notify-url: "http://frps-10550.s209.websoft.top/api/shop/shop-order/notify" notify-url: "https://glt-dev-api.websoft.top/api/shop/shop-order/notify"
# 开发环境是否启用环境感知 # 开发环境是否启用环境感知
environment-aware: true environment-aware: true

View File

@@ -38,11 +38,11 @@ public class ShopGenerator {
// Vue文件输出目录 // Vue文件输出目录
private static final String OUTPUT_DIR_VUE = "/src"; private static final String OUTPUT_DIR_VUE = "/src";
// 作者名称 // 作者名称
private static final String AUTHOR = "科技小王子"; private static final String AUTHOR = "xm";
// 是否在xml中添加二级缓存配置 // 是否在xml中添加二级缓存配置
private static final boolean ENABLE_CACHE = false; private static final boolean ENABLE_CACHE = false;
// 数据库连接配置 // 数据库连接配置
private static final String DB_URL = "jdbc:mysql://8.134.55.105:13306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8"; private static final String DB_URL = "jdbc:mysql://47.107.249.41:13306/modules?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8";
private static final String DB_DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String DB_DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String DB_USERNAME = "modules"; private static final String DB_USERNAME = "modules";
private static final String DB_PASSWORD = "tYmmMGh5wpwXR3ae"; private static final String DB_PASSWORD = "tYmmMGh5wpwXR3ae";
@@ -105,7 +105,9 @@ public class ShopGenerator {
// "shop_express_template", // "shop_express_template",
// "shop_express_template_detail", // "shop_express_template_detail",
// "shop_gift" // "shop_gift"
"shop_article" // "shop_flash_sale_activity"
// "shop_active_image"
"aa"
}; };
// 需要去除的表前缀 // 需要去除的表前缀
private static final String[] TABLE_PREFIX = new String[]{ private static final String[] TABLE_PREFIX = new String[]{

View File

@@ -19,10 +19,8 @@ import ${cfg.packageName!}.${package.ModuleName}.entity.${entity};
import ${cfg.packageName!}.${package.ModuleName}.param.${entity}Param; import ${cfg.packageName!}.${package.ModuleName}.param.${entity}Param;
import ${cfg.packageName!}.common.core.web.ApiResult; import ${cfg.packageName!}.common.core.web.ApiResult;
import ${cfg.packageName!}.common.core.web.PageResult; import ${cfg.packageName!}.common.core.web.PageResult;
import ${cfg.packageName!}.common.core.web.PageParam;
import ${cfg.packageName!}.common.core.web.BatchParam; import ${cfg.packageName!}.common.core.web.BatchParam;
import ${cfg.packageName!}.common.core.annotation.OperationLog; import ${cfg.packageName!}.common.core.annotation.OperationLog;
import ${cfg.packageName!}.common.system.entity.User;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;

View File

@@ -3,6 +3,7 @@ package ${package.Entity};
<% for(pkg in table.importPackages) { %> <% for(pkg in table.importPackages) { %>
import ${pkg}; import ${pkg};
<% } %> <% } %>
import com.baomidou.mybatisplus.annotation.TableName;
<% if(swagger2) { %> <% if(swagger2) { %>
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
<% } %> <% } %>
@@ -34,6 +35,7 @@ import lombok.experimental.Accessors;
<% if(swagger2) { %> <% if(swagger2) { %>
@Schema(name = "${entity}对象", description = "${table.comment!''}") @Schema(name = "${entity}对象", description = "${table.comment!''}")
<% } %> <% } %>
@TableName("${table.name}")
<% if(table.convert) { %> <% if(table.convert) { %>
@TableName("${table.name}") @TableName("${table.name}")
<% } %> <% } %>

View File

@@ -16,6 +16,7 @@ import ${package.Entity}.${entity};
import ${cfg.packageName!}.${package.ModuleName}.param.${entity}Param; import ${cfg.packageName!}.${package.ModuleName}.param.${entity}Param;
import ${cfg.packageName!}.common.core.web.PageParam; import ${cfg.packageName!}.common.core.web.PageParam;
import ${cfg.packageName!}.common.core.web.PageResult; import ${cfg.packageName!}.common.core.web.PageResult;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
@@ -27,6 +28,7 @@ import java.util.List;
* @since ${date(), 'yyyy-MM-dd HH:mm:ss'} * @since ${date(), 'yyyy-MM-dd HH:mm:ss'}
*/ */
@Service @Service
@AllArgsConstructor
<% if(kotlin){ %> <% if(kotlin){ %>
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} { open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {