| | |
| | | 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; |
| | |
| | | @Slf4j |
| | | @Service |
| | | public class AppVzanLiveEntryService { |
| | | // TODO 以下参数由微赞提供,联调前替换;正式环境应迁移至环境变量或配置中心。 |
| | | // private static final String VZAN_AUTH_SECRET = "e70ccf6a-1745-42b4-be41-aaed6f65700f"; |
| | | private static final String VZAN_AUTH_SECRET = "2b4fefe91d8cba50"; |
| | | private static final String VZAN_TUID = "9594"; |
| | | private static final String followPhone = "18912345680"; |
| | | private static final String VZAN_NICKNAME = "微赞测试用户"; |
| | | private static final String VZAN_AVATAR = "https://static3.njyqkj0ksyz.com/zhibo/images/defaultuser.jpg"; |
| | | 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) { |
| | | 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() { |
| | | validateStaticParameters(); |
| | | 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", VZAN_TUID); |
| | | payload.set("tuid", user.getId()); |
| | | payload.set("followPhone", followPhone); |
| | | payload.set("third_ts", System.currentTimeMillis() + 5000); |
| | | if (StrUtil.isNotBlank(VZAN_NICKNAME)) { |
| | | payload.set("nickname", VZAN_NICKNAME); |
| | | } |
| | | if (StrUtil.isNotBlank(VZAN_AVATAR)) { |
| | | payload.set("avatar", VZAN_AVATAR); |
| | | } |
| | | 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); |
| | |
| | | .encode(StandardCharsets.UTF_8) |
| | | .toUriString(); |
| | | log.info("微赞直播生成URL: {}", entryUrl); |
| | | return new AppVzanLiveEntryVO(entryUrl); |
| | | return new AppVzanLiveEntryVO(entryUrl, VZAN_WEB_PAY_UA); |
| | | } |
| | | |
| | | private void validateStaticParameters() { |
| | | if (VZAN_AUTH_SECRET.getBytes(StandardCharsets.UTF_8).length != 16) { |
| | | throw new IllegalStateException("微赞鉴权密钥必须为16字节"); |
| | | private String resolveFollowPhone(Long clinicId) { |
| | | if (clinicId == null) { |
| | | throw new IllegalArgumentException("当前用户未绑定诊所"); |
| | | } |
| | | if (StrUtil.isBlank(VZAN_TUID)) { |
| | | throw new IllegalStateException("微赞用户标识暂未配置"); |
| | | 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) { |
| | |
| | | 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 : ""; |
| | | } |
| | | } |