|
@@ -0,0 +1,617 @@
|
|
|
+package com.zhentao.baoming.service.impl;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import com.zhentao.baoming.dto.common.PageResult;
|
|
|
+import com.zhentao.baoming.dto.order.OrderActivityDTO;
|
|
|
+import com.zhentao.baoming.dto.order.OrderActivityDetailDTO;
|
|
|
+import com.zhentao.baoming.dto.order.OrderActivityQueryDTO;
|
|
|
+import com.zhentao.baoming.mapper.AreaMapper;
|
|
|
+import com.zhentao.baoming.mapper.OrderActivityMapper;
|
|
|
+import com.zhentao.baoming.mapper.OrderSignupMapper;
|
|
|
+import com.zhentao.baoming.mapper.ServiceCategoryMapper;
|
|
|
+import com.zhentao.baoming.pojo.*;
|
|
|
+import com.zhentao.baoming.service.ActivityTagRelationService;
|
|
|
+import com.zhentao.baoming.service.OrderActivityService;
|
|
|
+import com.zhentao.baoming.service.OrderTagService;
|
|
|
+import com.zhentao.common.utils.TokenUtils;
|
|
|
+import com.zhentao.user.domain.User;
|
|
|
+import com.zhentao.user.mapper.UserMapper;
|
|
|
+import org.redisson.api.RLock;
|
|
|
+import org.redisson.api.RedissonClient;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+* @author ZhuanZ(无密码)
|
|
|
+* @description 针对表【order_activity(点单活动表)】的数据库操作Service实现
|
|
|
+* @createDate 2025-07-01 14:17:25
|
|
|
+*/
|
|
|
+@Service
|
|
|
+public class OrderActivityServiceImpl extends ServiceImpl<OrderActivityMapper, OrderActivity>
|
|
|
+ implements OrderActivityService {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(OrderActivityServiceImpl.class);
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ActivityTagRelationService activityTagRelationService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private OrderTagService orderTagService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private UserMapper userMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ServiceCategoryMapper serviceCategoryMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AreaMapper areaMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private OrderSignupMapper orderSignupMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private StringRedisTemplate stringRedisTemplate;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedissonClient redissonClient;
|
|
|
+ @Autowired
|
|
|
+ private OrderActivityMapper orderActivityMapper;
|
|
|
+ private static final String ACTIVITY_DETAIL_KEY_PREFIX = "activity:detail:";
|
|
|
+ private static final String ACTIVITY_DETAIL_LOCK_PREFIX = "activity:detail:lock:";
|
|
|
+ private static final long CACHE_EXPIRE_TIME = 30; // 缓存时间增加到30分钟
|
|
|
+ private static final long LOCK_WAIT_TIME = 5; // 锁等待增加到5秒
|
|
|
+ private static final long LOCK_LEASE_TIME = 10; // 锁租约增加到10秒
|
|
|
+
|
|
|
+ // 添加活动列表缓存key前缀
|
|
|
+ private static final String ACTIVITY_LIST_KEY_PREFIX = "activity:list:";
|
|
|
+ private static final String ACTIVITY_LIST_LOCK_PREFIX = "activity:list:lock:";
|
|
|
+
|
|
|
+ // OrderSignup的状态常量
|
|
|
+ private static final int SIGNUP_STATUS_APPLIED = 0; // 已报名(待审核)
|
|
|
+ private static final int SIGNUP_STATUS_APPROVED = 1; // 已通过
|
|
|
+ private static final int SIGNUP_STATUS_REJECTED = 2; // 已拒绝
|
|
|
+ private static final int SIGNUP_STATUS_CANCELED = 3; // 已取消
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public OrderActivityDetailDTO getActivityDetail(Integer activityId) {
|
|
|
+ if (activityId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前登录用户ID
|
|
|
+ Long currentUserId = null;
|
|
|
+ try {
|
|
|
+ String token = TokenUtils.getTokenFromRequest();
|
|
|
+ if (token != null) {
|
|
|
+ String userIdStr = TokenUtils.getUserIdFromToken(token);
|
|
|
+ if (userIdStr != null) {
|
|
|
+ currentUserId = Long.valueOf(userIdStr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 用户未登录或token无效,忽略异常
|
|
|
+ }
|
|
|
+
|
|
|
+ // 缓存key
|
|
|
+ String cacheKey = ACTIVITY_DETAIL_KEY_PREFIX + activityId;
|
|
|
+ String userSpecificCacheKey = currentUserId != null ? cacheKey + ":" + currentUserId : cacheKey;
|
|
|
+
|
|
|
+ // 先从缓存获取,使用try-catch包裹Redis操作以避免异常影响正常流程
|
|
|
+ try {
|
|
|
+ String cacheValue = stringRedisTemplate.opsForValue().get(userSpecificCacheKey);
|
|
|
+ if (cacheValue != null && !cacheValue.isEmpty()) {
|
|
|
+ try {
|
|
|
+ return JSON.parseObject(cacheValue, OrderActivityDetailDTO.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析缓存数据失败,将重新查询数据库. key={}", userSpecificCacheKey, e);
|
|
|
+ try {
|
|
|
+ stringRedisTemplate.delete(userSpecificCacheKey);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.warn("删除失效缓存失败", ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // Redis操作异常,直接从数据库查询
|
|
|
+ log.warn("Redis操作异常,将直接查询数据库", e);
|
|
|
+ return queryActivityDetail(activityId, currentUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用分布式锁,避免缓存击穿
|
|
|
+ String lockKey = ACTIVITY_DETAIL_LOCK_PREFIX + activityId;
|
|
|
+ RLock lock = redissonClient.getLock(lockKey);
|
|
|
+ boolean locked = false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 尝试获取锁,但不要一直等待
|
|
|
+ locked = lock.tryLock(LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS);
|
|
|
+ if (!locked) {
|
|
|
+ // 获取锁失败,直接查询数据库
|
|
|
+ log.warn("获取分布式锁超时,将直接查询数据库. lockKey={}", lockKey);
|
|
|
+ return queryActivityDetail(activityId, currentUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 双重检查
|
|
|
+ try {
|
|
|
+ String cacheValue = stringRedisTemplate.opsForValue().get(userSpecificCacheKey);
|
|
|
+ if (cacheValue != null && !cacheValue.isEmpty()) {
|
|
|
+ try {
|
|
|
+ return JSON.parseObject(cacheValue, OrderActivityDetailDTO.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析缓存数据失败. key={}", userSpecificCacheKey, e);
|
|
|
+ try {
|
|
|
+ stringRedisTemplate.delete(userSpecificCacheKey);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.warn("删除失效缓存失败", ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("Redis操作异常,将直接查询数据库", e);
|
|
|
+ return queryActivityDetail(activityId, currentUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询数据库
|
|
|
+ OrderActivityDetailDTO detailDTO = queryActivityDetail(activityId, currentUserId);
|
|
|
+
|
|
|
+ // 存入缓存,使用try-catch避免异常
|
|
|
+ if (detailDTO != null) {
|
|
|
+ try {
|
|
|
+ stringRedisTemplate.opsForValue().set(userSpecificCacheKey, JSON.toJSONString(detailDTO), CACHE_EXPIRE_TIME, TimeUnit.MINUTES);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("将数据存入Redis缓存失败,但不影响返回结果", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return detailDTO;
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ return queryActivityDetail(activityId, currentUserId);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 处理其他异常
|
|
|
+ log.error("获取活动详情异常", e);
|
|
|
+ return queryActivityDetail(activityId, currentUserId);
|
|
|
+ } finally {
|
|
|
+ // 只有当前线程持有锁时才释放
|
|
|
+ if (locked && lock.isHeldByCurrentThread()) {
|
|
|
+ try {
|
|
|
+ lock.unlock();
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("释放锁失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private OrderActivityDetailDTO queryActivityDetail(Integer activityId, Long currentUserId) {
|
|
|
+ // 查询活动基本信息
|
|
|
+ OrderActivity activity = getById(activityId);
|
|
|
+ System.out.println(111);
|
|
|
+ if (activity == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换为DTO
|
|
|
+ OrderActivityDetailDTO detailDTO = new OrderActivityDetailDTO();
|
|
|
+ BeanUtils.copyProperties(activity, detailDTO);
|
|
|
+
|
|
|
+ // 确保招募人数不为null
|
|
|
+ if (detailDTO.getRecruitNumber() == null) {
|
|
|
+ detailDTO.setRecruitNumber(20);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查当前用户是否已报名
|
|
|
+ if (currentUserId != null) {
|
|
|
+ LambdaQueryWrapper<OrderSignup> userSignupQuery = new LambdaQueryWrapper<>();
|
|
|
+ userSignupQuery.eq(OrderSignup::getOrderId, activityId)
|
|
|
+ .eq(OrderSignup::getUserId, currentUserId)
|
|
|
+ .in(OrderSignup::getStatus, SIGNUP_STATUS_APPLIED, SIGNUP_STATUS_APPROVED);
|
|
|
+ int count = Math.toIntExact(orderSignupMapper.selectCount(userSignupQuery));
|
|
|
+ detailDTO.setIsSignedUp(count > 0);
|
|
|
+ log.info("活动ID: {}, 用户ID: {}, 是否已报名: {}", activityId, currentUserId, count > 0);
|
|
|
+ } else {
|
|
|
+ detailDTO.setIsSignedUp(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询发布者信息
|
|
|
+ User publisher = userMapper.selectById(activity.getPublisherId());
|
|
|
+ if (publisher != null) {
|
|
|
+ detailDTO.setPublisherName(publisher.getRealName());
|
|
|
+ detailDTO.setPublisherAvatar(publisher.getAvatar());
|
|
|
+ detailDTO.setPublisherRating(publisher.getRating() != null ? publisher.getRating().intValue() : 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询服务类别信息
|
|
|
+ if (activity.getCategoryId() != null) {
|
|
|
+ ServiceCategory category = serviceCategoryMapper.selectById(activity.getCategoryId());
|
|
|
+ if (category != null) {
|
|
|
+ detailDTO.setCategoryName(category.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //查询状态
|
|
|
+ if (activity.getEnrollmentStatus() != null) {
|
|
|
+ detailDTO.setEnrollmentStatus(activity.getEnrollmentStatus());
|
|
|
+ } else {
|
|
|
+ detailDTO.setEnrollmentStatus(0); // 默认为待报名状态
|
|
|
+ }
|
|
|
+ // 查询区域信息
|
|
|
+ if (activity.getAreaId() != null) {
|
|
|
+ Area area = areaMapper.selectById(activity.getAreaId());
|
|
|
+ if (area != null) {
|
|
|
+ detailDTO.setAreaName(area.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询标签信息
|
|
|
+ List<Integer> tagIds = activityTagRelationService.getTagIdsByActivityId(activityId);
|
|
|
+ if (tagIds != null && !tagIds.isEmpty()) {
|
|
|
+ List<OrderTag> tags = orderTagService.listByIds(tagIds);
|
|
|
+ if (tags != null && !tags.isEmpty()) {
|
|
|
+ List<OrderActivityDetailDTO.OrderTagDTO> tagDTOs = tags.stream().map(tag -> {
|
|
|
+ OrderActivityDetailDTO.OrderTagDTO tagDTO = new OrderActivityDetailDTO.OrderTagDTO();
|
|
|
+ tagDTO.setId(tag.getId());
|
|
|
+ tagDTO.setName(tag.getName());
|
|
|
+ return tagDTO;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ detailDTO.setTags(tagDTOs);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return detailDTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public PageResult<OrderActivityDTO> queryActivityList(OrderActivityQueryDTO queryDTO) {
|
|
|
+ // 创建分页对象
|
|
|
+ Page<OrderActivity> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
|
|
|
+
|
|
|
+ // 构建查询条件
|
|
|
+ LambdaQueryWrapper<OrderActivity> wrapper = new LambdaQueryWrapper<>();
|
|
|
+
|
|
|
+ // 添加区域筛选条件
|
|
|
+ if (queryDTO.getAreaId() != null) {
|
|
|
+ wrapper.eq(OrderActivity::getAreaId, queryDTO.getAreaId());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加类别筛选条件
|
|
|
+ if (queryDTO.getCategoryId() != null) {
|
|
|
+ wrapper.eq(OrderActivity::getCategoryId, queryDTO.getCategoryId());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按创建时间倒序排序
|
|
|
+ wrapper.orderByDesc(OrderActivity::getCreateTime);
|
|
|
+
|
|
|
+ // 执行分页查询
|
|
|
+ Page<OrderActivity> activityPage = page(page, wrapper);
|
|
|
+
|
|
|
+ // 转换为DTO列表
|
|
|
+ List<OrderActivityDTO> activityDTOs = activityPage.getRecords().stream().map(activity -> {
|
|
|
+ OrderActivityDTO dto = new OrderActivityDTO();
|
|
|
+ BeanUtils.copyProperties(activity, dto);
|
|
|
+
|
|
|
+ // 查询发布者信息
|
|
|
+ User publisher = userMapper.selectById(activity.getPublisherId());
|
|
|
+ if (publisher != null) {
|
|
|
+ dto.setPublisherName(publisher.getRealName());
|
|
|
+ dto.setPublisherAvatar(publisher.getAvatar());
|
|
|
+ dto.setPublisherRating(publisher.getRating() != null ? publisher.getRating().intValue() : 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询活动标签
|
|
|
+ List<Integer> tagIds = activityTagRelationService.getTagIdsByActivityId(activity.getId());
|
|
|
+ if (tagIds != null && !tagIds.isEmpty()) {
|
|
|
+ List<OrderTag> tags = orderTagService.listByIds(tagIds);
|
|
|
+ String tagNames = tags.stream().map(OrderTag::getName).collect(Collectors.joining(","));
|
|
|
+ dto.setTags(tagNames);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询区域信息
|
|
|
+ if (activity.getAreaId() != null) {
|
|
|
+ Area area = areaMapper.selectById(activity.getAreaId());
|
|
|
+ if (area != null) {
|
|
|
+ dto.setAreaName(area.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询类别信息
|
|
|
+ if (activity.getCategoryId() != null) {
|
|
|
+ ServiceCategory category = serviceCategoryMapper.selectById(activity.getCategoryId());
|
|
|
+ if (category != null) {
|
|
|
+ dto.setCategoryName(category.getName());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用数据库中的报名人数
|
|
|
+ dto.setCurrentNumber(activity.getCurrentNumber());
|
|
|
+
|
|
|
+ // 确保招募人数不为null
|
|
|
+ if (dto.getRecruitNumber() == null) {
|
|
|
+ dto.setRecruitNumber(20);
|
|
|
+ }
|
|
|
+
|
|
|
+ return dto;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 构建分页结果
|
|
|
+ PageResult<OrderActivityDTO> result = new PageResult<>();
|
|
|
+ result.setList(activityDTOs);
|
|
|
+ result.setTotal(activityPage.getTotal());
|
|
|
+ result.setPages((int) activityPage.getPages());
|
|
|
+ result.setPageNum((int) activityPage.getCurrent());
|
|
|
+ result.setPageSize((int) activityPage.getSize());
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateActivityStatusToAccepted(Integer activityId) {
|
|
|
+ OrderActivity activity = getById(activityId);
|
|
|
+ if (activity == null || activity.getConnect() != 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新状态为已接单
|
|
|
+ activity.setConnect(1);
|
|
|
+ if (updateById(activity)) {
|
|
|
+ // 清除缓存
|
|
|
+ String cacheKey = ACTIVITY_DETAIL_KEY_PREFIX + activityId;
|
|
|
+ stringRedisTemplate.delete(cacheKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public Map<String, Object> updateActivitySignupCount(Integer activityId) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ result.put("activityId", activityId);
|
|
|
+
|
|
|
+ try {
|
|
|
+ OrderActivity activity = getById(activityId);
|
|
|
+ if (activity == null) {
|
|
|
+ result.put("error", "活动不存在");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ result.put("activityTitle", activity.getTitle());
|
|
|
+ Integer oldCount = activity.getCurrentNumber();
|
|
|
+ result.put("oldCount", oldCount != null ? oldCount : 0);
|
|
|
+
|
|
|
+ // 查询实际报名人数
|
|
|
+ LambdaQueryWrapper<OrderSignup> signupQuery = new LambdaQueryWrapper<>();
|
|
|
+ signupQuery.eq(OrderSignup::getOrderId, activityId)
|
|
|
+ .in(OrderSignup::getStatus, SIGNUP_STATUS_APPLIED, SIGNUP_STATUS_APPROVED);
|
|
|
+ int signupCount = Math.toIntExact(orderSignupMapper.selectCount(signupQuery));
|
|
|
+ result.put("newCount", signupCount);
|
|
|
+
|
|
|
+ // 只返回查询结果,不更新数据库
|
|
|
+ log.info("活动ID: {}, 标题: {}, 数据库记录人数: {}, 实际查询人数: {}",
|
|
|
+ activityId, activity.getTitle(), oldCount, signupCount);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ } catch (Exception e) {
|
|
|
+ result.put("error", e.getMessage());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取报名状态的名称
|
|
|
+ * @param status 状态值
|
|
|
+ * @return 状态名称
|
|
|
+ */
|
|
|
+ private String getStatusName(int status) {
|
|
|
+ switch (status) {
|
|
|
+ case SIGNUP_STATUS_APPLIED:
|
|
|
+ return "已报名(待审核)";
|
|
|
+ case SIGNUP_STATUS_APPROVED:
|
|
|
+ return "已通过";
|
|
|
+ case SIGNUP_STATUS_REJECTED:
|
|
|
+ return "已拒绝";
|
|
|
+ case SIGNUP_STATUS_CANCELED:
|
|
|
+ return "已取消";
|
|
|
+ default:
|
|
|
+ return "未知状态";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public OrderActivity createActivity(OrderActivityDTO activityDTO) {
|
|
|
+ // 创建活动实体
|
|
|
+ OrderActivity activity = new OrderActivity();
|
|
|
+ BeanUtils.copyProperties(activityDTO, activity);
|
|
|
+
|
|
|
+ // 设置初始状态
|
|
|
+ activity.setStatus(0); // 0-待接单
|
|
|
+ activity.setConnect(0); // 0-待接单
|
|
|
+ activity.setEnrollmentStatus(0); // 0-待报名
|
|
|
+ activity.setCurrentNumber(0); // 初始化报名人数为0
|
|
|
+ activity.setCreateTime(new Date());
|
|
|
+ activity.setUpdateTime(new Date());
|
|
|
+
|
|
|
+ // 保存活动
|
|
|
+ save(activity);
|
|
|
+
|
|
|
+ // 如果有标签,保存标签关系
|
|
|
+ if (activityDTO.getTagIds() != null && !activityDTO.getTagIds().isEmpty()) {
|
|
|
+ activityTagRelationService.saveBatchByActivityId(activity.getId(), activityDTO.getTagIds());
|
|
|
+ }
|
|
|
+
|
|
|
+ return activity;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 修复所有活动的报名人数
|
|
|
+ * 这个方法应该在管理员后台调用,不要在普通接口中使用
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public void fixAllActivitySignupNumbers() {
|
|
|
+ log.info("开始修复所有活动的报名人数");
|
|
|
+
|
|
|
+ // 获取所有活动
|
|
|
+ List<OrderActivity> activities = list();
|
|
|
+ int fixedCount = 0;
|
|
|
+
|
|
|
+ for (OrderActivity activity : activities) {
|
|
|
+ // 查询实际报名人数
|
|
|
+ LambdaQueryWrapper<OrderSignup> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(OrderSignup::getOrderId, activity.getId())
|
|
|
+ .in(OrderSignup::getStatus, 0, 1); // 已报名或已通过状态
|
|
|
+ int actualCount = Math.toIntExact(orderSignupMapper.selectCount(queryWrapper));
|
|
|
+
|
|
|
+ // 如果数据库中的报名人数与实际不符,进行修复
|
|
|
+ if (activity.getCurrentNumber() == null || activity.getCurrentNumber() != actualCount) {
|
|
|
+ activity.setCurrentNumber(actualCount);
|
|
|
+ updateById(activity);
|
|
|
+ fixedCount++;
|
|
|
+ log.info("修复活动报名人数:活动ID={}, 标题={}, 原报名人数={}, 实际报名人数={}",
|
|
|
+ activity.getId(), activity.getTitle(),
|
|
|
+ activity.getCurrentNumber(), actualCount);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("完成修复所有活动的报名人数,共修复 {} 个活动", fixedCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Scheduled(cron = "0 */10 * * * *") // 每10分钟执行一次
|
|
|
+ public void syncSignupNumbers() {
|
|
|
+ log.info("开始同步活动报名人数...");
|
|
|
+ try {
|
|
|
+ // 获取所有进行中的活动
|
|
|
+ LambdaQueryWrapper<OrderActivity> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(OrderActivity::getStatus, 0, 1); // 待接单或招募中的活动
|
|
|
+ List<OrderActivity> activities = list(queryWrapper);
|
|
|
+
|
|
|
+ int updatedCount = 0;
|
|
|
+ for (OrderActivity activity : activities) {
|
|
|
+ // 查询实际报名人数
|
|
|
+ LambdaQueryWrapper<OrderSignup> signupQuery = new LambdaQueryWrapper<>();
|
|
|
+ signupQuery.eq(OrderSignup::getOrderId, activity.getId())
|
|
|
+ .in(OrderSignup::getStatus, 0, 1); // 已报名或已通过状态
|
|
|
+ int actualCount = Math.toIntExact(orderSignupMapper.selectCount(signupQuery));
|
|
|
+
|
|
|
+ // 如果数据库中的人数与实际不符,更新数据库
|
|
|
+ if (activity.getCurrentNumber() == null || activity.getCurrentNumber() != actualCount) {
|
|
|
+ activity.setCurrentNumber(actualCount);
|
|
|
+ updateById(activity);
|
|
|
+ updatedCount++;
|
|
|
+ log.info("更新活动报名人数:活动ID={},旧人数={},新人数={}",
|
|
|
+ activity.getId(), activity.getCurrentNumber(), actualCount);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("活动报名人数同步完成,共更新{}个活动", updatedCount);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("同步活动报名人数时发生错误", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加诊断方法
|
|
|
+ public Map<String, Object> diagnoseSignupNumbers(Integer activityId) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 获取活动信息
|
|
|
+ OrderActivity activity = getById(activityId);
|
|
|
+ if (activity == null) {
|
|
|
+ result.put("error", "活动不存在");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询实际报名人数
|
|
|
+ LambdaQueryWrapper<OrderSignup> signupQuery = new LambdaQueryWrapper<>();
|
|
|
+ signupQuery.eq(OrderSignup::getOrderId, activityId)
|
|
|
+ .in(OrderSignup::getStatus, 0, 1); // 已报名或已通过状态
|
|
|
+ int actualCount = Math.toIntExact(orderSignupMapper.selectCount(signupQuery));
|
|
|
+
|
|
|
+ // 获取报名详情
|
|
|
+ List<Map<String, Object>> signupDetails = orderSignupMapper.selectList(signupQuery)
|
|
|
+ .stream()
|
|
|
+ .map(signup -> {
|
|
|
+ Map<String, Object> detail = new HashMap<>();
|
|
|
+ detail.put("id", signup.getId());
|
|
|
+ detail.put("userId", signup.getUserId());
|
|
|
+ detail.put("status", signup.getStatus());
|
|
|
+ return detail;
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ result.put("activityId", activityId);
|
|
|
+ result.put("currentNumberInDB", activity.getCurrentNumber());
|
|
|
+ result.put("actualSignupCount", actualCount);
|
|
|
+ result.put("recruitNumber", activity.getRecruitNumber());
|
|
|
+ result.put("signupDetails", signupDetails);
|
|
|
+
|
|
|
+ // 检查是否需要修复
|
|
|
+ if (activity.getCurrentNumber() == null || activity.getCurrentNumber() != actualCount) {
|
|
|
+ result.put("needsFix", true);
|
|
|
+ result.put("suggestedFix", String.format(
|
|
|
+ "需要将 current_number 从 %d 更新为 %d",
|
|
|
+ activity.getCurrentNumber(),
|
|
|
+ actualCount
|
|
|
+ ));
|
|
|
+ } else {
|
|
|
+ result.put("needsFix", false);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ result.put("error", "诊断过程中发生错误: " + e.getMessage());
|
|
|
+ log.error("诊断报名人数时发生错误", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加修复方法
|
|
|
+ @Transactional
|
|
|
+ public boolean fixSignupNumbers(Integer activityId) {
|
|
|
+ try {
|
|
|
+ // 获取活动信息
|
|
|
+ OrderActivity activity = getById(activityId);
|
|
|
+ if (activity == null) {
|
|
|
+ log.error("修复报名人数失败:活动不存在,活动ID={}", activityId);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询实际报名人数
|
|
|
+ LambdaQueryWrapper<OrderSignup> signupQuery = new LambdaQueryWrapper<>();
|
|
|
+ signupQuery.eq(OrderSignup::getOrderId, activityId)
|
|
|
+ .in(OrderSignup::getStatus, 0, 1); // 已报名或已通过状态
|
|
|
+ int actualCount = Math.toIntExact(orderSignupMapper.selectCount(signupQuery));
|
|
|
+
|
|
|
+ // 更新数据库中的报名人数
|
|
|
+ activity.setCurrentNumber(actualCount);
|
|
|
+ boolean updated = updateById(activity);
|
|
|
+
|
|
|
+ if (updated) {
|
|
|
+ log.info("修复活动报名人数成功:活动ID={},更新为{}", activityId, actualCount);
|
|
|
+ // 清除相关缓存
|
|
|
+ String cacheKey = ACTIVITY_DETAIL_KEY_PREFIX + activityId;
|
|
|
+ stringRedisTemplate.delete(cacheKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ return updated;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("修复报名人数时发生错误", e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|