| tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/dto/CreateLiveRoomEntryRequest.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/service/AppVzanLiveEntryService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| tcm-training-admin/config/routes.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traditionalApp/pages/home/home.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traditionalApp/pages/home/service.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traditionalApp/pages/mine/mine.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java
@@ -1,5 +1,6 @@ package com.tcm.training.admin.app.home.controller; import com.tcm.training.admin.app.home.dto.CreateLiveRoomEntryRequest; import com.tcm.training.admin.app.home.service.AppVzanLiveEntryService; import com.tcm.training.admin.app.home.vo.AppVzanLiveEntryVO; import com.tcm.training.admin.common.ApiResponse; @@ -7,8 +8,11 @@ import com.tcm.training.admin.live.vo.LiveRoomSettingVO; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @RestController @RequestMapping({"/api/app/home", "/api/v1/app/home"}) @@ -28,7 +32,7 @@ } @PostMapping("/live-room-entry") public ApiResponse<AppVzanLiveEntryVO> createEntry() { return ApiResponse.ok(vzanLiveEntryService.createEntry()); public ApiResponse<AppVzanLiveEntryVO> createEntry(@Valid @RequestBody CreateLiveRoomEntryRequest request) { return ApiResponse.ok(vzanLiveEntryService.createEntry(request.getUserId())); } } tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/dto/CreateLiveRoomEntryRequest.java
New file @@ -0,0 +1,17 @@ package com.tcm.training.admin.app.home.dto; import lombok.Data; import javax.validation.constraints.NotNull; /** * 创建微赞直播入口请求参数。 */ @Data public class CreateLiveRoomEntryRequest { /** * 用户ID */ @NotNull(message = "用户ID不能为空") private Long userId; } tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/service/AppVzanLiveEntryService.java
@@ -14,6 +14,8 @@ import com.tcm.training.admin.live.mapper.ClinicMapper; import com.tcm.training.admin.live.service.LiveRoomSettingService; import com.tcm.training.admin.live.vo.LiveRoomSettingVO; import com.tcm.training.admin.member.entity.AppUser; import com.tcm.training.admin.member.mapper.UserMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.web.util.UriComponentsBuilder; @@ -25,34 +27,37 @@ @Service public class AppVzanLiveEntryService { private static final String VZAN_AUTH_SECRET = "2b4fefe91d8cba50"; private static final int USER_STATUS_ENABLED = 1; private final LiveRoomSettingService liveRoomSettingService; private final AppUserCenterService appUserCenterService; private final UserMapper userMapper; private final ClinicMapper clinicMapper; private final ProvincialAgentMapper provincialAgentMapper; public AppVzanLiveEntryService(LiveRoomSettingService liveRoomSettingService, AppUserCenterService appUserCenterService, UserMapper userMapper, ClinicMapper clinicMapper, ProvincialAgentMapper provincialAgentMapper) { this.liveRoomSettingService = liveRoomSettingService; this.appUserCenterService = appUserCenterService; this.userMapper = userMapper; this.clinicMapper = clinicMapper; this.provincialAgentMapper = provincialAgentMapper; } public AppVzanLiveEntryVO createEntry() { public AppVzanLiveEntryVO createEntry(Long userId) { LiveRoomSettingVO setting = liveRoomSettingService.current(); String liveRoomUrl = setting == null ? null : StrUtil.trim(setting.getLiveRoomUrl()); validateLiveRoomUrl(liveRoomUrl); AppUserCenterVO user = appUserCenterService.getCurrentUserCenter(); AppUser user = requireEnabledUser(userId); String followPhone = resolveFollowPhone(user.getClinicId()); String avatar = resolveAvatar(user); JSONObject payload = new JSONObject(); payload.set("tuid", user.getId()); payload.set("followPhone", followPhone); payload.set("third_ts", System.currentTimeMillis()); payload.set("avatar", StrUtil.trim(user.getAvatar())); payload.set("avatar", StrUtil.trim(avatar)); log.info("微赞入口参数已生成,tuid={}, third_ts={}", user.getId(), payload.get("third_ts")); SymmetricCrypto sm4 = SmUtil.sm4(VZAN_AUTH_SECRET.getBytes(StandardCharsets.UTF_8)); @@ -71,11 +76,11 @@ throw new IllegalArgumentException("当前用户未绑定诊所"); } Clinic clinic = clinicMapper.selectById(clinicId); if (clinic == null || clinic.getAgentId() == null) { if (clinic == null) { throw new IllegalArgumentException("当前诊所未绑定代理"); } ProvincialAgent agent = provincialAgentMapper.selectById(clinic.getAgentId()); String contactInfo = agent == null ? null : StrUtil.trim(agent.getContactInfo()); String contactInfo = StrUtil.trim(clinic.getContactPhone()); if (StrUtil.isBlank(contactInfo)) { throw new IllegalArgumentException("所属代理未配置联系电话"); } @@ -95,4 +100,30 @@ throw new IllegalArgumentException("直播间地址必须为有效的HTTPS地址", ex); } } /** * 校验用户存在且可用。 */ private AppUser requireEnabledUser(Long userId) { AppUser user = userMapper.selectById(userId); if (user == null || user.getDeleted() == null || user.getDeleted().intValue() != 0) { throw new IllegalArgumentException("用户不存在"); } if (user.getStatus() == null || user.getStatus().intValue() != USER_STATUS_ENABLED) { throw new IllegalArgumentException("用户已禁用"); } return user; } /** * 优先使用本地头像,没有时回退微信头像。 */ private String resolveAvatar(AppUser user) { String avatar = user == null ? null : StrUtil.trim(user.getAvatar()); if (StrUtil.isNotBlank(avatar)) { return avatar; } String wechatAvatar = user == null ? null : StrUtil.trim(user.getWechatAvatar()); return StrUtil.isNotBlank(wechatAvatar) ? wechatAvatar : ""; } } tcm-training-admin/config/routes.ts
@@ -363,6 +363,26 @@ component: './live/health-popularization/manage', }, { name: '项目介绍', path: '/live/course/project-introduction', access: 'live:project-introduction:view', component: './live/project-introduction', }, { name: '新增项目介绍', path: '/live/course/project-introduction/create', hideInMenu: true, access: 'live:project-introduction:add', component: './live/project-introduction/manage', }, { name: '编辑项目介绍', path: '/live/course/project-introduction/:id/edit', hideInMenu: true, access: 'live:project-introduction:edit', component: './live/project-introduction/manage', }, { name: '视频与课程资源管理', path: '/live/course/video-resources', access: 'live:health-course:view', traditionalApp/pages/home/home.vue
@@ -342,7 +342,13 @@ weiZanLiveOpening.value = true; uni.showLoading({ title: '正在进入直播间', mask: true }); try { const res = await createLiveRoomEntry(); const cachedUser = uni.getStorageSync('appUserInfo'); const userId = cachedUser?.id; if (!userId) { uni.showToast({ title: '请先登录', icon: 'none' }); return; } const res = await createLiveRoomEntry(userId); const targetUrl = res?.data?.url?.trim(); if (res?.code !== 0 || !targetUrl || !isValidLiveRoomUrl(targetUrl)) { uni.showToast({ title: res?.msg || '获取直播间地址失败', icon: 'none' }); traditionalApp/pages/home/service.js
@@ -52,6 +52,6 @@ /** * 根据当前登录用户生成短时有效的微赞直播入口。 */ export const createLiveRoomEntry = () => { return request.post('/api/app/home/live-room-entry', {}, false, false) export const createLiveRoomEntry = (userId) => { return request.post('/api/app/home/live-room-entry', { userId }, false, false) } traditionalApp/pages/mine/mine.vue
@@ -392,7 +392,12 @@ if (typeof target !== 'string' && target.dynamicVzan) { try { uni.showLoading({ title: '正在进入', mask: true }) const response = await createLiveRoomEntry() const userId = userCenter.value.id if (!userId) { uni.showToast({ title: '请先登录', icon: 'none' }) return } const response = await createLiveRoomEntry(userId) const targetUrl = response?.code === 0 ? response.data?.url : '' if (!targetUrl) { uni.showToast({ title: response?.msg || '获取跳转地址失败', icon: 'none' })