From fababb22599c8a4b3034bdfc98ee7502aa73bf4d Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期四, 20 八月 2026 12:15:53 +0800
Subject: [PATCH] feat0806: 动态生成微赞URL,传递门店电话

---
 traditionalApp/pages/home/home.vue                                                                                    |    8 +++
 traditionalApp/pages/mine/mine.vue                                                                                    |    7 +++
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/service/AppVzanLiveEntryService.java         |   49 ++++++++++++++++++++----
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/dto/CreateLiveRoomEntryRequest.java          |   17 ++++++++
 tcm-training-admin/config/routes.ts                                                                                   |   20 ++++++++++
 traditionalApp/pages/home/service.js                                                                                  |    4 +-
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java |    8 +++-
 7 files changed, 98 insertions(+), 15 deletions(-)

diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java
index 4af26a0..47b414b 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java
+++ b/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()));
     }
 }
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/dto/CreateLiveRoomEntryRequest.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/dto/CreateLiveRoomEntryRequest.java
new file mode 100644
index 0000000..1b4748a
--- /dev/null
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/dto/CreateLiveRoomEntryRequest.java
@@ -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;
+}
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/service/AppVzanLiveEntryService.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/service/AppVzanLiveEntryService.java
index 30c7ef6..f0e8b5c 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/service/AppVzanLiveEntryService.java
+++ b/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 : "";
+    }
 }
\ No newline at end of file
diff --git a/tcm-training-admin/config/routes.ts b/tcm-training-admin/config/routes.ts
index 44bcc0c..8e2351b 100644
--- a/tcm-training-admin/config/routes.ts
+++ b/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',
diff --git a/traditionalApp/pages/home/home.vue b/traditionalApp/pages/home/home.vue
index 4ff4341..ee85e3e 100644
--- a/traditionalApp/pages/home/home.vue
+++ b/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' });
diff --git a/traditionalApp/pages/home/service.js b/traditionalApp/pages/home/service.js
index d99b35d..9128c0a 100644
--- a/traditionalApp/pages/home/service.js
+++ b/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)
 }
diff --git a/traditionalApp/pages/mine/mine.vue b/traditionalApp/pages/mine/mine.vue
index e62665c..fda81ec 100644
--- a/traditionalApp/pages/mine/mine.vue
+++ b/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' })

--
Gitblit v1.7.1