LivingApiController.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. package com.tzy.controller.living;
  2. import com.beust.jcommander.internal.Lists;
  3. import com.github.pagehelper.PageHelper;
  4. import com.github.pagehelper.PageInfo;
  5. import com.tzy.annotation.ApiLimitRule;
  6. import com.tzy.common.config.CommonConfig;
  7. import com.tzy.common.config.handle.ApiVersion;
  8. import com.tzy.common.constant.Constants;
  9. import com.tzy.common.core.controller.BaseController;
  10. import com.tzy.common.dto.InDto;
  11. import com.tzy.common.dto.OutDTO;
  12. import com.tzy.common.dto.UserInfo;
  13. import com.tzy.common.utils.UserType;
  14. import com.tzy.common.utils.UserUtils;
  15. import com.tzy.common.utils.bean.JSONTools;
  16. import com.tzy.common.utils.http.forest.CommonForestClient;
  17. import com.tzy.dto.*;
  18. import com.tzy.framework.util.RedisUtils;
  19. import com.tzy.liveConfig.service.LiveLikeDBUpdateService;
  20. import com.tzy.liveConfig.service.LiveLikePushService;
  21. import com.tzy.sportcard.api.dto.LiveMerchantDTO;
  22. import com.tzy.sportcard.api.dto.group.LiveListDTO;
  23. import com.tzy.sportcard.api.service.GroupApiService;
  24. import com.tzy.sportcard.group.domain.CardGroupLivesConfig;
  25. import com.tzy.sportcard.group.domain.CardGroupLivesLike;
  26. import com.tzy.sportcard.group.mapper.CardGroupLivesConfigMapper;
  27. import com.tzy.sportcard.group.mapper.CardGroupLivesLikeMapper;
  28. import com.tzy.sportcard.group.service.ICardGroupLivesConfigService;
  29. import com.tzy.sportcard.group.service.impl.GroupActService;
  30. import io.swagger.annotations.Api;
  31. import io.swagger.annotations.ApiOperation;
  32. import lombok.extern.slf4j.Slf4j;
  33. import org.apache.commons.lang3.StringUtils;
  34. import org.springframework.beans.factory.annotation.Value;
  35. import org.springframework.util.CollectionUtils;
  36. import org.springframework.validation.annotation.Validated;
  37. import org.springframework.web.bind.annotation.*;
  38. import org.teasoft.bee.osql.SuidRich;
  39. import javax.annotation.Resource;
  40. import java.util.*;
  41. import java.util.stream.Collectors;
  42. /**
  43. * 直播-api
  44. */
  45. @Api("直播-api")
  46. @Slf4j
  47. @RestController
  48. @RequestMapping("/api/living")
  49. public class LivingApiController extends BaseController {
  50. @Resource
  51. private SuidRich suidRich;
  52. @Resource
  53. private CardGroupLivesConfigMapper cardGroupLivesConfigMapper;
  54. @Resource
  55. private CardGroupLivesLikeMapper cardGroupLivesLikeMapper;
  56. @Resource
  57. private GroupApiService groupApiService;
  58. @Resource
  59. private LiveLikePushService liveLikePushService;
  60. @Resource
  61. private LiveLikeDBUpdateService liveLikeDBUpdateService;
  62. @Resource
  63. private ICardGroupLivesConfigService livesConfigService;
  64. @Resource
  65. private CommonForestClient commonForestClient;
  66. @Value("${liveWebSocketUrl:https://im-dev.hobbystocks.cn/chat/handler}")
  67. private String liveWebSocketUrl;
  68. @Resource
  69. private RedisUtils redisUtils;
  70. @Resource
  71. private GroupActService groupActService;
  72. @PostMapping(value = "/details")
  73. public OutDTO details(@RequestBody InDto inDto) {
  74. Integer roomid = inDto.getIntegerParam("id");
  75. if (null == roomid) return OutDTO.error(10040, "参数[id]不能为空!");
  76. LivingRoomDTO cond = new LivingRoomDTO();
  77. cond.setId(roomid);
  78. LivingRoomDTO livingRoomDto = suidRich.selectOne(cond);
  79. if (livingRoomDto == null) {
  80. return OutDTO.error500("直播间不存在!");
  81. }
  82. return OutDTO.ok().put("livingRoomInfo", livingRoomDto);
  83. }
  84. @ApiOperation("获取直播间")
  85. @PostMapping("/{version}/list/{type}/{refId}")
  86. public OutDTO findMerchantLiving(@PathVariable("refId") Integer refId, @PathVariable("type") String type) {
  87. UserInfo user = UserUtils.getSimpleUserInfo();
  88. Integer userId = user == null ? null : user.getId();
  89. List<LiveListDTO> liveList = livesConfigService.findAllLiving(type, refId, userId);
  90. if (CollectionUtils.isEmpty(liveList)) {
  91. return OutDTO.ok().put("livingList", liveList);
  92. }
  93. boolean isShipping = user != null && UserType.USER_ROLE_SHIPPING.equals(user.getRoleCode());
  94. // 判断当前登陆的用户是否是商家角色
  95. Integer merchantId = isShipping && user.getMerchantId() != null ? user.getMerchantId() : null;
  96. liveList.parallelStream().filter(living -> Constants.ORDER_TYPE_GROUP.equals(living.getCategory()))
  97. .forEach(living -> {
  98. boolean confirmStatus = isShipping && Objects.equals(living.getMerchantId(), merchantId);
  99. living.setConfirmStatus(confirmStatus);
  100. groupApiService.setGroupData(userId, living);
  101. });
  102. return OutDTO.ok().put("livingList", liveList);
  103. }
  104. @ApiOperation("获取已结束直播")
  105. @PostMapping("/{version}/end/list")
  106. public OutDTO findEndLive(@RequestBody EndLiveParam endLiveParam) {
  107. PageInfo<LiveListDTO> liveList = livesConfigService.findEndLives(endLiveParam);
  108. return OutDTO.ok().put("livingList", liveList);
  109. }
  110. @ApiOperation("获取直播间用户信息")
  111. @PostMapping("/{version}/user/{code}")
  112. @ApiLimitRule(seconds = 1, limitCount = 30, msg = "网络失败,请重试!")
  113. public OutDTO findLivingRoomUser(@PathVariable("code") String code) {
  114. List<LivingRoomDTO> rooms = getRooms(code);
  115. if (CollectionUtils.isEmpty(rooms)) {
  116. return OutDTO.ok().put("users", new ArrayList<>());
  117. }
  118. Integer merchantId = rooms.get(0).getMerchantId();
  119. String url = String.format(CommonConfig.baseconfig.getImBaseUrl() + CommonConfig.baseconfig.getImUserUrl(), code);
  120. String result = commonForestClient.sendGet(url);
  121. ImUserResp resp = JSONTools.jsonStr2obj(result, ImUserResp.class);
  122. if (resp == null || resp.getCode() != 200 || CollectionUtils.isEmpty(resp.getData())) {
  123. log.warn("查询直播间用户异常:{}", result);
  124. return OutDTO.ok().put("users", new ArrayList<>());
  125. }
  126. UserInfo user = UserUtils.getSimpleUserInfo();
  127. boolean isOwnerMerchant = user != null && Objects.equals(merchantId, user.getMerchantId());
  128. log.debug("查询直播间用户结果:{}", result);
  129. List<ImUserDTO> users = livesConfigService.findLivingRoomUser(merchantId, resp.getData(), isOwnerMerchant, 1, 200);
  130. return OutDTO.ok().put("users", users);
  131. }
  132. private List<LivingRoomDTO> getRooms(String code) {
  133. LivingRoomDTO cond = new LivingRoomDTO();
  134. cond.setCode(code);
  135. cond.setDelFlg(0);
  136. cond.setStatus(1);
  137. return suidRich.select(cond);
  138. }
  139. private LivingRoomDTO checkRoomUser(ImParam param) {
  140. if (StringUtils.isEmpty(param.getCode())) {
  141. return null;
  142. }
  143. if (CollectionUtils.isEmpty(param.getUserIds())) {
  144. return null;
  145. }
  146. List<LivingRoomDTO> rooms = getRooms(param.getCode());
  147. if (CollectionUtils.isEmpty(rooms)) {
  148. return null;
  149. }
  150. return rooms.get(0);
  151. }
  152. @ApiOperation("直播间用户变动")
  153. @PostMapping("/{version}/user/change")
  154. public OutDTO livingUserChange(@RequestBody ImParam param) {
  155. LivingRoomDTO livingRoom = checkRoomUser(param);
  156. if (livingRoom == null) {
  157. return OutDTO.error(405, "直播间信息不存在");
  158. }
  159. log.debug("直播间用户变动:{}", param);
  160. List<ImUserDTO> users = livesConfigService.getImUsers(livingRoom.getMerchantId(), param.getUserIds(), 3);
  161. livesConfigService.setCachedLiveRoomUser(param.getCode(), param.getUserIds());
  162. if (CollectionUtils.isEmpty(users)) {
  163. return OutDTO.ok();
  164. }
  165. // if (!redisUtils.setIfAbsent(Constants.LIVING_USER_CHANGE_RECORD + param.getCode(), "1", 2, TimeUnit.SECONDS)) {
  166. // return OutDTO.ok();
  167. // }
  168. //List<String> imgList = new ArrayList<>(3);
  169. // StringJoiner stringJoiner = new StringJoiner(",");
  170. // for(ImUserDTO user:users){
  171. // stringJoiner.add(user.getId().toString());
  172. // imgList.add(user.getAvatar());
  173. // }
  174. // String currentUserIds = stringJoiner.toString();
  175. // String key=Constants.LIVING_ROOM_USER_KEY+param.getCode();
  176. // Object existUserId = redisUtils.get(key);
  177. // if (currentUserIds.equals(existUserId)) {
  178. // return OutDTO.ok();
  179. // }
  180. List<String> imgList = users.stream().map(ImUserDTO::getAvatar).collect(Collectors.toList());
  181. Message message = buildImReq(param.getCode(), imgList, param.getUserIds());
  182. //commonForestClient.sendPost(liveWebSocketUrl, Constants.JSON_HEADS, message);
  183. //redisUtils.set(key,currentUserIds,60);
  184. return OutDTO.ok().put("message",message);
  185. }
  186. private Message buildImReq(String code, List<String> imgList, List<Integer> userIdList) {
  187. Map<String, Object> body = new HashMap<>(2);
  188. body.put("imgList", imgList);
  189. body.put("totalNum", userIdList.size());
  190. return Message.builder().code("chat")
  191. .messageType("chat_to_room")
  192. .messageParam(
  193. Message.MessageParam.builder()
  194. .receiver(code)
  195. .payload(
  196. Message.MessageParam.Payload.builder()
  197. .contentType("CHANGES_NUM_PEOPLE")
  198. .sender("app")
  199. .content("user")
  200. .extra(JSONTools.obj2json(body))
  201. .build()
  202. ).build()).build();
  203. }
  204. @ApiOperation("获取直播间用户信息v2,分页")
  205. @PostMapping("/{version}/user")
  206. @ApiLimitRule(seconds = 1, limitCount = 30, msg = "网络失败,请重试!")
  207. public OutDTO findLivingRoomUserV2(@Validated @RequestBody LivingUserParam param) {
  208. String code = param.getCode();
  209. if (StringUtils.isEmpty(code)) {
  210. return OutDTO.error500("参数缺失");
  211. }
  212. List<LivingRoomDTO> rooms = getRooms(code);
  213. if (CollectionUtils.isEmpty(rooms)) {
  214. return OutDTO.ok().put("users", new ArrayList<>()).put("totalNum", 0);
  215. }
  216. Integer merchantId = rooms.get(0).getMerchantId();
  217. // String url = String.format(CommonConfig.baseconfig.getImBaseUrl() + CommonConfig.baseconfig.getImUserUrl(), code);
  218. // String result = commonForestClient.sendGet(url);
  219. String result = livesConfigService.getCachedLiveRoomUser(param.getCode());
  220. if (result == null) {
  221. log.warn("查询直播间用户异常:{}", result);
  222. return OutDTO.ok().put("users", new ArrayList<>()).put("totalNum", 0);
  223. }
  224. List<Integer> userIds = JSONTools.jsonStr2obj(result, List.class);
  225. UserInfo user = UserUtils.getSimpleUserInfo();
  226. boolean isOwnerMerchant = user != null && Objects.equals(merchantId, user.getMerchantId());
  227. log.debug("查询直播间用户结果:{}", result);
  228. List<ImUserDTO> users = livesConfigService.findLivingRoomUser(merchantId, userIds, isOwnerMerchant, param.getPageNo(), param.getPageSize());
  229. return OutDTO.ok().put("users", users).put("totalNum", userIds.size());
  230. }
  231. @ApiOperation("获取商城直播画面")
  232. @PostMapping("/{version}/screen")
  233. public OutDTO getlivingScreen() {
  234. // 查询直播中的商城直播
  235. List<CardGroupLivesConfig> shopLivings = livesConfigService.getShopLivings();
  236. if(CollectionUtils.isEmpty(shopLivings)){
  237. return OutDTO.ok().put("livingScreens", null);
  238. }
  239. List<LivingScreenDTO> livingScreens = Lists.newArrayList();
  240. shopLivings.forEach(living ->{
  241. String key = "simple_living_" + living.getCode();
  242. if(redisUtils.hasKey(key)){
  243. LivingScreenDTO livingScreenDTO = new LivingScreenDTO();
  244. Map<Object, Object> playbackData = redisUtils.hmget(key);
  245. livingScreenDTO.setCaptureTime(playbackData.get("capture_time").toString());
  246. livingScreenDTO.setCaptureUrl(playbackData.get("capture_url").toString());
  247. livingScreenDTO.setCode(living.getCode());
  248. livingScreens.add(livingScreenDTO);
  249. }
  250. });
  251. return OutDTO.ok().put("livingScreens", livingScreens);
  252. }
  253. @ApiOperation("获取直播间点赞")
  254. @PostMapping("/{version}/like/{code}")
  255. @ApiLimitRule(seconds = 1, limitCount = 30, msg = "网络失败,请重试!")
  256. public OutDTO getLivingRoomLike(@PathVariable("code") String code) {
  257. if (!redisUtils.hHasKey(Constants.LIVING_ROOM_LIKE_KEY, code)) {
  258. List<CardGroupLivesConfig> livingRoomDto = cardGroupLivesConfigMapper.selectLivingRoomByCode(code);
  259. // 写缓存
  260. int liveLikeNum = 0;
  261. if(livingRoomDto != null && !livingRoomDto.isEmpty()){
  262. liveLikeNum = livingRoomDto.get(0).getLiveLikeNum();
  263. }
  264. redisUtils.hset(Constants.LIVING_ROOM_LIKE_KEY, code, liveLikeNum, 24 * 60 * 60);
  265. }
  266. return OutDTO.ok().put("count", redisUtils.hget(Constants.LIVING_ROOM_LIKE_KEY, code));
  267. }
  268. @ApiOperation("点赞直播间")
  269. @PostMapping("/{version}/like")
  270. @ApiLimitRule(seconds = 1, limitCount = 30, msg = "网络失败,请重试!")
  271. public OutDTO likeLivingRoom(@Validated @RequestBody UserLikeParam param) {
  272. //1000465 HS-APP【直播间竞价】并发场景下当前端传的num参数大于50时,则接口最大有效值为50(防止被恶意修改参数)
  273. int likeNum = param.getNum() >= 50 ? 50 : param.getNum();
  274. //region LIVING_ROOM_LIKE_KEY logic
  275. int finalLikeNum = likeNum;
  276. if (redisUtils.hHasKey(Constants.LIVING_ROOM_LIKE_KEY, param.getCode())) {
  277. finalLikeNum = (int)redisUtils.hget(Constants.LIVING_ROOM_LIKE_KEY, param.getCode()) +finalLikeNum;
  278. }
  279. else{
  280. List<CardGroupLivesConfig> livingRoomDtoList = cardGroupLivesConfigMapper.selectLivingRoomByCode(param.getCode());
  281. if(livingRoomDtoList != null && !livingRoomDtoList.isEmpty()){
  282. finalLikeNum = (livingRoomDtoList.get(0).getLiveLikeNum()==null?0:livingRoomDtoList.get(0).getLiveLikeNum()) + likeNum;
  283. }
  284. }
  285. // 写LIVING_ROOM_LIKE_KEY缓存
  286. redisUtils.hset(Constants.LIVING_ROOM_LIKE_KEY, param.getCode(), finalLikeNum, 24 * 60 * 60);
  287. //endregion
  288. //region LIVING_ROOM_LIKE_USER_KEY logic
  289. UserInfo user = UserUtils.getSimpleUserInfo(true);
  290. // 写LIVING_ROOM_LIKE_USER_KEY缓存
  291. int finalUserLikeNum = likeNum;
  292. if (redisUtils.hHasKey(Constants.LIVING_ROOM_LIKE_USER_KEY, user.getId()+param.getCode())) {
  293. finalUserLikeNum = (int)redisUtils.hget(Constants.LIVING_ROOM_LIKE_USER_KEY, user.getId()+param.getCode()) +finalUserLikeNum;
  294. }
  295. else{
  296. CardGroupLivesLike livesLike = cardGroupLivesLikeMapper.selectCardGroupLivesLikeById(user.getId(),param.getCode());
  297. if (livesLike != null) {
  298. finalUserLikeNum = livesLike.getLike_num()==null?0:livesLike.getLike_num() + likeNum;
  299. }
  300. }
  301. // 写LIVING_ROOM_LIKE_USER_KEY缓存
  302. redisUtils.hset(Constants.LIVING_ROOM_LIKE_USER_KEY, user.getId()+param.getCode(), finalUserLikeNum, 24 * 60 * 60);
  303. //endregion
  304. // 定时推给db更新
  305. liveLikeDBUpdateService.handleRequest(param.getCode(),user.getId());
  306. // 定时推给im更新
  307. liveLikePushService.handleRequest(param.getCode());
  308. return OutDTO.ok().put("count", finalLikeNum);
  309. }
  310. @ApiOperation("首页直播商家排序")
  311. @PostMapping("/{version}/sorts")
  312. public OutDTO getLiveMerchantList(@RequestBody CardGroupLivesConfig cardGroupLivesConfig) {
  313. if(cardGroupLivesConfig == null){
  314. cardGroupLivesConfig = new CardGroupLivesConfig();
  315. }
  316. UserInfo userInfo = UserUtils.getSimpleUserInfo(false);
  317. if(userInfo != null){
  318. cardGroupLivesConfig.setAppUserId(userInfo.getId());
  319. }
  320. // 查询直播中的商家直播信息
  321. List<LiveMerchantDTO> liveMerchantList = livesConfigService.getLiveMerchantList(cardGroupLivesConfig);
  322. return OutDTO.ok().put("liveMerchantList", liveMerchantList);
  323. }
  324. @ApiVersion(1.0)
  325. @ApiOperation("根据paniniListId查询直播中的商品/拍品")
  326. @PostMapping("/{version}/panini/{paniniListId}")
  327. public OutDTO getLiveByPaniniListId(@PathVariable Long paniniListId) {
  328. // 根据paniniListId查询直播中的商品
  329. List<LiveMerchantDTO> paniniLives = livesConfigService.getLiveByPaniniListId(paniniListId);
  330. return OutDTO.ok().put("paniniLives", paniniLives);
  331. }
  332. @PostMapping("/{version}/main/place/list")
  333. @ApiVersion(1.0)
  334. @ApiOperation(value = "查询双11主会场直播列表")
  335. public OutDTO getMainPlaceLiveConfigs(@RequestBody InDto inDto){
  336. PageHelper.startPage(inDto.getPageNo(),inDto.getPageSize());
  337. List<LiveListDTO> livesConfigList = livesConfigService.getLivesByStartTime();
  338. return OutDTO.ok().put("livesConfigList",livesConfigList);
  339. }
  340. }