list);
}
diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/mapper/SysUserRoleMapper.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/mapper/SysUserRoleMapper.java
index a95c6542..ccc2b8d3 100644
--- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/mapper/SysUserRoleMapper.java
+++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/mapper/SysUserRoleMapper.java
@@ -21,7 +21,11 @@ package com.bsd.pjcloud.admin.mapper;
import com.bsd.pjcloud.admin.api.entity.SysUserRole;
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
+import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
/**
*
@@ -34,4 +38,15 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysUserRoleMapper extends PjcloudBaseMapper {
+ /** AI 喂数:多行插入用户角色 */
+ @Insert("""
+
+ """)
+ int insertAiFeedBatch(@Param("list") List list);
+
}
diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysUserService.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysUserService.java
index d4ac5c09..b828b9b2 100644
--- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysUserService.java
+++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/SysUserService.java
@@ -159,6 +159,26 @@ public interface SysUserService extends IService {
*/
R lockUser(String username);
+ /**
+ * AI 喂数清理:软删用户及关联角色/身份/关系/钱包(不校验余额与流水,演示数据专用)。
+ *
+ * @param userIds 用户 ID 列表
+ * @return 是否成功
+ */
+ R aiFeedDeleteUsers(List userIds);
+
+ /**
+ * AI 喂数专用:按手机号创建用户(独立实现,不走 saveUser/doSaveUser)。
+ * 固定 GENERAL_USER + WALLET_AUTO_CREATE;已存在则返回原 userId;不发异步开钱包事件。
+ */
+ Long aiFeedCreateOrGetUser(String phone, String name, Long deptId);
+
+ /**
+ * AI 喂数批量建用户(独立实现)。同批复用同一密码哈希,已存在手机号直接复用。
+ */
+ com.bsd.pjcloud.admin.api.dto.AiFeedUserBatchResultDTO aiFeedCreateUsersBatch(
+ java.util.List users);
+
/**
* 修改密码
*
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 13e502b0..35873a60 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
@@ -38,6 +38,8 @@ import com.bsd.pjcloud.admin.api.constant.UserExpireEnum;
import com.bsd.pjcloud.admin.api.constant.UserStaffingTypeEnum;
import com.bsd.pjcloud.admin.api.constant.UserStateEnum;
import com.bsd.pjcloud.admin.api.constant.user.UserIdentityTypeEnum;
+import com.bsd.pjcloud.admin.api.dto.AiFeedUserBatchResultDTO;
+import com.bsd.pjcloud.admin.api.dto.AiFeedUserCreateDTO;
import com.bsd.pjcloud.admin.api.dto.ForgotPasswordDTO;
import com.bsd.pjcloud.admin.api.dto.SysUserFaceDto;
import com.bsd.pjcloud.admin.api.dto.UserDTO;
@@ -215,6 +217,129 @@ public class SysUserServiceImpl extends ServiceImpl impl
}
}
+ /** 演示用户统一初始密码(仅喂数路径),整批只 BCrypt 一次 */
+ private static final String AI_FEED_DEFAULT_PASSWORD = "AiFeed@123456";
+
+ /**
+ * AI 喂数专用建用户(独立实现,不改动 {@link #doSaveUser} 业务路径)。
+ * 固定 GENERAL_USER + WALLET_AUTO_CREATE;不发异步开钱包事件(钱包由喂数侧同步开通)。
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long aiFeedCreateOrGetUser(String phone, String name, Long deptId) {
+ AiFeedUserCreateDTO dto = new AiFeedUserCreateDTO();
+ dto.setPhone(phone);
+ dto.setName(name);
+ dto.setDeptId(deptId);
+ AiFeedUserBatchResultDTO batch = aiFeedCreateUsersBatch(List.of(dto));
+ if (batch == null || CollUtil.isEmpty(batch.getItems()) || batch.getItems().get(0).getUserId() == null) {
+ throw new IllegalArgumentException("创建用户失败");
+ }
+ return batch.getItems().get(0).getUserId();
+ }
+
+ /**
+ * AI 喂数批量建用户:一次查已存在手机号,整批共用密码哈希,用户/角色一次性 saveBatch。
+ * name 为空时仅按手机号查已有用户(订单行引用已导入用户),不新建。
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public AiFeedUserBatchResultDTO aiFeedCreateUsersBatch(List users) {
+ AiFeedUserBatchResultDTO result = new AiFeedUserBatchResultDTO();
+ if (CollUtil.isEmpty(users)) {
+ return result;
+ }
+ // 同批内手机号去重,后者覆盖前者姓名/部门
+ Map unique = new LinkedHashMap<>();
+ for (AiFeedUserCreateDTO u : users) {
+ if (u == null || StrUtil.isBlank(u.getPhone())) {
+ continue;
+ }
+ AiFeedUserCreateDTO copy = new AiFeedUserCreateDTO();
+ copy.setPhone(StrUtil.trim(u.getPhone()));
+ copy.setName(StrUtil.isBlank(u.getName()) ? null : StrUtil.trim(u.getName()));
+ copy.setDeptId(u.getDeptId());
+ unique.put(copy.getPhone(), copy);
+ }
+ if (unique.isEmpty()) {
+ return result;
+ }
+ List phones = new ArrayList<>(unique.keySet());
+ Map existMap = baseMapper.selectList(Wrappers.lambdaQuery().in(SysUser::getPhone, phones))
+ .stream()
+ .collect(Collectors.toMap(SysUser::getPhone, u -> u, (a, b) -> a));
+
+ List toCreate = new ArrayList<>();
+ List pendingCreated = new ArrayList<>();
+ LocalDateTime now = LocalDateTime.now();
+ for (AiFeedUserCreateDTO u : unique.values()) {
+ AiFeedUserBatchResultDTO.Item item = new AiFeedUserBatchResultDTO.Item();
+ item.setPhone(u.getPhone());
+ SysUser exist = existMap.get(u.getPhone());
+ if (exist != null) {
+ item.setUserId(exist.getUserId());
+ item.setCreated(Boolean.FALSE);
+ result.getItems().add(item);
+ continue;
+ }
+ if (StrUtil.isBlank(u.getName()) || u.getDeptId() == null) {
+ continue;
+ }
+ SysUser sysUser = new SysUser();
+ // 预分配雪花 ID,便于随后批量写角色关系
+ sysUser.setUserId(com.baomidou.mybatisplus.core.toolkit.IdWorker.getId());
+ sysUser.setUsername(u.getPhone());
+ sysUser.setPhone(u.getPhone());
+ sysUser.setName(u.getName());
+ sysUser.setNickname(u.getName());
+ sysUser.setDeptId(u.getDeptId());
+ sysUser.setLockFlag(UserStateEnum.NORMAL.getCode());
+ sysUser.setProcessStatus(TaskCheckStatusEnum.RESULT.getCode());
+ sysUser.setProcessTime(now);
+ sysUser.setCreateBy(u.getPhone());
+ sysUser.setUpdateBy(u.getPhone());
+ sysUser.setPasswordModifyTime(now);
+ sysUser.setPasswordExpireFlag(UserExpireEnum.EXPIRE.getCode());
+ toCreate.add(sysUser);
+ item.setUserId(sysUser.getUserId());
+ item.setCreated(Boolean.TRUE);
+ pendingCreated.add(item);
+ }
+ if (!toCreate.isEmpty()) {
+ List roleIds = getNonAdminNewUserRoleIds();
+ String encodedPwd = ENCODER.encode(AI_FEED_DEFAULT_PASSWORD);
+ Long tenantId = TenantContextHolder.getTenantId();
+ LocalDateTime createTime = LocalDateTime.now();
+ for (SysUser sysUser : toCreate) {
+ sysUser.setPassword(encodedPwd);
+ sysUser.setDelFlag(CommonConstants.STATUS_NORMAL);
+ sysUser.setTenantId(tenantId);
+ sysUser.setCreateTime(createTime);
+ sysUser.setUpdateTime(createTime);
+ }
+ // 多行 INSERT,避免无 rewriteBatchedStatements 时 saveBatch 仍接近逐条写库
+ for (List chunk : CollUtil.split(toCreate, 200)) {
+ baseMapper.insertAiFeedBatch(chunk);
+ }
+ List roles = new ArrayList<>(toCreate.size() * Math.max(roleIds.size(), 1));
+ for (SysUser sysUser : toCreate) {
+ for (Long roleId : roleIds) {
+ SysUserRole userRole = new SysUserRole();
+ userRole.setUserId(sysUser.getUserId());
+ userRole.setRoleId(roleId);
+ roles.add(userRole);
+ }
+ }
+ if (!roles.isEmpty()) {
+ for (List chunk : CollUtil.split(roles, 500)) {
+ sysUserRoleMapper.insertAiFeedBatch(chunk);
+ }
+ }
+ result.getItems().addAll(pendingCreated);
+ }
+ return result;
+ }
+
/**
* 解析当前操作人的角色上下文,供一次或一批用户创建复用。
*
@@ -970,6 +1095,51 @@ public class SysUserServiceImpl extends ServiceImpl impl
return R.ok();
}
+ /**
+ * AI 喂数清理:软删演示用户。跳过钱包余额/流水校验,避免充值后无法清理。
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public R aiFeedDeleteUsers(List userIds) {
+ if (CollUtil.isEmpty(userIds)) {
+ return R.ok(true);
+ }
+ List distinctIds = userIds.stream().filter(Objects::nonNull).distinct().toList();
+ if (CollUtil.isEmpty(distinctIds)) {
+ return R.ok(true);
+ }
+ List userList = baseMapper.selectBatchIds(distinctIds);
+ if (CollUtil.isEmpty(userList)) {
+ return R.ok(true);
+ }
+ Cache cache = cacheManager.getCache(CacheConstants.USER_DETAILS);
+ for (SysUser sysUser : userList) {
+ if (cache != null && StrUtil.isNotBlank(sysUser.getUsername())) {
+ cache.evict(sysUser.getUsername());
+ }
+ }
+ List existIds = userList.stream().map(SysUser::getUserId).toList();
+ sysUserRoleMapper.delete(Wrappers.lambdaQuery().in(SysUserRole::getUserId, existIds));
+ sysUserRelationService.deleteUserRelationByUserIds(existIds);
+ sysUserIdentityService.deleteUserIdentityByUserIds(existIds);
+ this.removeBatchByIds(existIds);
+ try {
+ remoteWalletService.delWallet(existIds);
+ }
+ catch (Exception ex) {
+ log.warn("AI喂数清理钱包失败 userIds={}", existIds, ex);
+ }
+ for (Long userId : existIds) {
+ try {
+ remoteWalletService.userChange(userId, Boolean.TRUE);
+ }
+ catch (Exception ex) {
+ log.warn("AI喂数清理钱包同步失败 userId={}", userId, ex);
+ }
+ }
+ return R.ok(true);
+ }
+
@Override
@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username")
public R changePassword(UserDTO userDto) {
diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/resources/mapper/SysUserMapper.xml b/pjcloud-upms/pjcloud-upms-biz/src/main/resources/mapper/SysUserMapper.xml
index edd7d48b..600e94c9 100644
--- a/pjcloud-upms/pjcloud-upms-biz/src/main/resources/mapper/SysUserMapper.xml
+++ b/pjcloud-upms/pjcloud-upms-biz/src/main/resources/mapper/SysUserMapper.xml
@@ -562,4 +562,22 @@
order by a.dept_id
+
+
+ INSERT INTO sys_user (
+ user_id, username, password, phone, name, nickname, dept_id,
+ lock_flag, password_expire_flag, password_modify_time,
+ process_status, process_time, create_by, update_by, create_time, update_time,
+ del_flag, tenant_id
+ ) VALUES
+
+ (
+ #{u.userId}, #{u.username}, #{u.password}, #{u.phone}, #{u.name}, #{u.nickname}, #{u.deptId},
+ #{u.lockFlag}, #{u.passwordExpireFlag}, #{u.passwordModifyTime},
+ #{u.processStatus}, #{u.processTime}, #{u.createBy}, #{u.updateBy}, #{u.createTime}, #{u.updateTime},
+ #{u.delFlag}, #{u.tenantId}
+ )
+
+
+
diff --git a/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/feign/RemoteWalletService.java b/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/feign/RemoteWalletService.java
index 28219d79..92e43ed8 100644
--- a/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/feign/RemoteWalletService.java
+++ b/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/feign/RemoteWalletService.java
@@ -25,6 +25,17 @@ public interface RemoteWalletService {
@GetMapping("/wallet-manage/get-or-create-wallet")
R getOrCreateWallet(@RequestParam Long userId, @RequestParam(required = false) WalletTypeEnum walletTypeCode);
+ /** AI 喂数:确保饭卡钱包(免角色校验、免绑卡) */
+ @NoToken
+ @GetMapping("/wallet-manage/inner/ai-feed-ensure-eat")
+ R aiFeedEnsureEatWallet(@RequestParam Long userId);
+
+ /** AI 喂数:批量开饭卡并设定余额 */
+ @NoToken
+ @PostMapping("/wallet-manage/inner/ai-feed-ensure-eat-batch")
+ R aiFeedEnsureEatWalletsBatch(
+ @RequestBody java.util.List reqs);
+
@NoToken
@GetMapping("/wallet-manage/get-wallet")
R getWallet(@RequestParam Long userId, @RequestParam WalletTypeEnum walletTypeCode);
diff --git a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/WalletManageController.java b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/WalletManageController.java
index d6fb7a5c..ea195eeb 100644
--- a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/WalletManageController.java
+++ b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/controller/WalletManageController.java
@@ -80,6 +80,25 @@ public class WalletManageController {
return R.ok(wallet);
}
+ /** AI 喂数:确保饭卡钱包(免角色校验、免绑卡) */
+ @Inner
+ @GetMapping("/inner/ai-feed-ensure-eat")
+ public R aiFeedEnsureEatWallet(@RequestParam Long userId) {
+ WalletManageEntity wallet = walletManageService.aiFeedEnsureEatWallet(userId);
+ if (ObjectUtil.isEmpty(wallet)) {
+ return R.failed("开通饭卡钱包失败");
+ }
+ return R.ok(wallet);
+ }
+
+ /** AI 喂数:批量开饭卡并设定余额 */
+ @Inner
+ @PostMapping("/inner/ai-feed-ensure-eat-batch")
+ public R aiFeedEnsureEatWalletsBatch(
+ @RequestBody List reqs) {
+ return R.ok(walletManageService.aiFeedEnsureEatWalletsBatch(reqs));
+ }
+
@Inner
@GetMapping("/get-wallet-by-wallet-id")
public R getWalletByWalletTypeId(@RequestParam Long userId, @RequestParam Long walletTypeId) {
diff --git a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/mapper/WalletManageMapper.java b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/mapper/WalletManageMapper.java
index b3f87d63..c87e7ec5 100644
--- a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/mapper/WalletManageMapper.java
+++ b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/mapper/WalletManageMapper.java
@@ -25,6 +25,27 @@ import java.util.List;
@Mapper
public interface WalletManageMapper extends PjcloudBaseMapper {
+ /**
+ * AI 喂数:多行插入饭卡钱包(自行带 tenant_id)
+ */
+ @com.baomidou.mybatisplus.annotation.InterceptorIgnore(tenantLine = "true")
+ @org.apache.ibatis.annotations.Insert("""
+
+ """)
+ int insertAiFeedBatch(@Param("list") List list);
+
/**
* 构建钱包账户通用筛选条件,并以钱包主键作为最终排序条件保证分片导出顺序稳定。
*
diff --git a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/WalletManageService.java b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/WalletManageService.java
index 42430b34..a1cd8351 100644
--- a/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/WalletManageService.java
+++ b/pjcloud-wallet/pjcloud-wallet-biz/src/main/java/com/bsd/pjcloud/wallet/service/WalletManageService.java
@@ -55,6 +55,20 @@ public interface WalletManageService extends IService {
*/
WalletManageEntity getOrCreateWallet(@NotNull Long userId, @NotNull WalletTypeEnum walletTypeCode);
+ /**
+ * AI 喂数:确保饭卡钱包存在。跳过 WALLET_AUTO_CREATE 角色校验,且不自动绑卡(避免抢卡锁拖慢导入)。
+ *
+ * @param userId 用户 ID
+ * @return 饭卡钱包;用户不存在或类型未配置时返回 null
+ */
+ WalletManageEntity aiFeedEnsureEatWallet(@NotNull Long userId);
+
+ /**
+ * AI 喂数批量:确保饭卡并直接设定余额(不走充值流水,演示加速)。
+ */
+ com.bsd.pjcloud.wallet.api.dto.AiFeedWalletEnsureResultDTO aiFeedEnsureEatWalletsBatch(
+ java.util.List reqs);
+
/**
* 获取钱包(没有缓存)
*/
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 38307d0e..fe247c55 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
@@ -60,7 +60,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
+import java.time.LocalDateTime;
import java.util.*;
+import java.util.stream.Collectors;
/**
* 钱包管理
@@ -218,6 +220,165 @@ public class WalletManageServiceImpl extends ServiceImpl历史演示导入可能留下同用户多条饭卡钱包;此处用 list+取最早一条,
+ * 避免调用业务 {@link #getWallet} 的 selectOne 在重复数据上直接报错。
+ */
+ @Override
+ public WalletManageEntity aiFeedEnsureEatWallet(Long userId) {
+ if (userId == null) {
+ return null;
+ }
+ WalletTypeEntity eatType = walletTypeMapper.selectOne(new LambdaQueryWrapper()
+ .eq(WalletTypeEntity::getUniqueCode, WalletTypeEnum.EAT.getCode())
+ .last("LIMIT 1"));
+ if (ObjectUtil.isEmpty(eatType)) {
+ log.warn("|{}|AI喂数开通饭卡失败,userId:{},EAT钱包类型未配置", TAG, userId);
+ return null;
+ }
+ WalletManageEntity existing = findFirstEatWallet(userId, eatType.getId());
+ if (ObjectUtil.isNotEmpty(existing)) {
+ return existing;
+ }
+ UserInfo user = userService.getInnerByUserId(userId).getData();
+ if (ObjectUtil.isEmpty(user) || ObjectUtil.isEmpty(user.getSysUser())) {
+ log.warn("|{}|AI喂数开通饭卡失败,userId:{},用户不存在", TAG, userId);
+ return null;
+ }
+ Long deptId = user.getSysUser().getDeptId();
+ WalletManageEntity insertObj = new WalletManageEntity();
+ insertObj.setUserId(userId);
+ insertObj.setWalletTypeId(eatType.getId());
+ insertObj.setDeptId(deptId);
+ try {
+ baseMapper.insert(insertObj);
+ return insertObj;
+ }
+ catch (Exception ex) {
+ WalletManageEntity again = findFirstEatWallet(userId, eatType.getId());
+ if (ObjectUtil.isNotEmpty(again)) {
+ return again;
+ }
+ log.warn("|{}|AI喂数开通饭卡失败,userId:{},msg:{}", TAG, userId, ex.getMessage());
+ return null;
+ }
+ }
+
+ /** AI 喂数内部:同用户多条饭卡时取最早一条,不抛 TooManyResultsException */
+ private WalletManageEntity findFirstEatWallet(Long userId, Long eatTypeId) {
+ List list = baseMapper.selectList(new LambdaQueryWrapper()
+ .eq(WalletManageEntity::getUserId, userId)
+ .eq(WalletManageEntity::getWalletTypeId, eatTypeId)
+ .orderByAsc(WalletManageEntity::getId)
+ .last("LIMIT 1"));
+ return ObjectUtil.isEmpty(list) ? null : list.get(0);
+ }
+
+ /**
+ * AI 喂数批量开饭卡:一次查类型、一次查出已有钱包,缺失则插入,并按需直接写余额。
+ */
+ @Override
+ public com.bsd.pjcloud.wallet.api.dto.AiFeedWalletEnsureResultDTO aiFeedEnsureEatWalletsBatch(
+ List reqs) {
+ com.bsd.pjcloud.wallet.api.dto.AiFeedWalletEnsureResultDTO result =
+ new com.bsd.pjcloud.wallet.api.dto.AiFeedWalletEnsureResultDTO();
+ if (ObjectUtil.isEmpty(reqs)) {
+ return result;
+ }
+ WalletTypeEntity eatType = walletTypeMapper.selectOne(new LambdaQueryWrapper()
+ .eq(WalletTypeEntity::getUniqueCode, WalletTypeEnum.EAT.getCode())
+ .last("LIMIT 1"));
+ if (ObjectUtil.isEmpty(eatType)) {
+ log.warn("|{}|AI喂数批量开饭卡失败,EAT钱包类型未配置", TAG);
+ return result;
+ }
+ List userIds = reqs.stream()
+ .filter(Objects::nonNull)
+ .map(com.bsd.pjcloud.wallet.api.dto.AiFeedWalletEnsureDTO::getUserId)
+ .filter(Objects::nonNull)
+ .distinct()
+ .toList();
+ if (userIds.isEmpty()) {
+ return result;
+ }
+ Map walletByUser = baseMapper.selectList(new LambdaQueryWrapper()
+ .in(WalletManageEntity::getUserId, userIds)
+ .eq(WalletManageEntity::getWalletTypeId, eatType.getId())
+ .orderByAsc(WalletManageEntity::getId))
+ .stream()
+ .collect(Collectors.toMap(WalletManageEntity::getUserId, w -> w, (a, b) -> a));
+
+ Map balanceByUser = new HashMap<>();
+ Map deptByUser = new HashMap<>();
+ for (com.bsd.pjcloud.wallet.api.dto.AiFeedWalletEnsureDTO req : reqs) {
+ if (req == null || req.getUserId() == null) {
+ continue;
+ }
+ if (req.getBalanceFen() != null) {
+ balanceByUser.put(req.getUserId(), Math.max(req.getBalanceFen(), 0));
+ }
+ if (req.getDeptId() != null) {
+ deptByUser.put(req.getUserId(), req.getDeptId());
+ }
+ }
+
+ List toInsert = new ArrayList<>();
+ List toUpdate = new ArrayList<>();
+ Long tenantId = TenantContextHolder.getTenantId();
+ LocalDateTime now = LocalDateTime.now();
+ for (Long userId : userIds) {
+ WalletManageEntity wallet = walletByUser.get(userId);
+ if (wallet == null) {
+ wallet = new WalletManageEntity();
+ wallet.setId(com.baomidou.mybatisplus.core.toolkit.IdWorker.getId());
+ wallet.setUserId(userId);
+ wallet.setWalletTypeId(eatType.getId());
+ wallet.setDeptId(deptByUser.get(userId));
+ Integer bal = balanceByUser.get(userId);
+ wallet.setBalance(bal == null ? 0 : bal);
+ wallet.setTotalRecharge(bal != null && bal > 0 ? bal : 0);
+ wallet.setTotalExpense(0);
+ wallet.setFreezePrice(0);
+ wallet.setDelFlag(com.bsd.pjcloud.common.core.constant.CommonConstants.STATUS_NORMAL);
+ wallet.setTenantId(tenantId);
+ wallet.setCreateBy("ai-feed");
+ wallet.setUpdateBy("ai-feed");
+ wallet.setCreateTime(now);
+ wallet.setUpdateTime(now);
+ toInsert.add(wallet);
+ walletByUser.put(userId, wallet);
+ }
+ else if (balanceByUser.containsKey(userId)) {
+ Integer bal = balanceByUser.get(userId);
+ wallet.setBalance(bal == null ? 0 : bal);
+ toUpdate.add(wallet);
+ }
+ }
+ if (!toInsert.isEmpty()) {
+ // 多行 INSERT,一次 SQL 写入,避免 saveBatch 在无 rewriteBatchedStatements 时仍接近逐条
+ for (List chunk : cn.hutool.core.collection.CollUtil.split(toInsert, 200)) {
+ baseMapper.insertAiFeedBatch(chunk);
+ }
+ }
+ // 已有饭卡不再逐条改余额(喂数侧只给新建卡写 balance)
+ if (!toUpdate.isEmpty()) {
+ this.updateBatchById(toUpdate, 100);
+ }
+ for (Long userId : userIds) {
+ WalletManageEntity wallet = walletByUser.get(userId);
+ if (wallet == null || wallet.getId() == null) {
+ continue;
+ }
+ com.bsd.pjcloud.wallet.api.dto.AiFeedWalletEnsureResultDTO.Item item =
+ new com.bsd.pjcloud.wallet.api.dto.AiFeedWalletEnsureResultDTO.Item();
+ item.setUserId(userId);
+ item.setWalletId(wallet.getId());
+ result.getItems().add(item);
+ }
+ return result;
+ }
+
@Override
public WalletManageEntity getWallet(Long userId, WalletTypeEnum walletTypeCode) {
WalletTypeEntity walletType = walletTypeService.getByUniqueCode(walletTypeCode);