新增用户级别、用户查询接口

This commit is contained in:
wanggeng 2021-11-14 16:26:03 +08:00
parent 8f8e5231d2
commit 110c2d1211
14 changed files with 6559 additions and 8 deletions

View File

@ -29,7 +29,7 @@ public class UserExpandAppController extends DefaultBaseController {
@ApiOperation(value = "修改拓展属性", notes = "修改拓展属性接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "userId", value = "队伍类型ID", paramType = "path")
@ApiImplicitParam(name = "userId", value = "用户ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@PutMapping("update/{userId}")
@ -41,7 +41,7 @@ public class UserExpandAppController extends DefaultBaseController {
@ApiOperation(value = "拓展属性详情", notes = "拓展属性详情接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "teamTypeId", value = "队伍类型ID", paramType = "path")
@ApiImplicitParam(name = "userId", value = "用户ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("get/{userId}")

View File

@ -0,0 +1,77 @@
package cn.com.tenlion.usercenter.controller.resource.userexpand;
import cn.com.tenlion.usercenter.pojo.dtos.userexpand.UserExpandDTO;
import cn.com.tenlion.usercenter.pojo.vos.userexpand.UserExpandVO;
import cn.com.tenlion.usercenter.service.userexpand.IUserExpandService;
import ink.wgink.annotation.CheckRequestBodyAnnotation;
import ink.wgink.common.base.DefaultBaseController;
import ink.wgink.exceptions.ParamsException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.pojo.result.ErrorResult;
import ink.wgink.pojo.result.SuccessResult;
import ink.wgink.pojo.vos.IdsVO;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @ClassName: UserExpandController
* @Description: 用户拓展
* @Author: CodeFactory
* @Date: 2021-10-21 11:38:52
* @Version: 3.0
**/
@Api(tags = ISystemConstant.API_TAGS_RESOURCE_PREFIX + "用户拓展接口")
@RestController
@RequestMapping(ISystemConstant.RESOURCE_PREFIX + "/user-expand")
public class UserExpandResourceController extends DefaultBaseController {
@Autowired
private IUserExpandService userExpandService;
@ApiOperation(value = "修改拓展属性", notes = "修改拓展属性接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "userId", value = "用户ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@PutMapping("update/{userId}")
@CheckRequestBodyAnnotation
public SuccessResult update(@PathVariable("userId") String userId, @RequestBody UserExpandVO userExpandVO) throws Exception {
userExpandService.update(userId, userExpandVO);
return new SuccessResult();
}
@ApiOperation(value = "拓展属性详情", notes = "拓展属性详情接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "userId", value = "用户ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("get/{userId}")
public UserExpandDTO get(@PathVariable("userId") String userId) {
return userExpandService.get(userId);
}
@ApiOperation(value = "拓展属性完整列表", notes = "拓展属性完整列表接口")
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@PostMapping("list-whole/user-ids")
public List<UserExpandDTO> listWholeByUserIds(@RequestBody IdsVO idsVO) {
if (idsVO.getIds().isEmpty()) {
throw new ParamsException("用户ID列表不能为空");
}
return userExpandService.listWholeByUserIds(idsVO.getIds());
}
@ApiOperation(value = "拓展属性完整列表", notes = "拓展属性完整列表接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "roleId", value = "角色ID", paramType = "path", required = true),
@ApiImplicitParam(name = "areaCode", value = "地区编码", paramType = "path", required = true)
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("list-whole/role-id/{roleId}/area-code/{areaCode}")
public List<UserExpandDTO> listWholeByRoleIdAndAreaCode(@PathVariable("roleId") String roleId, @PathVariable("areaCode") String areaCode) {
return userExpandService.listWholeByRoleIdAndAreaCode(roleId, areaCode);
}
}

View File

@ -8,6 +8,7 @@ import ink.wgink.exceptions.UpdateException;
import ink.wgink.interfaces.init.IInitBaseTable;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
/**
@ -61,6 +62,13 @@ public interface IUserExpandDao extends IInitBaseTable {
* @return
* @throws SearchException
*/
UserExpandPO listPO(Map<String, Object> params) throws SearchException;
List<UserExpandPO> listPO(Map<String, Object> params) throws SearchException;
/**
*
* @param params
* @return
* @throws SearchException
*/
List<UserExpandDTO> list(Map<String, Object> params) throws SearchException;
}

View File

@ -1,7 +1,11 @@
package cn.com.tenlion.usercenter.pojo.dtos.userexpand;
import ink.wgink.pojo.dtos.department.DepartmentDTO;
import ink.wgink.pojo.dtos.user.UserDTO;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName: UserExpandDTO
* @Description: 用户拓展
@ -13,6 +17,8 @@ public class UserExpandDTO extends UserDTO {
private String areaCode;
private String areaName;
private Integer userLevel;
private List<DepartmentDTO> departments;
public void setUserDTO(UserDTO userDTO) {
super.setUserDTO(userDTO);
@ -33,4 +39,20 @@ public class UserExpandDTO extends UserDTO {
public void setAreaName(String areaName) {
this.areaName = areaName;
}
public Integer getUserLevel() {
return userLevel == null ? 0 : userLevel;
}
public void setUserLevel(Integer userLevel) {
this.userLevel = userLevel;
}
public List<DepartmentDTO> getDepartments() {
return departments == null ? new ArrayList<>() : departments;
}
public void setDepartments(List<DepartmentDTO> departments) {
this.departments = departments;
}
}

View File

@ -12,6 +12,7 @@ public class UserExpandPO {
private String userId;
private String areaCode;
private String areaName;
private Integer userLevel;
private String creator;
private String gmtCreate;
private String modifier;
@ -41,6 +42,14 @@ public class UserExpandPO {
this.areaName = areaName;
}
public Integer getUserLevel() {
return userLevel == null ? 0 : userLevel;
}
public void setUserLevel(Integer userLevel) {
this.userLevel = userLevel;
}
public String getCreator() {
return creator == null ? "" : creator.trim();
}

View File

@ -20,6 +20,9 @@ public class UserExpandVO {
@ApiModelProperty(name = "areaName", value = "地区名称")
@CheckEmptyAnnotation(name = "地区名称")
private String areaName;
@ApiModelProperty(name = "userLevel", value = "用户级别")
@CheckEmptyAnnotation(name = "用户级别")
private Integer userLevel;
public String getAreaCode() {
return areaCode == null ? "" : areaCode.trim();
@ -36,4 +39,12 @@ public class UserExpandVO {
public void setAreaName(String areaName) {
this.areaName = areaName;
}
public Integer getUserLevel() {
return userLevel == null ? 0 : userLevel;
}
public void setUserLevel(Integer userLevel) {
this.userLevel = userLevel;
}
}

View File

@ -5,6 +5,8 @@ import cn.com.tenlion.usercenter.pojo.pos.userexpand.UserExpandPO;
import cn.com.tenlion.usercenter.pojo.vos.userexpand.UserExpandVO;
import ink.wgink.interfaces.user.IUserExpandBaseService;
import java.util.List;
/**
* @ClassName: IUserExpandService
* @Description: 用户拓展业务
@ -31,4 +33,30 @@ public interface IUserExpandService extends IUserExpandBaseService<UserExpandDTO
*/
UserExpandPO getPO(String userId);
/**
* 拓展属性列表
*
* @param userIds 用户ID列表
* @param areaCode 地区编码
* @return
*/
List<UserExpandDTO> listByUserIdsAndAreaCode(List<String> userIds, String areaCode);
/**
* 拓展属性完整列表
*
* @param userIds
* @return
*/
List<UserExpandDTO> listWholeByUserIds(List<String> userIds);
/**
* 拓展属性完整列表
*
* @param roleId
* @param areaCode
* @return
*/
List<UserExpandDTO> listWholeByRoleIdAndAreaCode(String roleId, String areaCode);
}

View File

@ -7,14 +7,24 @@ import cn.com.tenlion.usercenter.pojo.vos.userexpand.UserExpandVO;
import cn.com.tenlion.usercenter.service.userexpand.IUserExpandService;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.SearchException;
import ink.wgink.interfaces.department.IDepartmentBaseService;
import ink.wgink.interfaces.department.IDepartmentUserBaseService;
import ink.wgink.interfaces.role.IRoleUserBaseService;
import ink.wgink.interfaces.user.IUserBaseService;
import ink.wgink.pojo.ListPage;
import ink.wgink.pojo.dtos.department.DepartmentDTO;
import ink.wgink.pojo.dtos.department.DepartmentUserDTO;
import ink.wgink.pojo.dtos.user.UserDTO;
import ink.wgink.pojo.pos.DepartmentPO;
import ink.wgink.pojo.result.SuccessResultList;
import ink.wgink.util.ArrayListUtil;
import ink.wgink.util.map.HashMapUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -32,6 +42,12 @@ public class UserExpandServiceImpl extends DefaultBaseService implements IUserEx
private IUserBaseService userBaseService;
@Autowired
private IUserExpandDao userExpandDao;
@Autowired
private IDepartmentUserBaseService departmentUserBaseService;
@Autowired
private IDepartmentBaseService departmentBaseService;
@Autowired
private IRoleUserBaseService roleUserBaseService;
@Override
public String getRoute() {
@ -61,17 +77,34 @@ public class UserExpandServiceImpl extends DefaultBaseService implements IUserEx
@Override
public List<UserExpandDTO> listByUserIds(List<String> userIds) {
return null;
List<UserDTO> userDTOs = userBaseService.listByUserIds(userIds);
if (userDTOs.isEmpty()) {
return new ArrayList<>();
}
Map<String, Object> params = getHashMap(2);
params.put("userIds", userIds);
List<UserExpandDTO> userExpandDTOs = list(params);
setUser(userDTOs, userExpandDTOs);
return userExpandDTOs;
}
@Override
public List<UserExpandDTO> listByUsernames(List<String> usernames) {
return null;
List<UserDTO> userDTOs = userBaseService.listByUsernames(usernames);
if (userDTOs.isEmpty()) {
return new ArrayList<>();
}
List<String> userIds = ArrayListUtil.listBeanStringIdValue(userDTOs, "userId", UserDTO.class);
Map<String, Object> params = getHashMap(2);
params.put("userIds", userIds);
List<UserExpandDTO> userExpandDTOs = list(params);
setUser(userDTOs, userExpandDTOs);
return userExpandDTOs;
}
@Override
public List<UserExpandDTO> list(Map<String, Object> params) {
return null;
return userExpandDao.list(params);
}
@Override
@ -124,4 +157,94 @@ public class UserExpandServiceImpl extends DefaultBaseService implements IUserEx
params.put("userId", userId);
return userExpandDao.getPO(params);
}
@Override
public List<UserExpandDTO> listByUserIdsAndAreaCode(List<String> userIds, String areaCode) {
List<UserDTO> userDTOs = userBaseService.listByUserIds(userIds);
if (userDTOs.isEmpty()) {
return new ArrayList<>();
}
Map<String, Object> params = getHashMap(2);
params.put("areaCode", areaCode);
params.put("userIds", userIds);
List<UserExpandDTO> userExpandDTOs = userExpandDao.list(params);
if (userExpandDTOs.isEmpty()) {
return new ArrayList<>();
}
setUser(userDTOs, userExpandDTOs);
return userExpandDTOs;
}
@Override
public List<UserExpandDTO> listWholeByUserIds(List<String> userIds) {
List<UserExpandDTO> userExpandDTOs = listByUserIds(userIds);
setDepartment(userExpandDTOs);
return userExpandDTOs;
}
@Override
public List<UserExpandDTO> listWholeByRoleIdAndAreaCode(String roleId, String areaCode) {
List<String> userIds = roleUserBaseService.listUserId(roleId);
if (userIds.isEmpty()) {
return new ArrayList<>();
}
List<UserExpandDTO> userExpandDTOs = listByUserIdsAndAreaCode(userIds, areaCode);
setDepartment(userExpandDTOs);
return userExpandDTOs;
}
/**
* 设置用户
*
* @param userDTOs
* @param userExpandDTOs
*/
private void setUser(List<UserDTO> userDTOs, List<UserExpandDTO> userExpandDTOs) {
for (UserExpandDTO userExpandDTO : userExpandDTOs) {
for (UserDTO userDTO : userDTOs) {
if (StringUtils.equals(userExpandDTO.getUserId(), userDTO.getUserId())) {
userExpandDTO.setUserDTO(userDTO);
break;
}
}
}
}
/**
* 设置部门
*
* @param userExpandDTOs
*/
private void setDepartment(List<UserExpandDTO> userExpandDTOs) {
if (userExpandDTOs.isEmpty()) {
return;
}
List<DepartmentUserDTO> departmentUserDTOs = departmentUserBaseService.listByUserIds(ArrayListUtil.listBeanStringIdValue(userExpandDTOs, "userId", UserExpandDTO.class));
if (departmentUserDTOs.isEmpty()) {
return;
}
List<DepartmentPO> departmentPOs = departmentBaseService.listPO(ArrayListUtil.listBeanStringIdValue(departmentUserDTOs, "departmentId", DepartmentUserDTO.class));
if (departmentPOs.isEmpty()) {
return;
}
// 合并内容
for (UserExpandDTO userExpandDTO : userExpandDTOs) {
List<DepartmentDTO> departmentDTOs = new ArrayList<>();
for (DepartmentUserDTO departmentUserDTO : departmentUserDTOs) {
if (!StringUtils.equals(userExpandDTO.getUserId(), departmentUserDTO.getUserId())) {
continue;
}
for (DepartmentPO departmentPO : departmentPOs) {
if (!StringUtils.equals(departmentUserDTO.getDepartmentId(), departmentPO.getDepartmentId())) {
continue;
}
DepartmentDTO departmentDTO = new DepartmentDTO();
BeanUtils.copyProperties(departmentPO, departmentDTO);
departmentDTOs.add(departmentDTO);
break;
}
}
userExpandDTO.setDepartments(departmentDTOs);
}
}
}

View File

@ -6,6 +6,7 @@
<result column="user_id" property="userId"/>
<result column="area_code" property="areaCode"/>
<result column="area_name" property="areaName"/>
<result column="user_level" property="userLevel"/>
<result column="creator" property="creator"/>
<result column="gmt_create" property="gmtCreate"/>
<result column="modifier" property="modifier"/>
@ -16,6 +17,7 @@
<result column="user_id" property="userId"/>
<result column="area_code" property="areaCode"/>
<result column="area_name" property="areaName"/>
<result column="user_level" property="userLevel"/>
<result column="gmt_create" property="gmtCreate"/>
</resultMap>
@ -24,8 +26,9 @@
CREATE TABLE IF NOT EXISTS `sys_user_expand` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` char(36) DEFAULT NULL COMMENT '主键',
`area_code` varchar(255) DEFAULT NULL COMMENT '名称',
`area_name` varchar(255) DEFAULT NULL COMMENT '描述',
`area_code` varchar(255) DEFAULT NULL COMMENT '地区编码',
`area_name` varchar(255) DEFAULT NULL COMMENT '地区名称',
`user_level` varchar(255) DEFAULT NULL COMMENT '地区级别',
`creator` char(36) DEFAULT NULL COMMENT '创建人',
`gmt_create` datetime DEFAULT NULL COMMENT '创建时间',
`modifier` char(36) DEFAULT NULL COMMENT '修改人',
@ -41,6 +44,7 @@
user_id,
area_code,
area_name,
user_level,
creator,
gmt_create,
modifier,
@ -49,6 +53,7 @@
#{userId},
#{areaCode},
#{areaName},
#{userLevel},
#{creator},
#{gmtCreate},
#{modifier},
@ -90,6 +95,7 @@
t1.user_id,
t1.area_code,
t1.area_name,
t1.user_level,
t1.gmt_create
FROM
sys_user_expand t1
@ -103,6 +109,7 @@
t1.user_id,
t1.area_code,
t1.area_name,
t1.user_level,
t1.creator,
t1.gmt_create,
t1.modifier,
@ -119,6 +126,7 @@
t1.user_id,
t1.area_code,
t1.area_name,
t1.user_level,
t1.creator,
t1.gmt_create,
t1.modifier,
@ -135,4 +143,23 @@
</if>
</select>
<!-- 列表 -->
<select id="list" parameterType="map" resultMap="userExpandDTO">
SELECT
t1.user_id,
t1.area_code,
t1.area_name,
t1.user_level
FROM
sys_user_expand t1
WHERE
<if test="userIds != null and userIds.size > 0">
AND
t1.user_id IN
<foreach collection="userIds" index="index" open="(" separator="," close=")">
#{userIds[${index}]}
</foreach>
</if>
</select>
</mapper>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,535 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2021 Winlin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-awat-prmise based SRS RTC Publisher.
function SrsRtcPublisherAsync() {
var self = {};
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
self.constraints = {
audio: true,
video: {
width: {ideal: 320, max: 576}
}
};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the publish:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(ip) of answer:
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.publish = async function (url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "sendonly"});
self.pc.addTransceiver("video", {direction: "sendonly"});
var stream = await navigator.mediaDevices.getUserMedia(self.constraints);
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
stream.getTracks().forEach(function (track) {
self.pc.addTrack(track);
// Notify about local track when stream is ok.
self.ontrack && self.ontrack({track: track});
});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function (resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
console.log("Generated offer: ", data);
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType: 'application/json', dataType: 'json'
}).done(function (data) {
console.log("Got answer: ", data);
if (data.code) {
reject(data);
return;
}
resolve(data);
}).fail(function (reason) {
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the publisher.
self.close = function () {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got local stream.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
self.ontrack = function (event) {
// Add track to stream of SDK.
self.stream.addTrack(event.track);
};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/publish/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).substr(0, 7)
};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
// parse the vhost in the params of app, that srs supports.
app = app.replace("...vhost...", "?vhost=");
if (app.indexOf("?") >= 0) {
var params = app.substr(app.indexOf("?"));
app = app.substr(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.substr(0, vhost.indexOf("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.substr(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
if (query_string.indexOf("?") >= 0) {
query_string = query_string.split("?")[1];
}
var queries = query_string.split("&");
for (var i = 0; i < queries.length; i++) {
var elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
self.pc = new RTCPeerConnection(null);
// To keep api consistent between player and publisher.
// @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addStream#Migrating_to_addTrack
// @see https://webrtc.org/getting-started/media-devices
self.stream = new MediaStream();
return self;
}
// Depends on adapter-7.4.0.min.js from https://github.com/webrtc/adapter
// Async-await-promise based SRS RTC Player.
function SrsRtcPlayerAsync() {
var self = {};
// @see https://github.com/rtcdn/rtcdn-draft
// @url The WebRTC url to play with, for example:
// webrtc://r.ossrs.net/live/livestream
// or specifies the API port:
// webrtc://r.ossrs.net:11985/live/livestream
// or autostart the play:
// webrtc://r.ossrs.net/live/livestream?autostart=true
// or change the app from live to myapp:
// webrtc://r.ossrs.net:11985/myapp/livestream
// or change the stream from livestream to mystream:
// webrtc://r.ossrs.net:11985/live/mystream
// or set the api server to myapi.domain.com:
// webrtc://myapi.domain.com/live/livestream
// or set the candidate(ip) of answer:
// webrtc://r.ossrs.net/live/livestream?eip=39.107.238.185
// or force to access https API:
// webrtc://r.ossrs.net/live/livestream?schema=https
// or use plaintext, without SRTP:
// webrtc://r.ossrs.net/live/livestream?encrypt=false
// or any other information, will pass-by in the query:
// webrtc://r.ossrs.net/live/livestream?vhost=xxx
// webrtc://r.ossrs.net/live/livestream?token=xxx
self.play = async function(url) {
var conf = self.__internal.prepareUrl(url);
self.pc.addTransceiver("audio", {direction: "recvonly"});
self.pc.addTransceiver("video", {direction: "recvonly"});
var offer = await self.pc.createOffer();
await self.pc.setLocalDescription(offer);
var session = await new Promise(function(resolve, reject) {
// @see https://github.com/rtcdn/rtcdn-draft
var data = {
api: conf.apiUrl, tid: conf.tid, streamurl: conf.streamUrl,
clientip: null, sdp: offer.sdp
};
console.log("Generated offer: ", data);
$.ajax({
type: "POST", url: conf.apiUrl, data: JSON.stringify(data),
contentType:'application/json', dataType: 'json'
}).done(function(data) {
console.log("Got answer: ", data);
if (data.code) {
reject(data); return;
}
resolve(data);
}).fail(function(reason){
reject(reason);
});
});
await self.pc.setRemoteDescription(
new RTCSessionDescription({type: 'answer', sdp: session.sdp})
);
session.simulator = conf.schema + '//' + conf.urlObject.server + ':' + conf.port + '/rtc/v1/nack/';
return session;
};
// Close the player.
self.close = function() {
self.pc && self.pc.close();
self.pc = null;
};
// The callback when got remote track.
// Note that the onaddstream is deprecated, @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/onaddstream
self.ontrack = function (event) {
// https://webrtc.org/getting-started/remote-streams
self.stream.addTrack(event.track);
};
// Internal APIs.
self.__internal = {
defaultPath: '/rtc/v1/play/',
prepareUrl: function (webrtcUrl) {
var urlObject = self.__internal.parse(webrtcUrl);
// If user specifies the schema, use it as API schema.
var schema = urlObject.user_query.schema;
schema = schema ? schema + ':' : window.location.protocol;
var port = urlObject.port || 1985;
if (schema === 'https:') {
port = urlObject.port || 443;
}
// @see https://github.com/rtcdn/rtcdn-draft
var api = urlObject.user_query.play || self.__internal.defaultPath;
if (api.lastIndexOf('/') !== api.length - 1) {
api += '/';
}
apiUrl = schema + '//' + urlObject.server + ':' + port + api;
for (var key in urlObject.user_query) {
if (key !== 'api' && key !== 'play') {
apiUrl += '&' + key + '=' + urlObject.user_query[key];
}
}
// Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
var apiUrl = apiUrl.replace(api + '&', api + '?');
var streamUrl = urlObject.url;
return {
apiUrl: apiUrl, streamUrl: streamUrl, schema: schema, urlObject: urlObject, port: port,
tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).substr(0, 7)
};
},
parse: function (url) {
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
var a = document.createElement("a");
a.href = url.replace("rtmp://", "http://")
.replace("webrtc://", "http://")
.replace("rtc://", "http://");
var vhost = a.hostname;
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
// parse the vhost in the params of app, that srs supports.
app = app.replace("...vhost...", "?vhost=");
if (app.indexOf("?") >= 0) {
var params = app.substr(app.indexOf("?"));
app = app.substr(0, app.indexOf("?"));
if (params.indexOf("vhost=") > 0) {
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
if (vhost.indexOf("&") > 0) {
vhost = vhost.substr(0, vhost.indexOf("&"));
}
}
}
// when vhost equals to server, and server is ip,
// the vhost is __defaultVhost__
if (a.hostname === vhost) {
var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
if (re.test(a.hostname)) {
vhost = "__defaultVhost__";
}
}
// parse the schema
var schema = "rtmp";
if (url.indexOf("://") > 0) {
schema = url.substr(0, url.indexOf("://"));
}
var port = a.port;
if (!port) {
if (schema === 'http') {
port = 80;
} else if (schema === 'https') {
port = 443;
} else if (schema === 'rtmp') {
port = 1935;
}
}
var ret = {
url: url,
schema: schema,
server: a.hostname, port: port,
vhost: vhost, app: app, stream: stream
};
self.__internal.fill_query(a.search, ret);
// For webrtc API, we use 443 if page is https, or schema specified it.
if (!ret.port) {
if (schema === 'webrtc' || schema === 'rtc') {
if (ret.user_query.schema === 'https') {
ret.port = 443;
} else if (window.location.href.indexOf('https://') === 0) {
ret.port = 443;
} else {
// For WebRTC, SRS use 1985 as default API port.
ret.port = 1985;
}
}
}
return ret;
},
fill_query: function (query_string, obj) {
// pure user query object.
obj.user_query = {};
if (query_string.length === 0) {
return;
}
// split again for angularjs.
if (query_string.indexOf("?") >= 0) {
query_string = query_string.split("?")[1];
}
var queries = query_string.split("&");
for (var i = 0; i < queries.length; i++) {
var elem = queries[i];
var query = elem.split("=");
obj[query[0]] = query[1];
obj.user_query[query[0]] = query[1];
}
// alias domain for vhost.
if (obj.domain) {
obj.vhost = obj.domain;
}
}
};
self.pc = new RTCPeerConnection(null);
// Create a stream to add track to the stream, @see https://webrtc.org/getting-started/remote-streams
self.stream = new MediaStream();
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack
self.pc.ontrack = function(event) {
if (self.ontrack) {
self.ontrack(event);
}
};
return self;
}
// Format the codec of RTCRtpSender, kind(audio/video) is optional filter.
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#getting_the_supported_codecs
function SrsRtcFormatSenders(senders, kind) {
var codecs = [];
senders.forEach(function (sender) {
var params = sender.getParameters();
params && params.codecs && params.codecs.forEach(function(c) {
if (kind && sender.track.kind !== kind) {
return;
}
if (c.mimeType.indexOf('/red') > 0 || c.mimeType.indexOf('/rtx') > 0 || c.mimeType.indexOf('/fec') > 0) {
return;
}
var s = '';
s += c.mimeType.replace('audio/', '').replace('video/', '');
s += ', ' + c.clockRate + 'HZ';
if (sender.track.kind === "audio") {
s += ', channels: ' + c.channels;
}
s += ', pt: ' + c.payloadType;
codecs.push(s);
});
});
return codecs.join(", ");
}

View File

@ -0,0 +1,149 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2021 Winlin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
'use strict';
// Async-await-promise based SRS RTC Signaling.
function SrsRtcSignalingAsync() {
var self = {};
// The schema is ws or wss, host is ip or ip:port, display is nickname
// of user to join the room.
self.connect = async function (schema, host, room, display) {
var url = schema + '://' + host + '/sig/v1/rtc';
self.ws = new WebSocket(url + '?room=' + room + '&display=' + display);
self.ws.onmessage = function(event) {
var r = JSON.parse(event.data);
console.log('r', r);
var promise = self._internals.msgs[r.tid];
if (promise) {
promise.resolve(r.msg);
delete self._internals.msgs[r.tid];
} else {
self.onmessage(r.msg);
}
};
return new Promise(function (resolve, reject) {
self.ws.onopen = function (event) {
resolve(event);
};
self.ws.onerror = function (event) {
reject(event);
};
});
};
// The message is a json object.
self.send = async function (message) {
return new Promise(function (resolve, reject) {
var r = {tid: Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).substr(0, 7), msg: message};
self._internals.msgs[r.tid] = {resolve: resolve, reject: reject};
self.ws.send(JSON.stringify(r));
});
};
self.close = function () {
self.ws && self.ws.close();
self.ws = null;
for (const tid in self._internals.msgs) {
var promise = self._internals.msgs[tid];
promise.reject('close');
}
};
// The callback when got messages from signaling server.
self.onmessage = function (msg) {
};
self._internals = {
// Key is tid, value is object {resolve, reject, response}.
msgs: {}
};
return self;
}
// Parse params in query string.
function SrsRtcSignalingParse(location) {
let query = location.href.split('?')[1];
query = query? '?' + query : null;
let wsSchema = location.href.split('wss=')[1];
wsSchema = wsSchema? wsSchema.split('&')[0] : (location.protocol === 'http:'? 'ws' : 'wss');
let wsHost = location.href.split('wsh=')[1];
wsHost = wsHost? wsHost.split('&')[0] : location.hostname;
let wsPort = location.href.split('wsp=')[1];
wsPort = wsPort? wsPort.split('&')[0] : location.host.split(':')[1];
let host = location.href.split('host=')[1];
host = host? host.split('&')[0] : location.hostname;
let room = location.href.split('room=')[1];
room = room? room.split('&')[0] : null;
let display = location.href.split('display=')[1];
display = display? display.split('&')[0] : Number(parseInt(new Date().getTime()*Math.random()*100)).toString(16).toString(16).substr(0, 7);
let autostart = location.href.split('autostart=')[1];
autostart = autostart && autostart.split('&')[0] === 'true';
// Remove data in query.
let rawQuery = query;
if (query) {
query = query.replace('wss=' + wsSchema, '');
query = query.replace('wsh=' + wsHost, '');
query = query.replace('wsp=' + wsPort, '');
query = query.replace('host=' + host, '');
if (room) {
query = query.replace('room=' + room, '');
}
query = query.replace('display=' + display, '');
query = query.replace('autostart=' + autostart, '');
while (query.indexOf('&&') >= 0) {
query = query.replace('&&', '&');
}
query = query.replace('?&', '?');
if (query.lastIndexOf('?') === query.length - 1) {
query = query.substr(0, query.length - 1);
}
if (query.lastIndexOf('&') === query.length - 1) {
query = query.substr(0, query.length - 1);
}
}
// Regenerate the host of websocket.
wsHost = wsPort? wsHost.split(':')[0] + ':' + wsPort : wsHost;
return {
query: query, rawQuery: rawQuery, wsSchema: wsSchema, wsHost: wsHost, host: host,
room: room, display: display, autostart: autostart,
};
}

View File

@ -22,6 +22,16 @@
<input type="text" id="areaName" name="areaName" class="layui-input" value="" placeholder="请选择地区" maxlength="255" readonly lay-verify="required" style="cursor:pointer;">
</div>
</div>
<div class="layui-form-item" pane>
<label class="layui-form-label">用户级别</label>
<div class="layui-input-block">
<input type="radio" name="userLevel" value="1" title="1级" checked>
<input type="radio" name="userLevel" value="2" title="2级">
<input type="radio" name="userLevel" value="3" title="3级">
<input type="radio" name="userLevel" value="4" title="4级">
<input type="radio" name="userLevel" value="5" title="5级">
</div>
</div>
<div class="layui-form-item layui-layout-admin">
<div class="layui-input-block">
<div class="layui-footer" style="left: 0;">