Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -149,6 +149,7 @@ public class CreditCompanyController extends BaseController {
|
|||||||
List<String> errorMessages = new ArrayList<>();
|
List<String> errorMessages = new ArrayList<>();
|
||||||
int insertedCount = 0;
|
int insertedCount = 0;
|
||||||
Set<String> touchedMatchNames = new HashSet<>();
|
Set<String> touchedMatchNames = new HashSet<>();
|
||||||
|
String refreshWarning = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<CreditCompanyImportParam> list = null;
|
List<CreditCompanyImportParam> list = null;
|
||||||
@@ -324,13 +325,31 @@ public class CreditCompanyController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
creditCompanyRecordCountService.refreshAll(touchedCompanyIds);
|
creditCompanyRecordCountService.refreshAll(touchedCompanyIds);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// 导入本身已经成功写入,回填计数字段失败不应导致整个导入失败(可后续单独重试刷新)。
|
||||||
|
String msg = ex.getMessage();
|
||||||
|
if (msg != null && msg.length() > 300) {
|
||||||
|
msg = msg.substring(0, 300) + "...";
|
||||||
|
}
|
||||||
|
refreshWarning = "关联记录数回填失败:" + (msg != null ? msg : ex.getClass().getSimpleName());
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMessages.isEmpty()) {
|
if (errorMessages.isEmpty()) {
|
||||||
return success("成功入库" + insertedCount + "条数据", null);
|
String msg = "成功入库" + insertedCount + "条数据";
|
||||||
|
if (refreshWarning != null) {
|
||||||
|
msg = msg + ";" + refreshWarning;
|
||||||
|
}
|
||||||
|
return success(msg, null);
|
||||||
} else {
|
} else {
|
||||||
return success("导入完成,入库" + insertedCount + "条,失败" + errorMessages.size() + "条", errorMessages);
|
String msg = "导入完成,入库" + insertedCount + "条,失败" + errorMessages.size() + "条";
|
||||||
|
if (refreshWarning != null) {
|
||||||
|
msg = msg + ";" + refreshWarning;
|
||||||
|
}
|
||||||
|
return success(msg, errorMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -82,16 +82,24 @@ public class CreditCompanyRecordCountService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 表/字段名来自固定枚举,不允许外部传入,避免 SQL 注入。
|
// 表/字段名来自固定枚举,不允许外部传入,避免 SQL 注入。
|
||||||
|
//
|
||||||
|
// 这里不要用「UPDATE ... SET col=(SELECT COUNT... WHERE t.company_id=c.id)」的相关子查询:
|
||||||
|
// 如果关联表缺少 (company_id, deleted) 索引,会导致对每个 company_id 都进行一次全表扫描,极慢,
|
||||||
|
// 进而容易触发 JDBC/MySQL 侧的超时/断链(Communications link failure)。
|
||||||
|
//
|
||||||
|
// 用聚合后再 JOIN 的方式:每个 chunk 只扫描一次关联表。
|
||||||
String sql = ""
|
String sql = ""
|
||||||
+ "UPDATE credit_company c "
|
+ "UPDATE credit_company c "
|
||||||
+ "SET " + type.getCompanyColumn() + " = ("
|
+ "LEFT JOIN ("
|
||||||
+ " SELECT COUNT(1) "
|
+ " SELECT company_id, COUNT(1) AS cnt "
|
||||||
+ " FROM " + type.getSourceTable() + " t "
|
+ " FROM " + type.getSourceTable() + " "
|
||||||
+ " WHERE t.company_id = c.id AND t.deleted = 0"
|
+ " WHERE deleted = 0 AND company_id IN (:ids) "
|
||||||
+ ") "
|
+ " GROUP BY company_id"
|
||||||
|
+ ") t ON t.company_id = c.id "
|
||||||
|
+ "SET c." + type.getCompanyColumn() + " = IFNULL(t.cnt, 0) "
|
||||||
+ "WHERE c.id IN (:ids)";
|
+ "WHERE c.id IN (:ids)";
|
||||||
|
|
||||||
final int inChunkSize = 1000;
|
final int inChunkSize = 500;
|
||||||
for (int i = 0; i < ids.size(); i += inChunkSize) {
|
for (int i = 0; i < ids.size(); i += inChunkSize) {
|
||||||
List<Integer> chunk = ids.subList(i, Math.min(ids.size(), i + inChunkSize));
|
List<Integer> chunk = ids.subList(i, Math.min(ids.size(), i + inChunkSize));
|
||||||
namedJdbc.update(sql, new MapSqlParameterSource("ids", chunk));
|
namedJdbc.update(sql, new MapSqlParameterSource("ids", chunk));
|
||||||
|
|||||||
@@ -242,6 +242,25 @@ public class GltTicketOrderController extends BaseController {
|
|||||||
@PutMapping()
|
@PutMapping()
|
||||||
public ApiResult<?> update(@RequestBody GltTicketOrder gltTicketOrder) {
|
public ApiResult<?> update(@RequestBody GltTicketOrder gltTicketOrder) {
|
||||||
if (gltTicketOrderService.updateById(gltTicketOrder)) {
|
if (gltTicketOrderService.updateById(gltTicketOrder)) {
|
||||||
|
Integer tenantId = getTenantId();
|
||||||
|
// 后台指派配送员(直接改 riderId)时,同步商城订单为“已发货”(deliveryStatus=20)
|
||||||
|
if (gltTicketOrder != null
|
||||||
|
&& gltTicketOrder.getId() != null
|
||||||
|
&& gltTicketOrder.getRiderId() != null
|
||||||
|
&& gltTicketOrder.getRiderId() > 0) {
|
||||||
|
gltTicketOrderService.markShopOrderShippedAfterRiderAssigned(
|
||||||
|
gltTicketOrder.getId(),
|
||||||
|
tenantId,
|
||||||
|
gltTicketOrder.getRiderId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 后台直接改“已完成”(deliveryStatus=40)时,同步商城订单为“已完成”(orderStatus=1)
|
||||||
|
if (gltTicketOrder != null
|
||||||
|
&& gltTicketOrder.getId() != null
|
||||||
|
&& gltTicketOrder.getDeliveryStatus() != null
|
||||||
|
&& gltTicketOrder.getDeliveryStatus() == GltTicketOrderService.DELIVERY_STATUS_FINISHED) {
|
||||||
|
gltTicketOrderService.markShopOrderCompletedAfterTicketFinished(gltTicketOrder.getId(), tenantId);
|
||||||
|
}
|
||||||
return success("修改成功");
|
return success("修改成功");
|
||||||
}
|
}
|
||||||
return fail("修改失败");
|
return fail("修改失败");
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ public class GltTicketOrder implements Serializable {
|
|||||||
@Schema(description = "用户水票ID")
|
@Schema(description = "用户水票ID")
|
||||||
private Integer userTicketId;
|
private Integer userTicketId;
|
||||||
|
|
||||||
|
@Schema(description = "订单编号")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String orderNo;
|
||||||
|
|
||||||
@Schema(description = "门店ID")
|
@Schema(description = "门店ID")
|
||||||
private Integer storeId;
|
private Integer storeId;
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,16 @@
|
|||||||
u.nickname, u.phone, u.avatar,
|
u.nickname, u.phone, u.avatar,
|
||||||
d.name as receiverName, d.phone as receiverPhone,
|
d.name as receiverName, d.phone as receiverPhone,
|
||||||
d.province as receiverProvince, d.city as receiverCity, d.region as receiverRegion,
|
d.province as receiverProvince, d.city as receiverCity, d.region as receiverRegion,
|
||||||
d.address as receiverAddress, d.full_address as receiverFullAddress, d.lat as receiverLat, d.lng as receiverLng
|
d.address as receiverAddress, d.full_address as receiverFullAddress, d.lat as receiverLat, d.lng as receiverLng,
|
||||||
|
COALESCE(o.order_no, f.order_no) as orderNo
|
||||||
FROM glt_ticket_order a
|
FROM glt_ticket_order a
|
||||||
LEFT JOIN shop_store b ON a.store_id = b.id
|
LEFT JOIN shop_store b ON a.store_id = b.id
|
||||||
LEFT JOIN shop_store_warehouse w ON a.warehouse_id = w.id
|
LEFT JOIN shop_store_warehouse w ON a.warehouse_id = w.id
|
||||||
LEFT JOIN shop_store_rider c ON a.rider_id = c.user_id
|
LEFT JOIN shop_store_rider c ON a.rider_id = c.user_id
|
||||||
LEFT JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id
|
LEFT JOIN gxwebsoft_core.sys_user u ON a.user_id = u.user_id
|
||||||
LEFT JOIN shop_user_address d ON a.address_id = d.id
|
LEFT JOIN shop_user_address d ON a.address_id = d.id
|
||||||
|
LEFT JOIN glt_user_ticket f ON a.user_ticket_id = f.id
|
||||||
|
LEFT JOIN shop_order o ON f.order_id = o.order_id AND f.tenant_id = o.tenant_id AND o.deleted = 0
|
||||||
|
|
||||||
<where>
|
<where>
|
||||||
<if test="param.id != null">
|
<if test="param.id != null">
|
||||||
|
|||||||
@@ -168,9 +168,6 @@ public class GltTicketIssueService {
|
|||||||
new LambdaUpdateWrapper<ShopOrder>()
|
new LambdaUpdateWrapper<ShopOrder>()
|
||||||
.eq(ShopOrder::getOrderId, order.getOrderId())
|
.eq(ShopOrder::getOrderId, order.getOrderId())
|
||||||
.eq(ShopOrder::getTenantId, tenantId)
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
.set(ShopOrder::getOrderStatus, 1)
|
|
||||||
// 同步更新发货状态为“已发货”
|
|
||||||
.set(ShopOrder::getDeliveryStatus, 20)
|
|
||||||
.set(ShopOrder::getHasTakeGift, true)
|
.set(ShopOrder::getHasTakeGift, true)
|
||||||
.set(ShopOrder::getUpdateTime, now)
|
.set(ShopOrder::getUpdateTime, now)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -64,6 +64,20 @@ public interface GltTicketOrderService extends IService<GltTicketOrder> {
|
|||||||
*/
|
*/
|
||||||
void accept(Integer id, Integer riderId, Integer tenantId);
|
void accept(Integer id, Integer riderId, Integer tenantId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指派/接单成功后,同步关联商城订单发货状态为“已发货”(deliveryStatus=20)。
|
||||||
|
*
|
||||||
|
* <p>用于后台指派配送员(不走接单接口)等场景的状态兜底同步。</p>
|
||||||
|
*/
|
||||||
|
void markShopOrderShippedAfterRiderAssigned(Integer ticketOrderId, Integer tenantId, Integer riderId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 送水订单完成后,同步关联商城订单为“已完成”(orderStatus=1)。
|
||||||
|
*
|
||||||
|
* <p>用于后台直接改 deliveryStatus=40 等不经过 confirmReceive/autoConfirmTimeout 的兜底同步。</p>
|
||||||
|
*/
|
||||||
|
void markShopOrderCompletedAfterTicketFinished(Integer ticketOrderId, Integer tenantId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 配送员开始配送:10 -> 20,并写 sendStartTime。
|
* 配送员开始配送:10 -> 20,并写 sendStartTime。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.gxwebsoft.glt.service.impl;
|
package com.gxwebsoft.glt.service.impl;
|
||||||
|
|
||||||
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.update.LambdaUpdateWrapper;
|
||||||
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.common.core.web.PageParam;
|
import com.gxwebsoft.common.core.web.PageParam;
|
||||||
@@ -18,8 +20,12 @@ 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.entity.ShopDealerCapital;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
|
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.ShopOrderService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -70,6 +76,12 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
@Resource
|
@Resource
|
||||||
private UserMapper userMapper;
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ShopOrderService shopOrderService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ShopOrderGoodsService shopOrderGoodsService;
|
||||||
|
|
||||||
@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);
|
||||||
@@ -192,6 +204,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void accept(Integer id, Integer riderId, Integer tenantId) {
|
public void accept(Integer id, Integer riderId, Integer tenantId) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
throw new BusinessException("订单id不能为空");
|
throw new BusinessException("订单id不能为空");
|
||||||
@@ -204,9 +217,10 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 原子接单:避免并发抢单
|
// 原子接单:避免并发抢单
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
boolean ok = this.lambdaUpdate()
|
boolean ok = this.lambdaUpdate()
|
||||||
.set(GltTicketOrder::getRiderId, riderId)
|
.set(GltTicketOrder::getRiderId, riderId)
|
||||||
.set(GltTicketOrder::getUpdateTime, LocalDateTime.now())
|
.set(GltTicketOrder::getUpdateTime, now)
|
||||||
.eq(GltTicketOrder::getId, id)
|
.eq(GltTicketOrder::getId, id)
|
||||||
.eq(GltTicketOrder::getTenantId, tenantId)
|
.eq(GltTicketOrder::getTenantId, tenantId)
|
||||||
.eq(GltTicketOrder::getDeleted, 0)
|
.eq(GltTicketOrder::getDeleted, 0)
|
||||||
@@ -215,6 +229,8 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
.and(w -> w.isNull(GltTicketOrder::getRiderId).or().eq(GltTicketOrder::getRiderId, 0))
|
.and(w -> w.isNull(GltTicketOrder::getRiderId).or().eq(GltTicketOrder::getRiderId, 0))
|
||||||
.update();
|
.update();
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
// 接单成功后,同步商城订单发货状态:10未发货 -> 20已发货
|
||||||
|
updateShopOrderDeliveryStatusAfterAccept(id, tenantId, riderId, now);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,6 +249,146 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
throw new BusinessException("订单状态不允许接单");
|
throw new BusinessException("订单状态不允许接单");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markShopOrderShippedAfterRiderAssigned(Integer ticketOrderId, Integer tenantId, Integer riderId) {
|
||||||
|
updateShopOrderDeliveryStatusAfterAccept(ticketOrderId, tenantId, riderId, LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markShopOrderCompletedAfterTicketFinished(Integer ticketOrderId, Integer tenantId) {
|
||||||
|
updateShopOrderOrderStatusAfterTicketFinished(ticketOrderId, tenantId, LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateShopOrderDeliveryStatusAfterAccept(Integer ticketOrderId, Integer tenantId, Integer riderId, LocalDateTime now) {
|
||||||
|
if (ticketOrderId == null || tenantId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到关联水票的商城订单(glt_user_ticket.orderId / orderNo)
|
||||||
|
GltTicketOrder ticketOrder = this.lambdaQuery()
|
||||||
|
.select(GltTicketOrder::getId, GltTicketOrder::getUserTicketId, GltTicketOrder::getRiderId)
|
||||||
|
.eq(GltTicketOrder::getId, ticketOrderId)
|
||||||
|
.eq(GltTicketOrder::getTenantId, tenantId)
|
||||||
|
.eq(GltTicketOrder::getDeleted, 0)
|
||||||
|
.last("limit 1")
|
||||||
|
.one();
|
||||||
|
if (ticketOrder == null || ticketOrder.getUserTicketId() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer actualRiderId = (riderId != null && riderId > 0) ? riderId : ticketOrder.getRiderId();
|
||||||
|
|
||||||
|
GltUserTicket userTicket = gltUserTicketService.getOne(
|
||||||
|
new LambdaQueryWrapper<GltUserTicket>()
|
||||||
|
.eq(GltUserTicket::getTenantId, tenantId)
|
||||||
|
.eq(GltUserTicket::getDeleted, 0)
|
||||||
|
.eq(GltUserTicket::getId, ticketOrder.getUserTicketId())
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
if (userTicket == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer shopOrderId = userTicket.getOrderId();
|
||||||
|
String shopOrderNo = userTicket.getOrderNo();
|
||||||
|
boolean resolvedByOrderGoodsId = false;
|
||||||
|
// 兼容历史数据:部分水票可能只写了 orderGoodsId(未写 orderId/orderNo),此处兜底通过 orderGoodsId 反查 ShopOrder.orderId。
|
||||||
|
if (shopOrderId == null && !StringUtils.hasText(shopOrderNo) && userTicket.getOrderGoodsId() != null) {
|
||||||
|
ShopOrderGoods og = shopOrderGoodsService.getOne(
|
||||||
|
new LambdaQueryWrapper<ShopOrderGoods>()
|
||||||
|
.select(ShopOrderGoods::getOrderId)
|
||||||
|
.eq(ShopOrderGoods::getTenantId, tenantId)
|
||||||
|
.eq(ShopOrderGoods::getId, userTicket.getOrderGoodsId())
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
if (og != null) {
|
||||||
|
shopOrderId = og.getOrderId();
|
||||||
|
resolvedByOrderGoodsId = shopOrderId != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shopOrderId == null && !StringUtils.hasText(shopOrderNo)) {
|
||||||
|
log.warn("同步商城订单发货状态失败:未找到关联商城订单 - tenantId={}, ticketOrderId={}, userTicketId={}, userTicket.orderId={}, userTicket.orderNo={}, userTicket.orderGoodsId={}",
|
||||||
|
tenantId, ticketOrderId, userTicket.getId(), userTicket.getOrderId(), userTicket.getOrderNo(), userTicket.getOrderGoodsId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 若是通过 orderGoodsId 兜底反查到 orderId,则顺便回填 glt_user_ticket.order_id/order_no,减少后续同步/查询依赖兜底分支。
|
||||||
|
if (resolvedByOrderGoodsId && userTicket.getOrderId() == null && shopOrderId != null) {
|
||||||
|
if (!StringUtils.hasText(shopOrderNo)) {
|
||||||
|
ShopOrder order = shopOrderService.getOne(
|
||||||
|
new LambdaQueryWrapper<ShopOrder>()
|
||||||
|
.select(ShopOrder::getOrderNo)
|
||||||
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
|
.eq(ShopOrder::getDeleted, 0)
|
||||||
|
.eq(ShopOrder::getOrderId, shopOrderId)
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
if (order != null) {
|
||||||
|
shopOrderNo = order.getOrderNo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LambdaUpdateWrapper<GltUserTicket> backfill = new LambdaUpdateWrapper<GltUserTicket>()
|
||||||
|
.eq(GltUserTicket::getTenantId, tenantId)
|
||||||
|
.eq(GltUserTicket::getDeleted, 0)
|
||||||
|
.eq(GltUserTicket::getId, userTicket.getId());
|
||||||
|
backfill.set(GltUserTicket::getOrderId, shopOrderId);
|
||||||
|
if (!StringUtils.hasText(userTicket.getOrderNo()) && StringUtils.hasText(shopOrderNo)) {
|
||||||
|
backfill.set(GltUserTicket::getOrderNo, shopOrderNo);
|
||||||
|
}
|
||||||
|
backfill.set(GltUserTicket::getUpdateTime, now);
|
||||||
|
try {
|
||||||
|
gltUserTicketService.update(backfill);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("回填水票关联商城订单信息失败(不影响主流程) - tenantId={}, userTicketId={}, orderId={}, orderNo={}",
|
||||||
|
tenantId, userTicket.getId(), shopOrderId, shopOrderNo, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
|
||||||
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
|
.eq(ShopOrder::getDeleted, 0)
|
||||||
|
// deliveryStatus 已经是 20 时也可能需要补写/更新 riderId,因此条件包含 riderId 不一致的场景
|
||||||
|
.and(w -> {
|
||||||
|
w.ne(ShopOrder::getDeliveryStatus, 20).or().isNull(ShopOrder::getDeliveryStatus);
|
||||||
|
if (actualRiderId != null && actualRiderId > 0) {
|
||||||
|
w.or().ne(ShopOrder::getRiderId, actualRiderId).or().isNull(ShopOrder::getRiderId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.set(ShopOrder::getDeliveryStatus, 20)
|
||||||
|
.set(ShopOrder::getUpdateTime, now);
|
||||||
|
if (actualRiderId != null && actualRiderId > 0) {
|
||||||
|
uw.set(ShopOrder::getRiderId, actualRiderId);
|
||||||
|
}
|
||||||
|
if (shopOrderId != null) {
|
||||||
|
uw.eq(ShopOrder::getOrderId, shopOrderId);
|
||||||
|
} else {
|
||||||
|
uw.eq(ShopOrder::getOrderNo, shopOrderNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean updated = shopOrderService.update(uw);
|
||||||
|
if (updated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 幂等:若已是 20,则视为成功;否则记录日志便于排查关联关系/数据缺失
|
||||||
|
LambdaQueryWrapper<ShopOrder> qw = new LambdaQueryWrapper<ShopOrder>()
|
||||||
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
|
.eq(ShopOrder::getDeleted, 0)
|
||||||
|
.eq(ShopOrder::getDeliveryStatus, 20);
|
||||||
|
if (actualRiderId != null && actualRiderId > 0) {
|
||||||
|
qw.eq(ShopOrder::getRiderId, actualRiderId);
|
||||||
|
}
|
||||||
|
if (shopOrderId != null) {
|
||||||
|
qw.eq(ShopOrder::getOrderId, shopOrderId);
|
||||||
|
} else {
|
||||||
|
qw.eq(ShopOrder::getOrderNo, shopOrderNo);
|
||||||
|
}
|
||||||
|
if (shopOrderService.count(qw) <= 0) {
|
||||||
|
log.warn("接单/指派成功但同步商城订单发货状态/配送员失败 - tenantId={}, ticketOrderId={}, riderId={}, shopOrderId={}, shopOrderNo={}",
|
||||||
|
tenantId, ticketOrderId, actualRiderId, shopOrderId, shopOrderNo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Integer id, Integer riderId, Integer tenantId) {
|
public void start(Integer id, Integer riderId, Integer tenantId) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
@@ -310,6 +466,8 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
if (ok) {
|
if (ok) {
|
||||||
// 配送员拍照上传送达后可触发提成结算(幂等,重复调用不会重复入账)
|
// 配送员拍照上传送达后可触发提成结算(幂等,重复调用不会重复入账)
|
||||||
settleRiderCommissionIfEligible(id, tenantId, true);
|
settleRiderCommissionIfEligible(id, tenantId, true);
|
||||||
|
// 配送员确认送达后,同步商城订单为“已完成”(orderStatus=1)
|
||||||
|
updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, now);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,6 +487,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
|| order.getDeliveryStatus() == DELIVERY_STATUS_FINISHED)) {
|
|| order.getDeliveryStatus() == DELIVERY_STATUS_FINISHED)) {
|
||||||
// 幂等:重复送达视为成功
|
// 幂等:重复送达视为成功
|
||||||
settleRiderCommissionIfEligible(id, tenantId, true);
|
settleRiderCommissionIfEligible(id, tenantId, true);
|
||||||
|
updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, LocalDateTime.now());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new BusinessException("订单状态不允许确认送达");
|
throw new BusinessException("订单状态不允许确认送达");
|
||||||
@@ -362,6 +521,8 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
if (ok) {
|
if (ok) {
|
||||||
// 用户确认收货完成后触发配送员提成结算(幂等,重复调用不会重复入账)
|
// 用户确认收货完成后触发配送员提成结算(幂等,重复调用不会重复入账)
|
||||||
settleRiderCommissionIfEligible(id, tenantId, false);
|
settleRiderCommissionIfEligible(id, tenantId, false);
|
||||||
|
// 送水订单完成后,同步商城订单为“已完成”(orderStatus=1)
|
||||||
|
updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, now);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,6 +540,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
if (order.getDeliveryStatus() != null && order.getDeliveryStatus() == DELIVERY_STATUS_FINISHED) {
|
if (order.getDeliveryStatus() != null && order.getDeliveryStatus() == DELIVERY_STATUS_FINISHED) {
|
||||||
// 幂等:重复确认收货视为成功
|
// 幂等:重复确认收货视为成功
|
||||||
settleRiderCommissionIfEligible(id, tenantId, false);
|
settleRiderCommissionIfEligible(id, tenantId, false);
|
||||||
|
updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, LocalDateTime.now());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new BusinessException("订单状态不允许确认收货");
|
throw new BusinessException("订单状态不允许确认收货");
|
||||||
@@ -437,6 +599,7 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
}
|
}
|
||||||
// 超时自动确认收货后,也按“完成”逻辑触发配送员提成结算(幂等)。
|
// 超时自动确认收货后,也按“完成”逻辑触发配送员提成结算(幂等)。
|
||||||
settleRiderCommissionIfEligible(id, tenantId, false);
|
settleRiderCommissionIfEligible(id, tenantId, false);
|
||||||
|
updateShopOrderOrderStatusAfterTicketFinished(id, tenantId, nowFinal);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
if (Boolean.TRUE.equals(ok)) {
|
if (Boolean.TRUE.equals(ok)) {
|
||||||
@@ -449,6 +612,125 @@ public class GltTicketOrderServiceImpl extends ServiceImpl<GltTicketOrderMapper,
|
|||||||
return confirmed;
|
return confirmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateShopOrderOrderStatusAfterTicketFinished(Integer ticketOrderId, Integer tenantId, LocalDateTime now) {
|
||||||
|
if (ticketOrderId == null || tenantId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (now == null) {
|
||||||
|
now = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到关联水票的商城订单(glt_user_ticket.orderId / orderNo)
|
||||||
|
GltTicketOrder ticketOrder = this.lambdaQuery()
|
||||||
|
.select(GltTicketOrder::getId, GltTicketOrder::getUserTicketId)
|
||||||
|
.eq(GltTicketOrder::getId, ticketOrderId)
|
||||||
|
.eq(GltTicketOrder::getTenantId, tenantId)
|
||||||
|
.eq(GltTicketOrder::getDeleted, 0)
|
||||||
|
.last("limit 1")
|
||||||
|
.one();
|
||||||
|
if (ticketOrder == null || ticketOrder.getUserTicketId() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GltUserTicket userTicket = gltUserTicketService.getOne(
|
||||||
|
new LambdaQueryWrapper<GltUserTicket>()
|
||||||
|
.eq(GltUserTicket::getTenantId, tenantId)
|
||||||
|
.eq(GltUserTicket::getDeleted, 0)
|
||||||
|
.eq(GltUserTicket::getId, ticketOrder.getUserTicketId())
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
if (userTicket == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer shopOrderId = userTicket.getOrderId();
|
||||||
|
String shopOrderNo = userTicket.getOrderNo();
|
||||||
|
boolean resolvedByOrderGoodsId = false;
|
||||||
|
// 兼容历史数据:部分水票可能只写了 orderGoodsId(未写 orderId/orderNo),此处兜底通过 orderGoodsId 反查 ShopOrder.orderId。
|
||||||
|
if (shopOrderId == null && !StringUtils.hasText(shopOrderNo) && userTicket.getOrderGoodsId() != null) {
|
||||||
|
ShopOrderGoods og = shopOrderGoodsService.getOne(
|
||||||
|
new LambdaQueryWrapper<ShopOrderGoods>()
|
||||||
|
.select(ShopOrderGoods::getOrderId)
|
||||||
|
.eq(ShopOrderGoods::getTenantId, tenantId)
|
||||||
|
.eq(ShopOrderGoods::getId, userTicket.getOrderGoodsId())
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
if (og != null) {
|
||||||
|
shopOrderId = og.getOrderId();
|
||||||
|
resolvedByOrderGoodsId = shopOrderId != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shopOrderId == null && !StringUtils.hasText(shopOrderNo)) {
|
||||||
|
log.warn("送水订单完成但未找到关联商城订单,无法同步完成状态 - tenantId={}, ticketOrderId={}, userTicketId={}, userTicket.orderId={}, userTicket.orderNo={}, userTicket.orderGoodsId={}",
|
||||||
|
tenantId, ticketOrderId, userTicket.getId(), userTicket.getOrderId(), userTicket.getOrderNo(), userTicket.getOrderGoodsId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 若是通过 orderGoodsId 兜底反查到 orderId,则顺便回填 glt_user_ticket.order_id/order_no,减少后续同步/查询依赖兜底分支。
|
||||||
|
if (resolvedByOrderGoodsId && userTicket.getOrderId() == null && shopOrderId != null) {
|
||||||
|
if (!StringUtils.hasText(shopOrderNo)) {
|
||||||
|
ShopOrder order = shopOrderService.getOne(
|
||||||
|
new LambdaQueryWrapper<ShopOrder>()
|
||||||
|
.select(ShopOrder::getOrderNo)
|
||||||
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
|
.eq(ShopOrder::getDeleted, 0)
|
||||||
|
.eq(ShopOrder::getOrderId, shopOrderId)
|
||||||
|
.last("limit 1")
|
||||||
|
);
|
||||||
|
if (order != null) {
|
||||||
|
shopOrderNo = order.getOrderNo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LambdaUpdateWrapper<GltUserTicket> backfill = new LambdaUpdateWrapper<GltUserTicket>()
|
||||||
|
.eq(GltUserTicket::getTenantId, tenantId)
|
||||||
|
.eq(GltUserTicket::getDeleted, 0)
|
||||||
|
.eq(GltUserTicket::getId, userTicket.getId());
|
||||||
|
backfill.set(GltUserTicket::getOrderId, shopOrderId);
|
||||||
|
if (!StringUtils.hasText(userTicket.getOrderNo()) && StringUtils.hasText(shopOrderNo)) {
|
||||||
|
backfill.set(GltUserTicket::getOrderNo, shopOrderNo);
|
||||||
|
}
|
||||||
|
backfill.set(GltUserTicket::getUpdateTime, now);
|
||||||
|
try {
|
||||||
|
gltUserTicketService.update(backfill);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("回填水票关联商城订单信息失败(不影响主流程) - tenantId={}, userTicketId={}, orderId={}, orderNo={}",
|
||||||
|
tenantId, userTicket.getId(), shopOrderId, shopOrderNo, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LambdaUpdateWrapper<ShopOrder> uw = new LambdaUpdateWrapper<ShopOrder>()
|
||||||
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
|
.eq(ShopOrder::getDeleted, 0)
|
||||||
|
.and(w -> w.ne(ShopOrder::getOrderStatus, 1).or().isNull(ShopOrder::getOrderStatus))
|
||||||
|
.set(ShopOrder::getOrderStatus, 1)
|
||||||
|
.set(ShopOrder::getUpdateTime, now);
|
||||||
|
if (shopOrderId != null) {
|
||||||
|
uw.eq(ShopOrder::getOrderId, shopOrderId);
|
||||||
|
} else {
|
||||||
|
uw.eq(ShopOrder::getOrderNo, shopOrderNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean updated = shopOrderService.update(uw);
|
||||||
|
if (updated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 幂等:若已是 1,则视为成功;否则记录日志便于排查关联关系/数据缺失
|
||||||
|
LambdaQueryWrapper<ShopOrder> qw = new LambdaQueryWrapper<ShopOrder>()
|
||||||
|
.eq(ShopOrder::getTenantId, tenantId)
|
||||||
|
.eq(ShopOrder::getDeleted, 0)
|
||||||
|
.eq(ShopOrder::getOrderStatus, 1);
|
||||||
|
if (shopOrderId != null) {
|
||||||
|
qw.eq(ShopOrder::getOrderId, shopOrderId);
|
||||||
|
} else {
|
||||||
|
qw.eq(ShopOrder::getOrderNo, shopOrderNo);
|
||||||
|
}
|
||||||
|
if (shopOrderService.count(qw) <= 0) {
|
||||||
|
log.warn("送水订单完成但同步商城订单完成状态失败 - tenantId={}, ticketOrderId={}, shopOrderId={}, shopOrderNo={}",
|
||||||
|
tenantId, ticketOrderId, shopOrderId, shopOrderNo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void settleRiderCommissionIfEligible(Integer ticketOrderId, Integer tenantId, boolean requirePhoto) {
|
private void settleRiderCommissionIfEligible(Integer ticketOrderId, Integer tenantId, boolean requirePhoto) {
|
||||||
if (ticketOrderId == null || tenantId == null) {
|
if (ticketOrderId == null || tenantId == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.gxwebsoft.glt.task;
|
package com.gxwebsoft.glt.task;
|
||||||
|
|
||||||
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.common.core.annotation.IgnoreTenant;
|
import com.gxwebsoft.common.core.annotation.IgnoreTenant;
|
||||||
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
import com.gxwebsoft.glt.entity.GltTicketOrder;
|
||||||
import com.gxwebsoft.glt.entity.GltTicketTemplate;
|
import com.gxwebsoft.glt.entity.GltTicketTemplate;
|
||||||
@@ -9,9 +10,11 @@ import com.gxwebsoft.glt.service.GltTicketOrderService;
|
|||||||
import com.gxwebsoft.glt.service.GltTicketTemplateService;
|
import com.gxwebsoft.glt.service.GltTicketTemplateService;
|
||||||
import com.gxwebsoft.glt.service.GltUserTicketService;
|
import com.gxwebsoft.glt.service.GltUserTicketService;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerCapital;
|
import com.gxwebsoft.shop.entity.ShopDealerCapital;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopDealerOrder;
|
||||||
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
import com.gxwebsoft.shop.entity.ShopDealerUser;
|
||||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
import com.gxwebsoft.shop.service.ShopDealerCapitalService;
|
||||||
|
import com.gxwebsoft.shop.service.ShopDealerOrderService;
|
||||||
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
import com.gxwebsoft.shop.service.ShopDealerUserService;
|
||||||
import com.gxwebsoft.shop.service.ShopOrderService;
|
import com.gxwebsoft.shop.service.ShopOrderService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -65,6 +68,9 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
@Resource
|
@Resource
|
||||||
private ShopDealerCapitalService shopDealerCapitalService;
|
private ShopDealerCapitalService shopDealerCapitalService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ShopDealerOrderService shopDealerOrderService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ShopDealerUserService shopDealerUserService;
|
private ShopDealerUserService shopDealerUserService;
|
||||||
|
|
||||||
@@ -79,7 +85,7 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
|
|
||||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
|
|
||||||
@Scheduled(cron = "${dealer.commission.unfreeze10584.cron:0 */1 * * * ?}")
|
@Scheduled(cron = "${dealer.commission.unfreeze10584.cron:0/30 * * * * ?}")
|
||||||
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!running.compareAndSet(false, true)) {
|
if (!running.compareAndSet(false, true)) {
|
||||||
@@ -355,12 +361,56 @@ public class DealerCommissionUnfreeze10584Task {
|
|||||||
marker.setUpdateTime(now);
|
marker.setUpdateTime(now);
|
||||||
shopDealerCapitalService.save(marker);
|
shopDealerCapitalService.save(marker);
|
||||||
|
|
||||||
|
// 佣金全部解冻完成后,将分销订单状态置为“已解冻”(0)。
|
||||||
|
// 以当前任务生成的 flowType=50 marker 数量作为完成度判断,避免提前将订单置为已解冻。
|
||||||
|
setDealerOrderUnfrozenIfCompleted(orderNo, now);
|
||||||
|
|
||||||
log.info("佣金解冻成功 - tenantId={}, orderNo={}, dealerUserId={}, amount={}, capitalId={}",
|
log.info("佣金解冻成功 - tenantId={}, orderNo={}, dealerUserId={}, amount={}, capitalId={}",
|
||||||
TENANT_ID, orderNo, dealerUserId, amount, capitalId);
|
TENANT_ID, orderNo, dealerUserId, amount, capitalId);
|
||||||
return true;
|
return true;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setDealerOrderUnfrozenIfCompleted(String orderNo, LocalDateTime now) {
|
||||||
|
if (orderNo == null || orderNo.isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long totalCommissions = shopDealerCapitalService.count(
|
||||||
|
new LambdaQueryWrapper<ShopDealerCapital>()
|
||||||
|
.eq(ShopDealerCapital::getTenantId, TENANT_ID)
|
||||||
|
.eq(ShopDealerCapital::getFlowType, 10)
|
||||||
|
.eq(ShopDealerCapital::getOrderNo, orderNo)
|
||||||
|
);
|
||||||
|
if (totalCommissions <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long unfrozenMarkers = shopDealerCapitalService.count(
|
||||||
|
new LambdaQueryWrapper<ShopDealerCapital>()
|
||||||
|
.eq(ShopDealerCapital::getTenantId, TENANT_ID)
|
||||||
|
.eq(ShopDealerCapital::getFlowType, 50)
|
||||||
|
.eq(ShopDealerCapital::getOrderNo, orderNo)
|
||||||
|
.like(ShopDealerCapital::getComments, "佣金解冻(capitalId=")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (unfrozenMarkers < totalCommissions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean updated = shopDealerOrderService.update(
|
||||||
|
new LambdaUpdateWrapper<ShopDealerOrder>()
|
||||||
|
.eq(ShopDealerOrder::getTenantId, TENANT_ID)
|
||||||
|
.eq(ShopDealerOrder::getOrderNo, orderNo)
|
||||||
|
.set(ShopDealerOrder::getIsUnfreeze, 1)
|
||||||
|
.set(ShopDealerOrder::getUnfreezeTime, now)
|
||||||
|
.set(ShopDealerOrder::getUpdateTime, now)
|
||||||
|
);
|
||||||
|
if (!updated) {
|
||||||
|
log.warn("已完成佣金解冻,但更新分销订单isUnfreeze失败/无记录 - tenantId={}, orderNo={}", TENANT_ID, orderNo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String buildUnfreezeMarkerComment(Integer capitalId) {
|
private String buildUnfreezeMarkerComment(Integer capitalId) {
|
||||||
return "佣金解冻(capitalId=" + capitalId + ")";
|
return "佣金解冻(capitalId=" + capitalId + ")";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import java.util.*;
|
|||||||
/**
|
/**
|
||||||
* 租户10584:分销订单结算任务
|
* 租户10584:分销订单结算任务
|
||||||
* <p>
|
* <p>
|
||||||
* 每20秒执行一次,查询“已付款且未结算”的订单,按指定规则计算佣金并先计入分销商冻结金额(freezeMoney),并将订单置为已结算。
|
* 每10秒执行一次,查询“已付款且未结算”的订单,按指定规则计算佣金并先计入分销商冻结金额(freezeMoney),并将订单置为已结算。
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -88,9 +88,9 @@ public class DealerOrderSettlement10584Task {
|
|||||||
private UserMapper userMapper;
|
private UserMapper userMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每30秒执行一次。
|
* 每10秒执行一次。
|
||||||
*/
|
*/
|
||||||
@Scheduled(cron = "0/20 * * * * ?")
|
@Scheduled(cron = "0/10 * * * * ?")
|
||||||
@IgnoreTenant("该定时任务仅处理租户10584,但需要显式按tenantId过滤,避免定时任务线程无租户上下文导致查询异常")
|
@IgnoreTenant("该定时任务仅处理租户10584,但需要显式按tenantId过滤,避免定时任务线程无租户上下文导致查询异常")
|
||||||
public void settleTenant10584Orders() {
|
public void settleTenant10584Orders() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* GLT 套票发放任务:
|
* GLT 套票发放任务:
|
||||||
* - 每分钟扫描一次今日订单(tenantId=10584, formId in 套票模板 goodsId, payStatus=1, orderStatus=0)
|
* - 每30秒扫描一次今日订单(tenantId=10584, formId in 套票模板 goodsId, payStatus=1, orderStatus=0)
|
||||||
* - 为订单生成用户套票账户 + 释放计划(幂等)
|
* - 为订单生成用户套票账户 + 释放计划(幂等)
|
||||||
* - 若模板配置了 startSendQty,则发放时自动核销对应数量(用于“第一次送水”场景)
|
* - 若模板配置了 startSendQty,则发放时自动核销对应数量(用于“第一次送水”场景)
|
||||||
*/
|
*/
|
||||||
@@ -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 */1 * * * ?}")
|
@Scheduled(cron = "${glt.ticket.issue10584.cron:0/15 * * * * ?}")
|
||||||
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!running.compareAndSet(false, true)) {
|
if (!running.compareAndSet(false, true)) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class GltTicketOrderAutoConfirm10584Task {
|
|||||||
|
|
||||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
|
|
||||||
@Scheduled(cron = "${glt.ticket-order.auto-confirm10584.cron:0 */1 * * * ?}")
|
@Scheduled(cron = "${glt.ticket-order.auto-confirm10584.cron:0/33 * * * * ?}")
|
||||||
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;内部使用 tenantId=10584 精确过滤")
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!running.compareAndSet(false, true)) {
|
if (!running.compareAndSet(false, true)) {
|
||||||
@@ -53,4 +53,3 @@ public class GltTicketOrderAutoConfirm10584Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class GltUserTicketAutoReleaseTask {
|
|||||||
|
|
||||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
|
|
||||||
@Scheduled(cron = "${glt.ticket.auto-release.cron:0 */1 * * * ?}")
|
@Scheduled(cron = "${glt.ticket.auto-release.cron:0 */10 * * * ?}")
|
||||||
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;释放记录自带 tenantId,更新时会校验 tenantId")
|
@IgnoreTenant("定时任务无登录态,需忽略租户隔离;释放记录自带 tenantId,更新时会校验 tenantId")
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!running.compareAndSet(false, true)) {
|
if (!running.compareAndSet(false, true)) {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
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.common.core.web.BatchParam;
|
import com.gxwebsoft.shop.service.ShopUserService;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
|
||||||
import com.gxwebsoft.common.system.entity.User;
|
|
||||||
import com.gxwebsoft.shop.entity.ShopUser;
|
import com.gxwebsoft.shop.entity.ShopUser;
|
||||||
import com.gxwebsoft.shop.param.ShopUserParam;
|
import com.gxwebsoft.shop.param.ShopUserParam;
|
||||||
import com.gxwebsoft.shop.service.ShopUserService;
|
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 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;
|
||||||
@@ -21,7 +21,7 @@ import java.util.List;
|
|||||||
* 用户记录表控制器
|
* 用户记录表控制器
|
||||||
*
|
*
|
||||||
* @author 科技小王子
|
* @author 科技小王子
|
||||||
* @since 2025-10-03 13:41:09
|
* @since 2026-02-10 00:37:07
|
||||||
*/
|
*/
|
||||||
@Tag(name = "用户记录表管理")
|
@Tag(name = "用户记录表管理")
|
||||||
@RestController
|
@RestController
|
||||||
@@ -46,11 +46,12 @@ public class ShopUserController extends BaseController {
|
|||||||
return success(shopUserService.listRel(param));
|
return success(shopUserService.listRel(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "根据userId查询用户记录表")
|
@PreAuthorize("hasAuthority('shop:shopUser:list')")
|
||||||
@GetMapping("/{userId}")
|
@Operation(summary = "根据id查询用户记录表")
|
||||||
public ApiResult<ShopUser> get(@PathVariable("userId") Integer userId) {
|
@GetMapping("/{id}")
|
||||||
|
public ApiResult<ShopUser> get(@PathVariable("id") Integer id) {
|
||||||
// 使用关联查询
|
// 使用关联查询
|
||||||
return success(shopUserService.getByIdRel(userId));
|
return success(shopUserService.getByIdRel(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreAuthorize("hasAuthority('shop:shopUser:save')")
|
@PreAuthorize("hasAuthority('shop:shopUser:save')")
|
||||||
@@ -59,10 +60,10 @@ public class ShopUserController extends BaseController {
|
|||||||
@PostMapping()
|
@PostMapping()
|
||||||
public ApiResult<?> save(@RequestBody ShopUser shopUser) {
|
public ApiResult<?> save(@RequestBody ShopUser shopUser) {
|
||||||
// 记录当前登录用户id
|
// 记录当前登录用户id
|
||||||
User loginUser = getLoginUser();
|
// User loginUser = getLoginUser();
|
||||||
if (loginUser != null) {
|
// if (loginUser != null) {
|
||||||
shopUser.setUserId(loginUser.getUserId());
|
// shopUser.setUserId(loginUser.getUserId());
|
||||||
}
|
// }
|
||||||
if (shopUserService.save(shopUser)) {
|
if (shopUserService.save(shopUser)) {
|
||||||
return success("添加成功");
|
return success("添加成功");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,9 +120,15 @@ public class ShopDealerOrder implements Serializable {
|
|||||||
@Schema(description = "佣金结算(0未结算 1已结算)")
|
@Schema(description = "佣金结算(0未结算 1已结算)")
|
||||||
private Integer isSettled;
|
private Integer isSettled;
|
||||||
|
|
||||||
|
@Schema(description = "佣金冻结(1解冻中 0已解冻)")
|
||||||
|
private Integer isUnfreeze;
|
||||||
|
|
||||||
@Schema(description = "结算时间")
|
@Schema(description = "结算时间")
|
||||||
private LocalDateTime settleTime;
|
private LocalDateTime settleTime;
|
||||||
|
|
||||||
|
@Schema(description = "解冻时间")
|
||||||
|
private LocalDateTime unfreezeTime;
|
||||||
|
|
||||||
@Schema(description = "备注")
|
@Schema(description = "备注")
|
||||||
private String comments;
|
private String comments;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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.fasterxml.jackson.annotation.JsonAlias;
|
||||||
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;
|
||||||
@@ -101,6 +102,7 @@ public class ShopGoods implements Serializable {
|
|||||||
private BigDecimal secondDividend;
|
private BigDecimal secondDividend;
|
||||||
|
|
||||||
@Schema(description = "库存计算方式(10下单减库存 20付款减库存)")
|
@Schema(description = "库存计算方式(10下单减库存 20付款减库存)")
|
||||||
|
@JsonAlias({"cdeductStockType"})
|
||||||
private Integer deductStockType;
|
private Integer deductStockType;
|
||||||
|
|
||||||
@Schema(description = "交付方式(0不启用)")
|
@Schema(description = "交付方式(0不启用)")
|
||||||
|
|||||||
@@ -1,37 +1,34 @@
|
|||||||
package com.gxwebsoft.shop.entity;
|
package com.gxwebsoft.shop.entity;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import java.time.LocalDate;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
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;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import java.io.Serializable;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户记录表
|
* 用户记录表
|
||||||
*
|
*
|
||||||
* @author 科技小王子
|
* @author 科技小王子
|
||||||
* @since 2025-10-03 13:41:09
|
* @since 2026-02-10 00:37:07
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Schema(description = "用户记录表")
|
@Schema(name = "ShopUser对象", description = "用户记录表")
|
||||||
public class ShopUser implements Serializable {
|
public class ShopUser implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "id")
|
|
||||||
@TableId(value = "id", type = IdType.AUTO)
|
|
||||||
private Integer id;
|
|
||||||
|
|
||||||
@Schema(description = "用户id")
|
@Schema(description = "用户id")
|
||||||
|
@TableId(value = "user_id", type = IdType.AUTO)
|
||||||
private Integer userId;
|
private Integer userId;
|
||||||
|
|
||||||
@Schema(description = "用户类型 0个人用户 1企业用户 2其他")
|
@Schema(description = "用户类型 0普通用户 1企业用户 2特殊用户")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
@Schema(description = "账号")
|
@Schema(description = "账号")
|
||||||
@@ -71,6 +68,7 @@ public class ShopUser implements Serializable {
|
|||||||
private String idCard;
|
private String idCard;
|
||||||
|
|
||||||
@Schema(description = "出生日期")
|
@Schema(description = "出生日期")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
private LocalDate birthday;
|
private LocalDate birthday;
|
||||||
|
|
||||||
@Schema(description = "所在国家")
|
@Schema(description = "所在国家")
|
||||||
@@ -143,7 +141,7 @@ public class ShopUser implements Serializable {
|
|||||||
private Integer age;
|
private Integer age;
|
||||||
|
|
||||||
@Schema(description = "是否线下会员")
|
@Schema(description = "是否线下会员")
|
||||||
private Boolean offline;
|
private Integer offline;
|
||||||
|
|
||||||
@Schema(description = "关注数")
|
@Schema(description = "关注数")
|
||||||
private Integer followers;
|
private Integer followers;
|
||||||
@@ -178,6 +176,9 @@ public class ShopUser implements Serializable {
|
|||||||
@Schema(description = "是否管理员")
|
@Schema(description = "是否管理员")
|
||||||
private Boolean isAdmin;
|
private Boolean isAdmin;
|
||||||
|
|
||||||
|
@Schema(description = "默认账号(适用于不同租户存在相同的手机号码)")
|
||||||
|
private Boolean isDefault;
|
||||||
|
|
||||||
@Schema(description = "是否企业管理员")
|
@Schema(description = "是否企业管理员")
|
||||||
private Boolean isOrganizationAdmin;
|
private Boolean isOrganizationAdmin;
|
||||||
|
|
||||||
@@ -209,6 +210,7 @@ public class ShopUser implements Serializable {
|
|||||||
private Integer expireTime;
|
private Integer expireTime;
|
||||||
|
|
||||||
@Schema(description = "最后结算时间")
|
@Schema(description = "最后结算时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime settlementTime;
|
private LocalDateTime settlementTime;
|
||||||
|
|
||||||
@Schema(description = "资质")
|
@Schema(description = "资质")
|
||||||
@@ -246,9 +248,11 @@ public class ShopUser implements Serializable {
|
|||||||
private Integer tenantId;
|
private Integer tenantId;
|
||||||
|
|
||||||
@Schema(description = "注册时间")
|
@Schema(description = "注册时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
@Schema(description = "修改时间")
|
@Schema(description = "修改时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private LocalDateTime updateTime;
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import java.util.List;
|
|||||||
* 用户记录表Mapper
|
* 用户记录表Mapper
|
||||||
*
|
*
|
||||||
* @author 科技小王子
|
* @author 科技小王子
|
||||||
* @since 2025-10-03 13:41:09
|
* @since 2026-02-10 00:37:07
|
||||||
*/
|
*/
|
||||||
public interface ShopUserMapper extends BaseMapper<ShopUser> {
|
public interface ShopUserMapper extends BaseMapper<ShopUser> {
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,6 @@
|
|||||||
SELECT a.*
|
SELECT a.*
|
||||||
FROM shop_user a
|
FROM shop_user a
|
||||||
<where>
|
<where>
|
||||||
<if test="param.id != null">
|
|
||||||
AND a.id = #{param.id}
|
|
||||||
</if>
|
|
||||||
<if test="param.userId != null">
|
<if test="param.userId != null">
|
||||||
AND a.user_id = #{param.userId}
|
AND a.user_id = #{param.userId}
|
||||||
</if>
|
</if>
|
||||||
@@ -160,6 +157,9 @@
|
|||||||
<if test="param.isAdmin != null">
|
<if test="param.isAdmin != null">
|
||||||
AND a.is_admin = #{param.isAdmin}
|
AND a.is_admin = #{param.isAdmin}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="param.isDefault != null">
|
||||||
|
AND a.is_default = #{param.isDefault}
|
||||||
|
</if>
|
||||||
<if test="param.isOrganizationAdmin != null">
|
<if test="param.isOrganizationAdmin != null">
|
||||||
AND a.is_organization_admin = #{param.isOrganizationAdmin}
|
AND a.is_organization_admin = #{param.isOrganizationAdmin}
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
@@ -1,37 +1,32 @@
|
|||||||
package com.gxwebsoft.shop.param;
|
package com.gxwebsoft.shop.param;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import java.math.BigDecimal;
|
||||||
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 lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户记录表查询参数
|
* 用户记录表查询参数
|
||||||
*
|
*
|
||||||
* @author 科技小王子
|
* @author 科技小王子
|
||||||
* @since 2025-10-03 13:41:08
|
* @since 2026-02-10 00:37:06
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
@Schema(description = "用户记录表查询参数")
|
@Schema(name = "ShopUserParam对象", description = "用户记录表查询参数")
|
||||||
public class ShopUserParam extends BaseParam {
|
public class ShopUserParam extends BaseParam {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "id")
|
|
||||||
@QueryField(type = QueryType.EQ)
|
|
||||||
private Integer id;
|
|
||||||
|
|
||||||
@Schema(description = "用户id")
|
@Schema(description = "用户id")
|
||||||
@QueryField(type = QueryType.EQ)
|
@QueryField(type = QueryType.EQ)
|
||||||
private Integer userId;
|
private Integer userId;
|
||||||
|
|
||||||
@Schema(description = "用户类型 0个人用户 1企业用户 2其他")
|
@Schema(description = "用户类型 0普通用户 1企业用户 2特殊用户")
|
||||||
@QueryField(type = QueryType.EQ)
|
@QueryField(type = QueryType.EQ)
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
@@ -157,7 +152,7 @@ public class ShopUserParam extends BaseParam {
|
|||||||
|
|
||||||
@Schema(description = "是否线下会员")
|
@Schema(description = "是否线下会员")
|
||||||
@QueryField(type = QueryType.EQ)
|
@QueryField(type = QueryType.EQ)
|
||||||
private Boolean offline;
|
private Integer offline;
|
||||||
|
|
||||||
@Schema(description = "关注数")
|
@Schema(description = "关注数")
|
||||||
@QueryField(type = QueryType.EQ)
|
@QueryField(type = QueryType.EQ)
|
||||||
@@ -199,6 +194,10 @@ public class ShopUserParam extends BaseParam {
|
|||||||
@QueryField(type = QueryType.EQ)
|
@QueryField(type = QueryType.EQ)
|
||||||
private Boolean isAdmin;
|
private Boolean isAdmin;
|
||||||
|
|
||||||
|
@Schema(description = "默认账号(适用于不同租户存在相同的手机号码)")
|
||||||
|
@QueryField(type = QueryType.EQ)
|
||||||
|
private Boolean isDefault;
|
||||||
|
|
||||||
@Schema(description = "是否企业管理员")
|
@Schema(description = "是否企业管理员")
|
||||||
@QueryField(type = QueryType.EQ)
|
@QueryField(type = QueryType.EQ)
|
||||||
private Boolean isOrganizationAdmin;
|
private Boolean isOrganizationAdmin;
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import java.util.Map;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class OrderBusinessService {
|
public class OrderBusinessService {
|
||||||
|
private static final int DEDUCT_STOCK_TYPE_ORDER = 10; // 下单减库存
|
||||||
|
private static final int DEDUCT_STOCK_TYPE_PAY = 20; // 付款减库存
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ShopOrderService shopOrderService;
|
private ShopOrderService shopOrderService;
|
||||||
@@ -713,6 +715,15 @@ public class OrderBusinessService {
|
|||||||
*/
|
*/
|
||||||
private void deductStock(OrderCreateRequest request) {
|
private void deductStock(OrderCreateRequest request) {
|
||||||
for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) {
|
for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) {
|
||||||
|
// 付款减库存的商品:创建订单时不扣库存
|
||||||
|
ShopGoods goodsForType = shopGoodsService.getById(item.getGoodsId());
|
||||||
|
Integer deductStockType = goodsForType != null ? goodsForType.getDeductStockType() : null;
|
||||||
|
if (deductStockType != null && deductStockType == DEDUCT_STOCK_TYPE_PAY) {
|
||||||
|
log.debug("跳过下单扣库存(付款减库存) - goodsId={}, skuId={}, qty={}",
|
||||||
|
item.getGoodsId(), item.getSkuId(), item.getQuantity());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (item.getSkuId() != null) {
|
if (item.getSkuId() != null) {
|
||||||
// 多规格商品,扣减SKU库存
|
// 多规格商品,扣减SKU库存
|
||||||
ShopGoodsSku sku = shopGoodsSkuService.getById(item.getSkuId());
|
ShopGoodsSku sku = shopGoodsSkuService.getById(item.getSkuId());
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import java.util.List;
|
|||||||
* 用户记录表Service
|
* 用户记录表Service
|
||||||
*
|
*
|
||||||
* @author 科技小王子
|
* @author 科技小王子
|
||||||
* @since 2025-10-03 13:41:09
|
* @since 2026-02-10 00:37:07
|
||||||
*/
|
*/
|
||||||
public interface ShopUserService extends IService<ShopUser> {
|
public interface ShopUserService extends IService<ShopUser> {
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import java.util.List;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class OrderCancelServiceImpl implements OrderCancelService {
|
public class OrderCancelServiceImpl implements OrderCancelService {
|
||||||
|
private static final int DEDUCT_STOCK_TYPE_PAY = 20; // 付款减库存:下单不扣库存,因此未支付取消无需回退
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ShopOrderService shopOrderService;
|
private ShopOrderService shopOrderService;
|
||||||
@@ -182,6 +183,20 @@ public class OrderCancelServiceImpl implements OrderCancelService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (ShopOrderGoods orderGood : orderGoods) {
|
for (ShopOrderGoods orderGood : orderGoods) {
|
||||||
|
// 付款减库存的商品:创建订单时未扣库存,未支付取消时无需回退(避免库存被“加多”)
|
||||||
|
try {
|
||||||
|
ShopGoods goods = shopGoodsService.getById(orderGood.getGoodsId());
|
||||||
|
if (goods != null && goods.getDeductStockType() != null && goods.getDeductStockType() == DEDUCT_STOCK_TYPE_PAY) {
|
||||||
|
log.debug("跳过未支付取消的库存回退(付款减库存) - orderId={}, goodsId={}, skuId={}, qty={}",
|
||||||
|
order.getOrderId(), orderGood.getGoodsId(), orderGood.getSkuId(), orderGood.getTotalNum());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 查不到商品或查询异常时,保持原有回退逻辑,避免出现“库存无法回退”的更坏情况
|
||||||
|
log.warn("读取商品扣库存方式失败,继续执行库存回退 - orderId={}, goodsId={}",
|
||||||
|
order.getOrderId(), orderGood.getGoodsId(), e);
|
||||||
|
}
|
||||||
|
|
||||||
if (orderGood.getSkuId() != null && orderGood.getSkuId() > 0) {
|
if (orderGood.getSkuId() != null && orderGood.getSkuId() > 0) {
|
||||||
// 多规格商品,恢复SKU库存
|
// 多规格商品,恢复SKU库存
|
||||||
restoreSkuStock(orderGood);
|
restoreSkuStock(orderGood);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
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.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.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.utils.*;
|
||||||
@@ -86,10 +88,13 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
|||||||
private ShopOrderDeliveryService shopOrderDeliveryService;
|
private ShopOrderDeliveryService shopOrderDeliveryService;
|
||||||
@Resource
|
@Resource
|
||||||
private ShopExpressService shopExpressService;
|
private ShopExpressService shopExpressService;
|
||||||
|
@Resource
|
||||||
|
private ShopGoodsSkuService shopGoodsSkuService;
|
||||||
|
|
||||||
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;
|
||||||
private static final String WECHAT_PREPAY_SNAPSHOT_KEY_PREFIX = "wxpay:prepay:snapshot:";
|
private static final String WECHAT_PREPAY_SNAPSHOT_KEY_PREFIX = "wxpay:prepay:snapshot:";
|
||||||
|
private static final int DEDUCT_STOCK_TYPE_PAY = 20; // 付款减库存
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
private static class WechatPrepaySnapshot {
|
private static class WechatPrepaySnapshot {
|
||||||
@@ -769,10 +774,15 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
|||||||
try {
|
try {
|
||||||
Transaction result = service.queryOrderByOutTradeNo(queryRequest);
|
Transaction result = service.queryOrderByOutTradeNo(queryRequest);
|
||||||
if (result.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {
|
if (result.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {
|
||||||
|
if (Boolean.TRUE.equals(shopOrder.getPayStatus())) {
|
||||||
|
// 已是支付成功状态,避免重复触发支付成功后的业务逻辑(销量/库存等)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
shopOrder.setPayStatus(true);
|
shopOrder.setPayStatus(true);
|
||||||
shopOrder.setPayTime(LocalDateTime.now());
|
shopOrder.setPayTime(LocalDateTime.now());
|
||||||
shopOrder.setTransactionId(result.getTransactionId());
|
shopOrder.setTransactionId(result.getTransactionId());
|
||||||
updateById(shopOrder);
|
updateById(shopOrder);
|
||||||
|
handlePaymentSuccess(shopOrder);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (ServiceException e) {
|
} catch (ServiceException e) {
|
||||||
@@ -801,6 +811,9 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
|||||||
*/
|
*/
|
||||||
private void handlePaymentSuccess(ShopOrder order) {
|
private void handlePaymentSuccess(ShopOrder order) {
|
||||||
try {
|
try {
|
||||||
|
// 0. 付款减库存:支付成功后扣库存(下单时不扣)
|
||||||
|
deductStockAfterPaidIfNeeded(order);
|
||||||
|
|
||||||
// 1. 使用优惠券
|
// 1. 使用优惠券
|
||||||
if (order.getCouponId() != null && order.getCouponId() > 0) {
|
if (order.getCouponId() != null && order.getCouponId() > 0) {
|
||||||
markCouponAsUsed(order);
|
markCouponAsUsed(order);
|
||||||
@@ -816,6 +829,90 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 付款减库存:支付成功后按商品设置扣减库存。
|
||||||
|
*
|
||||||
|
* 注意:此处使用忽略租户隔离 + 显式 tenantId 条件,避免支付回调没有 tenantId header 时更新失败。
|
||||||
|
* 若扣减失败(库存不足/数据缺失),仅记录日志,不阻断支付回调主流程。
|
||||||
|
*/
|
||||||
|
private void deductStockAfterPaidIfNeeded(ShopOrder order) {
|
||||||
|
if (order == null || order.getOrderId() == null || order.getTenantId() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
|
||||||
|
if (CollectionUtils.isEmpty(orderGoodsList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TenantContext.runIgnoreTenant(() -> {
|
||||||
|
for (ShopOrderGoods og : orderGoodsList) {
|
||||||
|
if (og == null || og.getGoodsId() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int qty = og.getTotalNum() == null ? 0 : og.getTotalNum();
|
||||||
|
if (qty <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShopGoods goods = shopGoodsService.getById(og.getGoodsId());
|
||||||
|
Integer deductStockType = goods != null ? goods.getDeductStockType() : null;
|
||||||
|
if (deductStockType == null || deductStockType != DEDUCT_STOCK_TYPE_PAY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (og.getSkuId() != null && og.getSkuId() > 0) {
|
||||||
|
// 多规格:扣 SKU 库存
|
||||||
|
boolean updated = shopGoodsSkuService.update(
|
||||||
|
new LambdaUpdateWrapper<ShopGoodsSku>()
|
||||||
|
.eq(ShopGoodsSku::getId, og.getSkuId())
|
||||||
|
.eq(ShopGoodsSku::getTenantId, order.getTenantId())
|
||||||
|
.apply("IFNULL(stock,0) >= {0}", qty)
|
||||||
|
.setSql("stock = IFNULL(stock,0) - " + qty)
|
||||||
|
);
|
||||||
|
if (!updated) {
|
||||||
|
log.warn("支付成功后扣SKU库存失败 - tenantId={}, orderId={}, skuId={}, goodsId={}, qty={}",
|
||||||
|
order.getTenantId(), order.getOrderId(), og.getSkuId(), og.getGoodsId(), qty);
|
||||||
|
// 兜底:库存不足时至少将库存置0,避免出现“明明已售出但库存仍大于0”的情况
|
||||||
|
shopGoodsSkuService.update(
|
||||||
|
new LambdaUpdateWrapper<ShopGoodsSku>()
|
||||||
|
.eq(ShopGoodsSku::getId, og.getSkuId())
|
||||||
|
.eq(ShopGoodsSku::getTenantId, order.getTenantId())
|
||||||
|
.apply("IFNULL(stock,0) < {0}", qty)
|
||||||
|
.setSql("stock = 0")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单规格:扣商品库存
|
||||||
|
boolean updated = shopGoodsService.update(
|
||||||
|
new LambdaUpdateWrapper<ShopGoods>()
|
||||||
|
.eq(ShopGoods::getGoodsId, og.getGoodsId())
|
||||||
|
.eq(ShopGoods::getTenantId, order.getTenantId())
|
||||||
|
.apply("IFNULL(stock,0) >= {0}", qty)
|
||||||
|
.setSql("stock = IFNULL(stock,0) - " + qty)
|
||||||
|
);
|
||||||
|
if (!updated) {
|
||||||
|
log.warn("支付成功后扣商品库存失败 - tenantId={}, orderId={}, goodsId={}, qty={}",
|
||||||
|
order.getTenantId(), order.getOrderId(), og.getGoodsId(), qty);
|
||||||
|
// 兜底:库存不足时至少将库存置0,避免出现“明明已售出但库存仍大于0”的情况
|
||||||
|
shopGoodsService.update(
|
||||||
|
new LambdaUpdateWrapper<ShopGoods>()
|
||||||
|
.eq(ShopGoods::getGoodsId, og.getGoodsId())
|
||||||
|
.eq(ShopGoods::getTenantId, order.getTenantId())
|
||||||
|
.apply("IFNULL(stock,0) < {0}", qty)
|
||||||
|
.setSql("stock = 0")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("支付成功后扣库存异常 - tenantId={}, orderId={}, goodsId={}, skuId={}, qty={}",
|
||||||
|
order.getTenantId(), order.getOrderId(), og.getGoodsId(), og.getSkuId(), qty, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标记优惠券为已使用
|
* 标记优惠券为已使用
|
||||||
*/
|
*/
|
||||||
@@ -1452,7 +1549,6 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean syncPaymentStatus(String orderNo, Integer paymentStatus, String transactionId, String payTime, Integer tenantId) {
|
public boolean syncPaymentStatus(String orderNo, Integer paymentStatus, String transactionId, String payTime, Integer tenantId) {
|
||||||
try {
|
|
||||||
// 查询订单
|
// 查询订单
|
||||||
ShopOrder order = getByOrderNo(orderNo, tenantId);
|
ShopOrder order = getByOrderNo(orderNo, tenantId);
|
||||||
if (order == null) {
|
if (order == null) {
|
||||||
@@ -1461,7 +1557,7 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果订单已经是支付成功状态,不需要更新
|
// 如果订单已经是支付成功状态,不需要更新
|
||||||
if (order.getPayStatus() && paymentStatus == 1) {
|
if (Boolean.TRUE.equals(order.getPayStatus()) && paymentStatus == 1) {
|
||||||
log.info("订单已经是支付成功状态,无需更新: orderNo={}", orderNo);
|
log.info("订单已经是支付成功状态,无需更新: orderNo={}", orderNo);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1479,16 +1575,15 @@ public class ShopOrderServiceImpl extends ServiceImpl<ShopOrderMapper, ShopOrder
|
|||||||
if (updated) {
|
if (updated) {
|
||||||
log.info("订单支付状态同步成功: orderNo={}, paymentStatus={}, transactionId={}",
|
log.info("订单支付状态同步成功: orderNo={}, paymentStatus={}, transactionId={}",
|
||||||
orderNo, paymentStatus, transactionId);
|
orderNo, paymentStatus, transactionId);
|
||||||
|
// paymentStatus=1时,同步触发支付成功后业务逻辑(包括付款减库存/销量等)
|
||||||
|
if (paymentStatus != null && paymentStatus == 1) {
|
||||||
|
handlePaymentSuccess(order);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.warn("订单支付状态同步失败: orderNo={}, paymentStatus={}", orderNo, paymentStatus);
|
log.warn("订单支付状态同步失败: orderNo={}, paymentStatus={}", orderNo, paymentStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
return updated;
|
return updated;
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("同步订单支付状态异常: orderNo={}, error={}", orderNo, e.getMessage(), e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.gxwebsoft.shop.service.impl;
|
package com.gxwebsoft.shop.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.gxwebsoft.shop.mapper.ShopUserMapper;
|
||||||
|
import com.gxwebsoft.shop.service.ShopUserService;
|
||||||
|
import com.gxwebsoft.shop.entity.ShopUser;
|
||||||
|
import com.gxwebsoft.shop.param.ShopUserParam;
|
||||||
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.shop.entity.ShopUser;
|
|
||||||
import com.gxwebsoft.shop.mapper.ShopUserMapper;
|
|
||||||
import com.gxwebsoft.shop.param.ShopUserParam;
|
|
||||||
import com.gxwebsoft.shop.service.ShopUserService;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -15,7 +15,7 @@ import java.util.List;
|
|||||||
* 用户记录表Service实现
|
* 用户记录表Service实现
|
||||||
*
|
*
|
||||||
* @author 科技小王子
|
* @author 科技小王子
|
||||||
* @since 2025-10-03 13:41:09
|
* @since 2026-02-10 00:37:07
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class ShopUserServiceImpl extends ServiceImpl<ShopUserMapper, ShopUser> implements ShopUserService {
|
public class ShopUserServiceImpl extends ServiceImpl<ShopUserMapper, ShopUser> implements ShopUserService {
|
||||||
|
|||||||
Reference in New Issue
Block a user