13404089107
2026-04-20 f4b6843a1b3990453d2aef6d87b5ac30723c5870
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/service/HealthCourseAppService.java
@@ -4,19 +4,26 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tcm.training.admin.app.notice.service.AppSystemNoticeDispatchCommand;
import com.tcm.training.admin.app.notice.service.AppSystemNoticeDispatchService;
import com.tcm.training.admin.app.notice.service.AppSystemNoticeType;
import com.tcm.training.admin.common.Jsons;
import com.tcm.training.admin.common.PageResponse;
import com.tcm.training.admin.live.dto.HealthCourseSaveRequest;
import com.tcm.training.admin.live.dto.HealthCourseStatusRequest;
import com.tcm.training.admin.live.dto.HealthCourseVideoSaveRequest;
import com.tcm.training.admin.live.entity.Expert;
import com.tcm.training.admin.live.entity.CoursePurchaseOrder;
import com.tcm.training.admin.live.entity.HealthCourse;
import com.tcm.training.admin.live.entity.HealthCourseMemberJoinRecord;
import com.tcm.training.admin.live.entity.HealthCourseVideo;
import com.tcm.training.admin.live.entity.LiveCourseCategory;
import com.tcm.training.admin.live.excel.HealthCourseExportRow;
import com.tcm.training.admin.live.excel.HealthCourseVideoImportRow;
import com.tcm.training.admin.live.mapper.ExpertMapper;
import com.tcm.training.admin.live.mapper.HealthCourseMapper;
import com.tcm.training.admin.live.mapper.CoursePurchaseOrderMapper;
import com.tcm.training.admin.live.mapper.HealthCourseMemberJoinRecordMapper;
import com.tcm.training.admin.live.mapper.HealthCourseVideoMapper;
import com.tcm.training.admin.live.mapper.LiveCourseCategoryMapper;
import com.tcm.training.admin.live.vo.ExpertVO;
@@ -53,15 +60,24 @@
    private final HealthCourseVideoMapper healthCourseVideoMapper;
    private final LiveCourseCategoryMapper liveCourseCategoryMapper;
    private final ExpertMapper expertMapper;
    private final CoursePurchaseOrderMapper coursePurchaseOrderMapper;
    private final HealthCourseMemberJoinRecordMapper healthCourseMemberJoinRecordMapper;
    private final AppSystemNoticeDispatchService appSystemNoticeDispatchService;
    public HealthCourseAppService(HealthCourseMapper healthCourseMapper,
                                  HealthCourseVideoMapper healthCourseVideoMapper,
                                  LiveCourseCategoryMapper liveCourseCategoryMapper,
                                  ExpertMapper expertMapper) {
                                  ExpertMapper expertMapper,
                                  CoursePurchaseOrderMapper coursePurchaseOrderMapper,
                                  HealthCourseMemberJoinRecordMapper healthCourseMemberJoinRecordMapper,
                                  AppSystemNoticeDispatchService appSystemNoticeDispatchService) {
        this.healthCourseMapper = healthCourseMapper;
        this.healthCourseVideoMapper = healthCourseVideoMapper;
        this.liveCourseCategoryMapper = liveCourseCategoryMapper;
        this.expertMapper = expertMapper;
        this.coursePurchaseOrderMapper = coursePurchaseOrderMapper;
        this.healthCourseMemberJoinRecordMapper = healthCourseMemberJoinRecordMapper;
        this.appSystemNoticeDispatchService = appSystemNoticeDispatchService;
    }
    public PageResponse<HealthCourseListItemVO> page(int page,
@@ -297,6 +313,7 @@
        next.setDeleted(0);
        healthCourseVideoMapper.insert(next);
        refreshCourseStats(courseId);
        dispatchCourseUpdateNotice(courseId, next);
        return detailVideoResource(next.getId());
    }
@@ -380,6 +397,7 @@
            video.setDeleted(0);
            healthCourseVideoMapper.insert(video);
            touchedCourseIds.add(course.getId());
            dispatchCourseUpdateNotice(course.getId(), video);
        }
        if (touchedCourseIds.isEmpty()) {
            throw new IllegalArgumentException("导入文件没有可用数据");
@@ -500,6 +518,52 @@
        healthCourseMapper.updateById(course);
    }
    /**
     * 在课程新增章节后通知已购课或已加入学习的用户。
     */
    private void dispatchCourseUpdateNotice(Long courseId, HealthCourseVideo video) {
        if (courseId == null || video == null || video.getId() == null) {
            return;
        }
        HealthCourse course = requireCourse(courseId);
        Set<Long> appUserIds = new LinkedHashSet<Long>();
        List<CoursePurchaseOrder> orders = coursePurchaseOrderMapper.selectList(new LambdaQueryWrapper<CoursePurchaseOrder>()
                .eq(CoursePurchaseOrder::getDeleted, 0)
                .eq(CoursePurchaseOrder::getStatus, "PAID")
                .eq(CoursePurchaseOrder::getCourseId, courseId));
        for (CoursePurchaseOrder order : orders) {
            if (order.getAppUserId() != null) {
                appUserIds.add(order.getAppUserId());
            }
        }
        List<HealthCourseMemberJoinRecord> joinRecords = healthCourseMemberJoinRecordMapper.selectList(new LambdaQueryWrapper<HealthCourseMemberJoinRecord>()
                .eq(HealthCourseMemberJoinRecord::getDeleted, 0)
                .eq(HealthCourseMemberJoinRecord::getCourseId, courseId));
        for (HealthCourseMemberJoinRecord joinRecord : joinRecords) {
            if (joinRecord.getAppUserId() != null) {
                appUserIds.add(joinRecord.getAppUserId());
            }
        }
        for (Long appUserId : appUserIds) {
            AppSystemNoticeDispatchCommand command = new AppSystemNoticeDispatchCommand();
            command.setAppUserId(appUserId);
            command.setNoticeTypeCode(AppSystemNoticeType.COURSE_UPDATE_NOTICE);
            command.setTitle("课程更新通知");
            command.setContent(course.getTitle() + " 已更新新章节");
            // 使用章节主键作为引用,保证同一章节对同一用户只发一次。
            command.setReferenceType("HEALTH_COURSE_VIDEO");
            command.setReferenceId(video.getId());
            command.setRequireUserSetting(Boolean.TRUE);
            Map<String, String> payload = new LinkedHashMap<String, String>();
            payload.put("courseId", String.valueOf(courseId));
            payload.put("courseTitle", course.getTitle());
            payload.put("videoId", String.valueOf(video.getId()));
            payload.put("chapterName", defaultText(video.getChapterName()));
            command.setPayload(payload);
            appSystemNoticeDispatchService.createAndPush(command);
        }
    }
    private HealthCourse requireCourse(Long id) {
        HealthCourse course = healthCourseMapper.selectById(id);
        if (course == null) {