hejianhao
2026-08-28 16a57a8fc6868cea9171a1119698572407a2fc03
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package com.tcm.training.admin.app.home.service;
 
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.tcm.training.admin.agent.entity.ProvincialAgent;
import com.tcm.training.admin.agent.mapper.ProvincialAgentMapper;
import com.tcm.training.admin.app.home.vo.AppVzanLiveEntryVO;
import com.tcm.training.admin.app.user.service.AppUserCenterService;
import com.tcm.training.admin.app.user.vo.AppUserCenterVO;
import com.tcm.training.admin.live.entity.Clinic;
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;
 
import java.net.URI;
import java.nio.charset.StandardCharsets;
 
@Slf4j
@Service
public class AppVzanLiveEntryService {
    private static final String VZAN_AUTH_SECRET = "2b4fefe91d8cba50";
    private static final String VZAN_WEB_PAY_UA = "webchatPay/wx074c1f0cf5a0ecf4 InAppPay";
    private static final int USER_STATUS_ENABLED = 1;
 
    private final LiveRoomSettingService liveRoomSettingService;
    private final UserMapper userMapper;
    private final ClinicMapper clinicMapper;
    private final ProvincialAgentMapper provincialAgentMapper;
 
    public AppVzanLiveEntryService(LiveRoomSettingService liveRoomSettingService,
                                   UserMapper userMapper,
                                   ClinicMapper clinicMapper,
                                   ProvincialAgentMapper provincialAgentMapper) {
        this.liveRoomSettingService = liveRoomSettingService;
        this.userMapper = userMapper;
        this.clinicMapper = clinicMapper;
        this.provincialAgentMapper = provincialAgentMapper;
    }
 
    public AppVzanLiveEntryVO createEntry(Long userId) {
        LiveRoomSettingVO setting = liveRoomSettingService.current();
        String liveRoomUrl = setting == null ? null : StrUtil.trim(setting.getLiveRoomUrl());
        validateLiveRoomUrl(liveRoomUrl);
 
        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() + 5000);
        payload.set("avatar", StrUtil.trim(avatar));
        log.info("微赞入口参数已生成,{}", JSONUtil.toJsonStr(payload));
 
        SymmetricCrypto sm4 = SmUtil.sm4(VZAN_AUTH_SECRET.getBytes(StandardCharsets.UTF_8));
        String encryptedData = sm4.encryptBase64(payload.toString(), StandardCharsets.UTF_8);
        String entryUrl = UriComponentsBuilder.fromHttpUrl(liveRoomUrl)
                .replaceQueryParam("data", encryptedData)
                .build()
                .encode(StandardCharsets.UTF_8)
                .toUriString();
        log.info("微赞直播生成URL: {}", entryUrl);
        return new AppVzanLiveEntryVO(entryUrl, VZAN_WEB_PAY_UA);
    }
 
    private String resolveFollowPhone(Long clinicId) {
        if (clinicId == null) {
            throw new IllegalArgumentException("当前用户未绑定诊所");
        }
        Clinic clinic = clinicMapper.selectById(clinicId);
        if (clinic == null) {
            throw new IllegalArgumentException("当前诊所未绑定代理");
        }
 
        String contactInfo = StrUtil.trim(clinic.getContactPhone());
        if (StrUtil.isBlank(contactInfo)) {
            throw new IllegalArgumentException("所属代理未配置联系电话");
        }
        return contactInfo;
    }
 
    private void validateLiveRoomUrl(String liveRoomUrl) {
        if (StrUtil.isBlank(liveRoomUrl)) {
            throw new IllegalArgumentException("直播间地址暂未配置");
        }
        try {
            URI uri = URI.create(liveRoomUrl);
            if (!"https".equalsIgnoreCase(uri.getScheme()) || StrUtil.isBlank(uri.getHost())) {
                throw new IllegalArgumentException("直播间地址必须为有效的HTTPS地址");
            }
        } catch (IllegalArgumentException ex) {
            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 : "";
    }
}