diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/api/feign/RemoteCanteenSpuService.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/api/feign/RemoteCanteenSpuService.java
index d13d7849d..ef7974f08 100644
--- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/api/feign/RemoteCanteenSpuService.java
+++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/api/feign/RemoteCanteenSpuService.java
@@ -23,6 +23,7 @@ package com.bsd.pjcloud.canteen.api.feign;
import com.bsd.pjcloud.canteen.dto.CanteenSpuUpdateStockReqDTO;
import com.bsd.pjcloud.canteen.entity.CanteenOnlineSpuEntity;
import com.bsd.pjcloud.canteen.entity.CanteenSpuEntity;
+import com.bsd.pjcloud.canteen.vo.app.OnlineSpuComboItemVO;
import com.bsd.pjcloud.common.core.constant.ServiceNameConstants;
import com.bsd.pjcloud.common.core.util.R;
import org.springframework.cloud.openfeign.FeignClient;
@@ -33,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
/**
* @author zjf
@@ -67,6 +69,15 @@ public interface RemoteCanteenSpuService {
@PostMapping("/canteenSpu/ad/getSpuListByIds")
R> getSpuListByIds(@RequestBody List ids);
+ /**
+ * 按套餐 SPU ID 批量查询套餐组成,用于跨服务补充只读展示数据。
+ *
+ * @param spuIds 套餐 SPU ID 列表,空列表返回空映射
+ * @return 套餐 SPU ID 与已按配置顺序排列的组成菜品映射
+ */
+ @PostMapping("/canteenSpu/ad/getComboItemsBySpuIds")
+ R
+ */
+public final class UserWalletCreateEvent {
+
+ /**
+ * 已保存的用户主键;钱包服务使用该主键回查用户及其自动创建钱包角色。
+ */
+ private final Long userId;
+
+ /**
+ * 发布事件时的租户主键;为空时沿用下游服务的默认租户处理规则。
+ */
+ private final Long tenantId;
+
+ /**
+ * 创建用户默认钱包事件。
+ *
+ * @param userId 已保存的用户主键
+ * @param tenantId 用户所属租户主键,非 Web 调用场景下允许为空
+ */
+ public UserWalletCreateEvent(Long userId, Long tenantId) {
+ this.userId = userId;
+ this.tenantId = tenantId;
+ }
+
+ /**
+ * 获取需要创建默认钱包的用户主键。
+ *
+ * @return 已保存的用户主键
+ */
+ public Long getUserId() {
+ return userId;
+ }
+
+ /**
+ * 获取发布事件时的租户主键。
+ *
+ * @return 租户主键,非 Web 调用场景下可能为空
+ */
+ public Long getTenantId() {
+ return tenantId;
+ }
+}
diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/handler/UserWalletCreateEventListener.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/handler/UserWalletCreateEventListener.java
new file mode 100644
index 000000000..1d489d853
--- /dev/null
+++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/handler/UserWalletCreateEventListener.java
@@ -0,0 +1,72 @@
+package com.bsd.pjcloud.admin.handler;
+
+import com.bsd.pjcloud.common.core.util.R;
+import com.bsd.pjcloud.common.data.tenant.TenantBroker;
+import com.bsd.pjcloud.wallet.api.feign.RemoteWalletService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.event.TransactionPhase;
+import org.springframework.transaction.event.TransactionalEventListener;
+
+/**
+ * 用户默认钱包创建事件监听器。
+ *
+ * 监听器在用户事务提交成功后才进入 Spring 管理的异步线程池,避免钱包服务回查到未提交的用户数据。
+ */
+@Slf4j
+@Component
+public class UserWalletCreateEventListener {
+
+ /**
+ * 钱包服务远程接口,用于创建默认钱包并同步用户部门等关联信息。
+ */
+ private final RemoteWalletService remoteWalletService;
+
+ /**
+ * 创建用户默认钱包事件监听器。
+ *
+ * @param remoteWalletService 钱包服务远程接口
+ */
+ public UserWalletCreateEventListener(RemoteWalletService remoteWalletService) {
+ this.remoteWalletService = remoteWalletService;
+ }
+
+ /**
+ * 在用户事务提交成功后异步创建默认钱包并通知钱包服务刷新用户信息。
+ *
+ * 无事务调用使用 fallbackExecution 直接进入异步线程;此类调用中的用户插入已按单条语句提交,
+ * 不需要等待事务同步回调。异步线程通过 TenantBroker 恢复事件发布时的租户,确保 Feign 请求携带正确租户头。
+ *
+ * @param event 用户主键及事件发布时租户信息
+ */
+ @Async
+ @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
+ public void createDefaultWallet(UserWalletCreateEvent event) {
+ try {
+ TenantBroker.runAs(event.getTenantId(), tenantId -> {
+ R> createResult = remoteWalletService.createWallet(event.getUserId());
+ if (createResult == null) {
+ log.error("用户事务提交后创建默认钱包失败,userId:{},钱包服务未返回结果", event.getUserId());
+ return;
+ }
+ if (!createResult.isOk()) {
+ log.error("用户事务提交后创建默认钱包失败,userId:{},code:{},msg:{}", event.getUserId(), createResult.getCode(), createResult.getMsg());
+ return;
+ }
+
+ R changeResult = remoteWalletService.userChange(event.getUserId(), Boolean.FALSE);
+ if (changeResult == null) {
+ log.error("默认钱包创建后同步用户信息失败,userId:{},钱包服务未返回结果", event.getUserId());
+ return;
+ }
+ if (!changeResult.isOk()) {
+ log.error("默认钱包创建后同步用户信息失败,userId:{},code:{},msg:{}", event.getUserId(), changeResult.getCode(), changeResult.getMsg());
+ }
+ });
+ } catch (Exception exception) {
+ // 异步异常无法反馈给原创建用户请求,必须保留完整堆栈供后续补偿和排查。
+ log.error("用户事务提交后调用钱包服务异常,userId:{},tenantId:{}", event.getUserId(), event.getTenantId(), exception);
+ }
+ }
+}
diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserServiceImpl.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserServiceImpl.java
index 15c64fbc7..5703c50f9 100644
--- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserServiceImpl.java
+++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserServiceImpl.java
@@ -53,6 +53,7 @@ import com.bsd.pjcloud.admin.api.entity.*;
import com.bsd.pjcloud.admin.api.entity.user.SysUserTagEntity;
import com.bsd.pjcloud.admin.api.utils.DataUtil;
import com.bsd.pjcloud.admin.api.vo.*;
+import com.bsd.pjcloud.admin.handler.UserWalletCreateEvent;
import com.bsd.pjcloud.admin.mapper.SysUserFaceMapper;
import com.bsd.pjcloud.admin.mapper.SysUserMapper;
import com.bsd.pjcloud.admin.mapper.SysUserPostMapper;
@@ -74,6 +75,7 @@ import com.bsd.pjcloud.common.core.util.MsgUtils;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.common.data.resolver.ParamResolver;
+import com.bsd.pjcloud.common.data.tenant.TenantContextHolder;
import com.bsd.pjcloud.common.excel.vo.ErrorMessage;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
@@ -91,6 +93,7 @@ import org.springframework.beans.BeanUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mock.web.MockMultipartFile;
@@ -173,6 +176,11 @@ public class SysUserServiceImpl extends ServiceImpl impl
private final FlowUtil flowUtil;
+ /**
+ * Spring 业务事件发布器,用于把钱包创建延迟到用户事务成功提交之后执行。
+ */
+ private final ApplicationEventPublisher applicationEventPublisher;
+
/**
* 申请注销用户
*/
@@ -207,6 +215,9 @@ public class SysUserServiceImpl extends ServiceImpl impl
return saveUser(userDto, true);
}
+ /**
+ * 保存用户主表、身份、岗位和角色关系,并按审核结果安排后续钱包创建。
+ */
private Long saveUser(UserDTO userDto, boolean skipAudit) {
sysUserIdentityService.validateDto(userDto);
SysUser sysUser = new SysUser();
@@ -272,15 +283,8 @@ public class SysUserServiceImpl extends ServiceImpl impl
if (isDeptAdmin && !skipAudit) {
getSelf().applyFlow(sysUser);
} else {
- // 创建用户默认钱包、保证线程header
- RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
- CompletableFuture.runAsync(() -> {
- RequestContextHolder.setRequestAttributes(requestAttributes);
- // 创建钱包
- remoteWalletService.createWallet(sysUser.getUserId());
- // 通知钱包
- remoteWalletService.userChange(sysUser.getUserId(), Boolean.FALSE);
- });
+ // 事务提交前其他连接无法读取新用户,因此仅发布事件,由监听器在提交成功后异步创建钱包。
+ applicationEventPublisher.publishEvent(new UserWalletCreateEvent(sysUser.getUserId(), TenantContextHolder.getTenantId()));
}
return sysUser.getUserId();
@@ -422,8 +426,11 @@ public class SysUserServiceImpl extends ServiceImpl impl
for (SysUser sysUser : userList) {
cache.evict(sysUser.getUsername());
}
+ // 删除角色
sysUserRoleMapper.delete(Wrappers.lambdaQuery().in(SysUserRole::getUserId, userIds));
+ // 删除关系
sysUserRelationService.deleteUserRelationByUserIds(userIds);
+ // 删除身份
sysUserIdentityService.deleteUserIdentityByUserIds(userIds);
this.removeBatchByIds(userIds);
// 删除钱包
diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImpl.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImpl.java
index 19cc4e171..b9fb90a26 100755
--- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImpl.java
+++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/user/SysUserRelationServiceImpl.java
@@ -258,14 +258,12 @@ public class SysUserRelationServiceImpl extends ServiceImpllambdaQuery()
- .eq(SysUser::getUsername, dto.getUsername()));
+ boolean usernameExists = sysUserService.exists(Wrappers.lambdaQuery().eq(SysUser::getUsername, dto.getUsername()));
if (usernameExists) {
throw new RuntimeException(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, dto.getUsername()));
}
if (StrUtil.isNotBlank(dto.getPhone())) {
- boolean phoneExists = sysUserService.exists(Wrappers.lambdaQuery()
- .eq(SysUser::getPhone, dto.getPhone()));
+ boolean phoneExists = sysUserService.exists(Wrappers.lambdaQuery().eq(SysUser::getPhone, dto.getPhone()));
if (phoneExists) {
throw new RuntimeException(MsgUtils.getMessage(ErrorCodes.SYS_USER_PHONE_EXISTING, dto.getPhone()));
}
diff --git a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/WalletActiveService.java b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/WalletActiveService.java
index e3f9c26ed..56656b15e 100644
--- a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/WalletActiveService.java
+++ b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/WalletActiveService.java
@@ -19,30 +19,21 @@ import java.util.List;
public interface WalletActiveService extends IService {
/**
* 一键开卡
- *
- * @param dto
*/
void fastCreateCard(WalletActiveDto dto);
/**
* 开卡成功的通知
- *
- * @param id 开卡id
*/
Boolean openCardSuccess(Long id);
/**
* 重新激活
- *
- * @param id 开卡id
*/
Boolean refreshAvatar(Long id);
/**
* 绑定饭卡
- *
- * @param dto
- * @return
*/
WalletActiveEntity bindCard(WalletActiveBindCardDto dto);
@@ -52,22 +43,16 @@ public interface WalletActiveService extends IService {
/**
* 取消绑定饭卡
- *
- * @param activeId 开卡id
*/
Boolean cancelAdminBindCard(Long activeId);
/**
* 卡号是否存在
- *
- * @return
*/
Boolean exitCardNumber(Long id, String CardNumber);
/**
* 更具卡号查询开卡信息
- *
- * @return
*/
WalletActiveEntity getByCardNumber(@NotEmpty String cardNumber);
@@ -90,8 +75,14 @@ public interface WalletActiveService extends IService {
*/
Long edit(WalletActiveEntity walletActive);
+ /**
+ * 根据卡号删除卡
+ */
+ void delCardByCardNumber(String cardNumber);
+
/**
* 导入开卡信息,会直接覆盖
*/
R importWalletActive(List excelVOList, BindingResult bindingResult);
+
}
\ No newline at end of file
diff --git a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/impl/WalletActiveServiceImpl.java b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/impl/WalletActiveServiceImpl.java
index dceb52cb7..cfb5dfd7b 100644
--- a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/impl/WalletActiveServiceImpl.java
+++ b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/impl/WalletActiveServiceImpl.java
@@ -339,6 +339,19 @@ public class WalletActiveServiceImpl extends ServiceImpl()
+ .eq(WalletActiveEntity::getCardNumber, cardNumber)
+ );
+ if (count <= 0) return;
+ baseMapper.delete(new LambdaQueryWrapper().eq(WalletActiveEntity::getCardNumber, cardNumber));
+ }
+
/**
* 导入开卡信息,会直接覆盖
*/
diff --git a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/impl/WalletManageServiceImpl.java b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/impl/WalletManageServiceImpl.java
index 1e3ebf0c9..9bba80d53 100644
--- a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/impl/WalletManageServiceImpl.java
+++ b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/impl/WalletManageServiceImpl.java
@@ -218,6 +218,11 @@ public class WalletManageServiceImpl extends ServiceImpl redisTemplate.opsForValue().getAndDelete(CacheConstants.WALLET_DETAIL + v.getId()));
}