Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
GeoFlyApi
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
GeoFly
GeoFlyApi
Commits
e1262cc7
Commit
e1262cc7
authored
Sep 08, 2026
by
真的三个金的鑫
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: WSS 按用户裁剪设备推送,并支持编辑用户角色
Co-authored-by: Cursor <cursoragent@cursor.com>
parent
b49d54c6
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
269 additions
and
11 deletions
+269
-11
sample/src/main/java/com/dji/sample/component/websocket/config/AuthPrincipalHandler.java
+3
-0
sample/src/main/java/com/dji/sample/component/websocket/config/MyWebSocketHandler.java
+7
-3
sample/src/main/java/com/dji/sample/component/websocket/service/IWebSocketManageService.java
+6
-0
sample/src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketManageServiceImpl.java
+21
-0
sample/src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketMessageServiceImpl.java
+72
-0
sample/src/main/java/com/dji/sample/manage/controller/UserController.java
+1
-1
sample/src/main/java/com/dji/sample/manage/service/IDeviceService.java
+6
-0
sample/src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java
+76
-7
sample/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java
+77
-0
No files found.
sample/src/main/java/com/dji/sample/component/websocket/config/AuthPrincipalHandler.java
View file @
e1262cc7
...
@@ -56,6 +56,8 @@ public class AuthPrincipalHandler extends DefaultHandshakeHandler {
...
@@ -56,6 +56,8 @@ public class AuthPrincipalHandler extends DefaultHandshakeHandler {
* @param attributes
* @param attributes
* @return
* @return
*/
*/
public
static
final
String
CLAIM_ATTR
=
"CUSTOM_CLAIM"
;
@Override
@Override
protected
Principal
determineUser
(
ServerHttpRequest
request
,
WebSocketHandler
wsHandler
,
Map
<
String
,
Object
>
attributes
)
{
protected
Principal
determineUser
(
ServerHttpRequest
request
,
WebSocketHandler
wsHandler
,
Map
<
String
,
Object
>
attributes
)
{
if
(
request
instanceof
ServletServerHttpRequest
)
{
if
(
request
instanceof
ServletServerHttpRequest
)
{
...
@@ -68,6 +70,7 @@ public class AuthPrincipalHandler extends DefaultHandshakeHandler {
...
@@ -68,6 +70,7 @@ public class AuthPrincipalHandler extends DefaultHandshakeHandler {
return
()
->
null
;
return
()
->
null
;
}
}
attributes
.
put
(
CLAIM_ATTR
,
claim
);
return
()
->
claim
.
getWorkspaceId
()
+
"/"
+
claim
.
getUserType
()
+
"/"
+
claim
.
getId
();
return
()
->
claim
.
getWorkspaceId
()
+
"/"
+
claim
.
getUserType
()
+
"/"
+
claim
.
getId
();
}
}
return
()
->
null
;
return
()
->
null
;
...
...
sample/src/main/java/com/dji/sample/component/websocket/config/MyWebSocketHandler.java
View file @
e1262cc7
...
@@ -42,11 +42,15 @@ public class MyWebSocketHandler extends WebSocketDefaultHandler {
...
@@ -42,11 +42,15 @@ public class MyWebSocketHandler extends WebSocketDefaultHandler {
@Override
@Override
public
void
afterConnectionEstablished
(
WebSocketSession
session
)
throws
Exception
{
public
void
afterConnectionEstablished
(
WebSocketSession
session
)
throws
Exception
{
Principal
principal
=
session
.
getPrincipal
();
Principal
principal
=
session
.
getPrincipal
();
String
principalName
=
principal
.
getName
()
;
String
principalName
=
principal
!=
null
?
principal
.
getName
()
:
null
;
if
(
StringUtils
.
hasText
(
principalName
)
&&
!
principalName
.
startsWith
(
"temp-"
))
{
if
(
StringUtils
.
hasText
(
principalName
)
&&
!
principalName
.
startsWith
(
"temp-"
))
{
webSocketManageService
.
put
(
principalName
,
new
MyConcurrentWebSocketSession
(
session
));
CustomClaim
claim
=
(
CustomClaim
)
session
.
getAttributes
().
get
(
AuthPrincipalHandler
.
CLAIM_ATTR
);
webSocketManageService
.
put
(
principalName
,
new
MyConcurrentWebSocketSession
(
session
),
claim
);
authenticatedSessions
.
put
(
session
.
getId
(),
true
);
authenticatedSessions
.
put
(
session
.
getId
(),
true
);
if
(
claim
!=
null
)
{
sessionClaims
.
put
(
session
.
getId
(),
claim
);
}
// 记录 principalName 用于 afterConnectionClosed 时清理
// 记录 principalName 用于 afterConnectionClosed 时清理
sessionPrincipalNames
.
put
(
session
.
getId
(),
principalName
);
sessionPrincipalNames
.
put
(
session
.
getId
(),
principalName
);
log
.
debug
(
"{} is connected (pre-authenticated). ID: {}. WebSocketSession[current count: {}]"
,
log
.
debug
(
"{} is connected (pre-authenticated). ID: {}. WebSocketSession[current count: {}]"
,
...
@@ -107,7 +111,7 @@ public class MyWebSocketHandler extends WebSocketDefaultHandler {
...
@@ -107,7 +111,7 @@ public class MyWebSocketHandler extends WebSocketDefaultHandler {
authenticatedSessions
.
put
(
sessionId
,
true
);
authenticatedSessions
.
put
(
sessionId
,
true
);
sessionClaims
.
put
(
sessionId
,
claim
);
sessionClaims
.
put
(
sessionId
,
claim
);
webSocketManageService
.
put
(
key
,
new
MyConcurrentWebSocketSession
(
session
));
webSocketManageService
.
put
(
key
,
new
MyConcurrentWebSocketSession
(
session
)
,
claim
);
log
.
debug
(
"Session {} authenticated successfully. User: {}"
,
sessionId
,
key
);
log
.
debug
(
"Session {} authenticated successfully. User: {}"
,
sessionId
,
key
);
...
...
sample/src/main/java/com/dji/sample/component/websocket/service/IWebSocketManageService.java
View file @
e1262cc7
package
com
.
dji
.
sample
.
component
.
websocket
.
service
;
package
com
.
dji
.
sample
.
component
.
websocket
.
service
;
import
com.dji.sample.common.model.CustomClaim
;
import
com.dji.sample.component.websocket.config.MyConcurrentWebSocketSession
;
import
com.dji.sample.component.websocket.config.MyConcurrentWebSocketSession
;
import
java.util.Collection
;
import
java.util.Collection
;
import
java.util.Optional
;
/**
/**
* @author sean
* @author sean
...
@@ -13,11 +15,15 @@ public interface IWebSocketManageService {
...
@@ -13,11 +15,15 @@ public interface IWebSocketManageService {
void
put
(
String
key
,
MyConcurrentWebSocketSession
val
);
void
put
(
String
key
,
MyConcurrentWebSocketSession
val
);
void
put
(
String
key
,
MyConcurrentWebSocketSession
val
,
CustomClaim
claim
);
void
remove
(
String
key
,
String
sessionId
);
void
remove
(
String
key
,
String
sessionId
);
Collection
<
MyConcurrentWebSocketSession
>
getValueWithWorkspace
(
String
workspaceId
);
Collection
<
MyConcurrentWebSocketSession
>
getValueWithWorkspace
(
String
workspaceId
);
Collection
<
MyConcurrentWebSocketSession
>
getValueWithWorkspaceAndUserType
(
String
workspaceId
,
Integer
userType
);
Collection
<
MyConcurrentWebSocketSession
>
getValueWithWorkspaceAndUserType
(
String
workspaceId
,
Integer
userType
);
Optional
<
CustomClaim
>
getClaim
(
String
sessionId
);
Long
getConnectedCount
();
Long
getConnectedCount
();
}
}
sample/src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketManageServiceImpl.java
View file @
e1262cc7
package
com
.
dji
.
sample
.
component
.
websocket
.
service
.
impl
;
package
com
.
dji
.
sample
.
component
.
websocket
.
service
.
impl
;
import
com.dji.sample.common.model.CustomClaim
;
import
com.dji.sample.component.redis.RedisConst
;
import
com.dji.sample.component.redis.RedisConst
;
import
com.dji.sample.component.redis.RedisOpsUtils
;
import
com.dji.sample.component.redis.RedisOpsUtils
;
import
com.dji.sample.component.websocket.config.MyConcurrentWebSocketSession
;
import
com.dji.sample.component.websocket.config.MyConcurrentWebSocketSession
;
...
@@ -12,6 +13,7 @@ import org.springframework.util.StringUtils;
...
@@ -12,6 +13,7 @@ import org.springframework.util.StringUtils;
import
java.util.Collection
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.Objects
;
import
java.util.Objects
;
import
java.util.Optional
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
...
@@ -26,8 +28,15 @@ public class WebSocketManageServiceImpl implements IWebSocketManageService {
...
@@ -26,8 +28,15 @@ public class WebSocketManageServiceImpl implements IWebSocketManageService {
private
static
final
ConcurrentHashMap
<
String
,
MyConcurrentWebSocketSession
>
SESSIONS
=
new
ConcurrentHashMap
<>(
16
);
private
static
final
ConcurrentHashMap
<
String
,
MyConcurrentWebSocketSession
>
SESSIONS
=
new
ConcurrentHashMap
<>(
16
);
private
static
final
ConcurrentHashMap
<
String
,
CustomClaim
>
SESSION_CLAIMS
=
new
ConcurrentHashMap
<>(
16
);
@Override
@Override
public
void
put
(
String
key
,
MyConcurrentWebSocketSession
val
)
{
public
void
put
(
String
key
,
MyConcurrentWebSocketSession
val
)
{
put
(
key
,
val
,
null
);
}
@Override
public
void
put
(
String
key
,
MyConcurrentWebSocketSession
val
,
CustomClaim
claim
)
{
String
[]
name
=
key
.
split
(
"/"
);
String
[]
name
=
key
.
split
(
"/"
);
if
(
name
.
length
!=
3
)
{
if
(
name
.
length
!=
3
)
{
log
.
debug
(
"The key is out of format. [{workspaceId}/{userType}/{userId}]"
);
log
.
debug
(
"The key is out of format. [{workspaceId}/{userType}/{userId}]"
);
...
@@ -39,6 +48,9 @@ public class WebSocketManageServiceImpl implements IWebSocketManageService {
...
@@ -39,6 +48,9 @@ public class WebSocketManageServiceImpl implements IWebSocketManageService {
RedisOpsUtils
.
hashSet
(
workspaceKey
,
sessionId
,
name
[
2
]);
RedisOpsUtils
.
hashSet
(
workspaceKey
,
sessionId
,
name
[
2
]);
RedisOpsUtils
.
hashSet
(
userTypeKey
,
sessionId
,
name
[
2
]);
RedisOpsUtils
.
hashSet
(
userTypeKey
,
sessionId
,
name
[
2
]);
SESSIONS
.
put
(
sessionId
,
val
);
SESSIONS
.
put
(
sessionId
,
val
);
if
(
claim
!=
null
)
{
SESSION_CLAIMS
.
put
(
sessionId
,
claim
);
}
RedisOpsUtils
.
expireKey
(
workspaceKey
,
RedisConst
.
WEBSOCKET_ALIVE_SECOND
);
RedisOpsUtils
.
expireKey
(
workspaceKey
,
RedisConst
.
WEBSOCKET_ALIVE_SECOND
);
RedisOpsUtils
.
expireKey
(
userTypeKey
,
RedisConst
.
WEBSOCKET_ALIVE_SECOND
);
RedisOpsUtils
.
expireKey
(
userTypeKey
,
RedisConst
.
WEBSOCKET_ALIVE_SECOND
);
}
}
...
@@ -53,6 +65,15 @@ public class WebSocketManageServiceImpl implements IWebSocketManageService {
...
@@ -53,6 +65,15 @@ public class WebSocketManageServiceImpl implements IWebSocketManageService {
RedisOpsUtils
.
hashDel
(
RedisConst
.
WEBSOCKET_PREFIX
+
name
[
0
],
new
String
[]
{
sessionId
});
RedisOpsUtils
.
hashDel
(
RedisConst
.
WEBSOCKET_PREFIX
+
name
[
0
],
new
String
[]
{
sessionId
});
RedisOpsUtils
.
hashDel
(
RedisConst
.
WEBSOCKET_PREFIX
+
UserTypeEnum
.
find
(
Integer
.
parseInt
(
name
[
1
])).
getDesc
(),
new
String
[]
{
sessionId
});
RedisOpsUtils
.
hashDel
(
RedisConst
.
WEBSOCKET_PREFIX
+
UserTypeEnum
.
find
(
Integer
.
parseInt
(
name
[
1
])).
getDesc
(),
new
String
[]
{
sessionId
});
SESSIONS
.
remove
(
sessionId
);
SESSIONS
.
remove
(
sessionId
);
SESSION_CLAIMS
.
remove
(
sessionId
);
}
@Override
public
Optional
<
CustomClaim
>
getClaim
(
String
sessionId
)
{
if
(!
StringUtils
.
hasText
(
sessionId
))
{
return
Optional
.
empty
();
}
return
Optional
.
ofNullable
(
SESSION_CLAIMS
.
get
(
sessionId
));
}
}
@Override
@Override
...
...
sample/src/main/java/com/dji/sample/component/websocket/service/impl/WebSocketMessageServiceImpl.java
View file @
e1262cc7
package
com
.
dji
.
sample
.
component
.
websocket
.
service
.
impl
;
package
com
.
dji
.
sample
.
component
.
websocket
.
service
.
impl
;
import
com.dji.sample.common.model.CustomClaim
;
import
com.dji.sample.component.websocket.config.MyConcurrentWebSocketSession
;
import
com.dji.sample.component.websocket.config.MyConcurrentWebSocketSession
;
import
com.dji.sample.component.websocket.model.BizCodeEnum
;
import
com.dji.sample.component.websocket.service.IWebSocketManageService
;
import
com.dji.sample.component.websocket.service.IWebSocketManageService
;
import
com.dji.sample.component.websocket.service.IWebSocketMessageService
;
import
com.dji.sample.component.websocket.service.IWebSocketMessageService
;
import
com.dji.sample.manage.model.dto.TelemetryDTO
;
import
com.dji.sample.manage.model.dto.TopologyDeviceDTO
;
import
com.dji.sample.manage.service.IDeviceService
;
import
com.dji.sdk.websocket.WebSocketMessageResponse
;
import
com.dji.sdk.websocket.WebSocketMessageResponse
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
lombok.extern.slf4j.Slf4j
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.context.annotation.Lazy
;
import
org.springframework.stereotype.Service
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.StringUtils
;
import
org.springframework.util.StringUtils
;
import
org.springframework.web.socket.TextMessage
;
import
org.springframework.web.socket.TextMessage
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.lang.reflect.Method
;
import
java.util.Collection
;
import
java.util.Collection
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.Objects
;
import
java.util.Set
;
import
java.util.stream.Collectors
;
/**
/**
* @author sean.zhou
* @author sean.zhou
...
@@ -24,12 +34,27 @@ import java.util.Objects;
...
@@ -24,12 +34,27 @@ import java.util.Objects;
@Slf4j
@Slf4j
public
class
WebSocketMessageServiceImpl
implements
IWebSocketMessageService
{
public
class
WebSocketMessageServiceImpl
implements
IWebSocketMessageService
{
private
static
final
Set
<
String
>
DEVICE_SCOPED_BIZ_CODES
=
Set
.
of
(
BizCodeEnum
.
DEVICE_ONLINE
.
getCode
(),
BizCodeEnum
.
DEVICE_OFFLINE
.
getCode
(),
BizCodeEnum
.
DEVICE_UPDATE_TOPO
.
getCode
(),
BizCodeEnum
.
DEVICE_OSD
.
getCode
(),
BizCodeEnum
.
RC_OSD
.
getCode
(),
BizCodeEnum
.
DOCK_OSD
.
getCode
(),
BizCodeEnum
.
DEVICE_HMS
.
getCode
(),
BizCodeEnum
.
FLIGHT_AREAS_DRONE_LOCATION
.
getCode
()
);
@Autowired
@Autowired
private
ObjectMapper
mapper
;
private
ObjectMapper
mapper
;
@Autowired
@Autowired
private
IWebSocketManageService
webSocketManageService
;
private
IWebSocketManageService
webSocketManageService
;
@Autowired
@Lazy
private
IDeviceService
deviceService
;
@Override
@Override
public
void
sendMessage
(
MyConcurrentWebSocketSession
session
,
WebSocketMessageResponse
message
)
{
public
void
sendMessage
(
MyConcurrentWebSocketSession
session
,
WebSocketMessageResponse
message
)
{
if
(
session
==
null
)
{
if
(
session
==
null
)
{
...
@@ -89,6 +114,17 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
...
@@ -89,6 +114,17 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
webSocketManageService
.
getValueWithWorkspace
(
workspaceId
)
:
webSocketManageService
.
getValueWithWorkspace
(
workspaceId
)
:
webSocketManageService
.
getValueWithWorkspaceAndUserType
(
workspaceId
,
userType
);
webSocketManageService
.
getValueWithWorkspaceAndUserType
(
workspaceId
,
userType
);
String
deviceSn
=
extractDeviceSn
(
bizCode
,
data
);
if
(
DEVICE_SCOPED_BIZ_CODES
.
contains
(
bizCode
))
{
if
(!
StringUtils
.
hasText
(
deviceSn
))
{
log
.
warn
(
"Drop device-scoped WSS without sn, bizCode={}"
,
bizCode
);
return
;
}
sessions
=
sessions
.
stream
()
.
filter
(
session
->
canSessionSeeDevice
(
workspaceId
,
session
,
deviceSn
))
.
collect
(
Collectors
.
toList
());
}
this
.
sendBatch
(
sessions
,
new
WebSocketMessageResponse
()
this
.
sendBatch
(
sessions
,
new
WebSocketMessageResponse
()
.
setData
(
Objects
.
requireNonNullElse
(
data
,
""
))
.
setData
(
Objects
.
requireNonNullElse
(
data
,
""
))
.
setTimestamp
(
System
.
currentTimeMillis
())
.
setTimestamp
(
System
.
currentTimeMillis
())
...
@@ -99,4 +135,40 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
...
@@ -99,4 +135,40 @@ public class WebSocketMessageServiceImpl implements IWebSocketMessageService {
public
void
sendBatch
(
String
workspaceId
,
String
bizCode
,
Object
data
)
{
public
void
sendBatch
(
String
workspaceId
,
String
bizCode
,
Object
data
)
{
this
.
sendBatch
(
workspaceId
,
null
,
bizCode
,
data
);
this
.
sendBatch
(
workspaceId
,
null
,
bizCode
,
data
);
}
}
private
boolean
canSessionSeeDevice
(
String
workspaceId
,
MyConcurrentWebSocketSession
session
,
String
deviceSn
)
{
CustomClaim
claim
=
webSocketManageService
.
getClaim
(
session
.
getId
()).
orElse
(
null
);
if
(
claim
==
null
)
{
// 无 claim 时无法做用户级裁剪,保守不推送
return
false
;
}
return
deviceService
.
isDeviceVisibleToUser
(
workspaceId
,
deviceSn
,
claim
.
getId
(),
claim
.
getRoleType
(),
claim
.
getOrgId
());
}
private
String
extractDeviceSn
(
String
bizCode
,
Object
data
)
{
if
(!
StringUtils
.
hasText
(
bizCode
)
||
data
==
null
)
{
return
null
;
}
if
(!
DEVICE_SCOPED_BIZ_CODES
.
contains
(
bizCode
))
{
return
null
;
}
if
(
data
instanceof
TelemetryDTO
)
{
return
((
TelemetryDTO
<?>)
data
).
getSn
();
}
if
(
data
instanceof
TopologyDeviceDTO
)
{
return
((
TopologyDeviceDTO
)
data
).
getSn
();
}
if
(
data
instanceof
Map
)
{
Object
sn
=
((
Map
<?,
?>)
data
).
get
(
"sn"
);
return
sn
!=
null
?
String
.
valueOf
(
sn
)
:
null
;
}
try
{
Method
getter
=
data
.
getClass
().
getMethod
(
"getSn"
);
Object
sn
=
getter
.
invoke
(
data
);
return
sn
!=
null
?
String
.
valueOf
(
sn
)
:
null
;
}
catch
(
Exception
ignored
)
{
return
null
;
}
}
}
}
sample/src/main/java/com/dji/sample/manage/controller/UserController.java
View file @
e1262cc7
...
@@ -56,7 +56,7 @@ public class UserController {
...
@@ -56,7 +56,7 @@ public class UserController {
}
}
/**
/**
*
Modify user information. Only mqtt account information is included, nothing else can be modified.
*
修改用户信息:MQTT 账号、用户类型、角色(同步当前团队 manage_user_org)。
* @param user
* @param user
* @param workspaceId
* @param workspaceId
* @param userId
* @param userId
...
...
sample/src/main/java/com/dji/sample/manage/service/IDeviceService.java
View file @
e1262cc7
...
@@ -341,4 +341,9 @@ public interface IDeviceService extends IService<DeviceEntity> {
...
@@ -341,4 +341,9 @@ public interface IDeviceService extends IService<DeviceEntity> {
*/
*/
Optional
<
Object
>
getDeviceOsd
(
String
workspaceId
,
String
deviceSn
);
Optional
<
Object
>
getDeviceOsd
(
String
workspaceId
,
String
deviceSn
);
/**
* 判断指定用户是否可见某设备(用于 WSS 推送裁剪,不依赖 HTTP 请求上下文)。
*/
boolean
isDeviceVisibleToUser
(
String
workspaceId
,
String
deviceSn
,
String
userId
,
Integer
roleType
,
String
orgId
);
}
}
\ No newline at end of file
sample/src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java
View file @
e1262cc7
...
@@ -62,8 +62,10 @@ import java.time.ZoneId;
...
@@ -62,8 +62,10 @@ import java.time.ZoneId;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.HashSet
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Objects
;
import
java.util.Optional
;
import
java.util.Optional
;
import
java.util.Set
;
import
java.util.Set
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
import
static
com
.
dji
.
sample
.
common
.
constant
.
DeviceConstant
.
CUSTOM_DOCK_START
;
import
static
com
.
dji
.
sample
.
common
.
constant
.
DeviceConstant
.
CUSTOM_DOCK_START
;
...
@@ -82,6 +84,20 @@ import static com.dji.sample.common.util.SecurityUtils.*;
...
@@ -82,6 +84,20 @@ import static com.dji.sample.common.util.SecurityUtils.*;
@Transactional
@Transactional
public
class
DeviceServiceImpl
extends
ServiceImpl
<
IDeviceMapper
,
DeviceEntity
>
implements
IDeviceService
{
public
class
DeviceServiceImpl
extends
ServiceImpl
<
IDeviceMapper
,
DeviceEntity
>
implements
IDeviceService
{
private
static
final
long
VISIBILITY_CACHE_MS
=
30_000L
;
private
final
ConcurrentHashMap
<
String
,
VisibilityCacheEntry
>
visibilityCache
=
new
ConcurrentHashMap
<>();
private
static
final
class
VisibilityCacheEntry
{
private
final
Set
<
String
>
sns
;
private
final
long
expireAt
;
private
VisibilityCacheEntry
(
Set
<
String
>
sns
,
long
expireAt
)
{
this
.
sns
=
sns
;
this
.
expireAt
=
expireAt
;
}
}
@Autowired
@Autowired
private
MqttGatewayPublish
messageSender
;
private
MqttGatewayPublish
messageSender
;
...
@@ -404,9 +420,12 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
...
@@ -404,9 +420,12 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
@Override
@Override
public
void
pushDeviceOnlineTopo
(
String
workspaceId
,
String
gatewaySn
,
String
deviceSn
)
{
public
void
pushDeviceOnlineTopo
(
String
workspaceId
,
String
gatewaySn
,
String
deviceSn
)
{
TopologyDeviceDTO
topo
=
getDeviceTopoForPilot
(
deviceSn
)
.
orElseGet
(
TopologyDeviceDTO:
:
new
)
.
setSn
(
deviceSn
)
.
setGatewaySn
(
gatewaySn
);
webSocketMessageService
.
sendBatch
(
webSocketMessageService
.
sendBatch
(
workspaceId
,
null
,
com
.
dji
.
sdk
.
websocket
.
BizCodeEnum
.
DEVICE_ONLINE
.
getCode
(),
workspaceId
,
null
,
com
.
dji
.
sdk
.
websocket
.
BizCodeEnum
.
DEVICE_ONLINE
.
getCode
(),
topo
);
getDeviceTopoForPilot
(
deviceSn
).
orElseGet
(
TopologyDeviceDTO:
:
new
).
setGatewaySn
(
gatewaySn
));
}
}
@Override
@Override
...
@@ -1878,6 +1897,7 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
...
@@ -1878,6 +1897,7 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
delUserDevice
.
in
(
UserDeviceEntity:
:
getDeviceSn
,
toRemove
);
delUserDevice
.
in
(
UserDeviceEntity:
:
getDeviceSn
,
toRemove
);
userDeviceService
.
remove
(
delUserDevice
);
userDeviceService
.
remove
(
delUserDevice
);
}
}
invalidateVisibilityCache
();
}
}
@Override
@Override
...
@@ -1898,6 +1918,7 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
...
@@ -1898,6 +1918,7 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
delUserDevice
.
eq
(
UserDeviceEntity:
:
getOrgId
,
orgId
);
delUserDevice
.
eq
(
UserDeviceEntity:
:
getOrgId
,
orgId
);
delUserDevice
.
in
(
UserDeviceEntity:
:
getDeviceSn
,
deviceSnList
);
delUserDevice
.
in
(
UserDeviceEntity:
:
getDeviceSn
,
deviceSnList
);
userDeviceService
.
remove
(
delUserDevice
);
userDeviceService
.
remove
(
delUserDevice
);
invalidateVisibilityCache
();
}
}
@Override
@Override
...
@@ -1992,6 +2013,7 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
...
@@ -1992,6 +2013,7 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
del
.
in
(
UserDeviceEntity:
:
getDeviceSn
,
toRemove
);
del
.
in
(
UserDeviceEntity:
:
getDeviceSn
,
toRemove
);
userDeviceService
.
remove
(
del
);
userDeviceService
.
remove
(
del
);
}
}
invalidateVisibilityCache
();
}
}
/**
/**
...
@@ -2016,7 +2038,8 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
...
@@ -2016,7 +2038,8 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
}
}
}
}
List
<
String
>
deviceSnList
=
resolveVisibleDeviceSnsForCurrentUser
(
workspaceId
,
filterOrgId
);
List
<
String
>
deviceSnList
=
resolveVisibleDeviceSnsForUser
(
workspaceId
,
getUserId
(),
getRoleType
(),
filterOrgId
);
if
(
CollectionUtils
.
isEmpty
(
deviceSnList
))
{
if
(
CollectionUtils
.
isEmpty
(
deviceSnList
))
{
return
false
;
return
false
;
}
}
...
@@ -2024,20 +2047,66 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
...
@@ -2024,20 +2047,66 @@ public class DeviceServiceImpl extends ServiceImpl<IDeviceMapper, DeviceEntity>
return
true
;
return
true
;
}
}
@Override
public
boolean
isDeviceVisibleToUser
(
String
workspaceId
,
String
deviceSn
,
String
userId
,
Integer
roleType
,
String
orgId
)
{
if
(!
StringUtils
.
hasText
(
deviceSn
))
{
return
false
;
}
if
(!
StringUtils
.
hasText
(
workspaceId
)
||
!
StringUtils
.
hasText
(
userId
)
||
roleType
==
null
)
{
return
false
;
}
if
(
aboveSysAdminRole
(
roleType
))
{
return
true
;
}
return
getVisibleDeviceSnsCached
(
workspaceId
,
userId
,
roleType
,
orgId
).
contains
(
deviceSn
);
}
/** 分配变更后清可见性缓存,避免 30s 窗口越权。 */
public
void
invalidateVisibilityCache
()
{
visibilityCache
.
clear
();
}
private
Set
<
String
>
getVisibleDeviceSnsCached
(
String
workspaceId
,
String
userId
,
Integer
roleType
,
String
orgId
)
{
String
cacheKey
=
workspaceId
+
"|"
+
userId
+
"|"
+
roleType
+
"|"
+
Objects
.
toString
(
orgId
,
""
);
VisibilityCacheEntry
cached
=
visibilityCache
.
get
(
cacheKey
);
long
now
=
System
.
currentTimeMillis
();
if
(
cached
!=
null
&&
cached
.
expireAt
>
now
)
{
return
cached
.
sns
;
}
Set
<
String
>
sns
=
new
HashSet
<>(
resolveVisibleDeviceSnsForUser
(
workspaceId
,
userId
,
roleType
,
orgId
));
visibilityCache
.
put
(
cacheKey
,
new
VisibilityCacheEntry
(
sns
,
now
+
VISIBILITY_CACHE_MS
));
return
sns
;
}
/**
/**
* 管理员看团队全量;飞手/成员仅看分配给自己的设备。
* 管理员看团队全量;飞手/成员仅看分配给自己的设备。
*/
*/
private
List
<
String
>
resolveVisibleDeviceSnsForCurrentUser
(
String
workspaceId
,
String
orgId
)
{
private
List
<
String
>
resolveVisibleDeviceSnsForCurrentUser
(
String
workspaceId
,
String
orgId
)
{
return
resolveVisibleDeviceSnsForUser
(
workspaceId
,
getUserId
(),
getRoleType
(),
orgId
);
}
private
List
<
String
>
resolveVisibleDeviceSnsForUser
(
String
workspaceId
,
String
userId
,
Integer
roleType
,
String
orgId
)
{
if
(
aboveSysAdminRole
(
roleType
))
{
// 超管:不在这里收窄;HTTP 列表不走 org 过滤时直接全量
if
(!
StringUtils
.
hasText
(
orgId
))
{
return
new
ArrayList
<>();
}
return
listVisibleDeviceSns
(
workspaceId
,
orgId
);
}
if
(!
StringUtils
.
hasText
(
orgId
))
{
return
new
ArrayList
<>();
}
List
<
String
>
orgSns
=
listVisibleDeviceSns
(
workspaceId
,
orgId
);
List
<
String
>
orgSns
=
listVisibleDeviceSns
(
workspaceId
,
orgId
);
if
(
CollectionUtils
.
isEmpty
(
orgSns
))
{
if
(
CollectionUtils
.
isEmpty
(
orgSns
))
{
return
new
ArrayList
<>();
return
new
ArrayList
<>();
}
}
// 团队管理员及以上看本团队全部设备
if
(
aboveAdminRole
(
roleType
))
{
if
(
aboveAdminRole
())
{
return
orgSns
;
return
orgSns
;
}
}
// 飞手/成员:与用户分配取交集
List
<
String
>
userSns
=
listUserDeviceSns
(
workspaceId
,
orgId
,
userId
);
List
<
String
>
userSns
=
listUserDeviceSns
(
workspaceId
,
orgId
,
getUserId
());
if
(
CollectionUtils
.
isEmpty
(
userSns
))
{
if
(
CollectionUtils
.
isEmpty
(
userSns
))
{
return
new
ArrayList
<>();
return
new
ArrayList
<>();
}
}
...
...
sample/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java
View file @
e1262cc7
...
@@ -918,6 +918,9 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
...
@@ -918,6 +918,9 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
@Override
@Override
public
Boolean
updateUser
(
String
workspaceId
,
String
userId
,
UserListDTO
user
)
{
public
Boolean
updateUser
(
String
workspaceId
,
String
userId
,
UserListDTO
user
)
{
// 管理员以上才能改用户
aboveAdminRoleAndThrowError
();
UserEntity
userEntity
=
mapper
.
selectOne
(
UserEntity
userEntity
=
mapper
.
selectOne
(
new
LambdaQueryWrapper
<
UserEntity
>()
new
LambdaQueryWrapper
<
UserEntity
>()
.
eq
(
UserEntity:
:
getUserId
,
userId
)
.
eq
(
UserEntity:
:
getUserId
,
userId
)
...
@@ -925,16 +928,90 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
...
@@ -925,16 +928,90 @@ public class UserServiceImpl extends ServiceImpl<IUserMapper, UserEntity> implem
if
(
userEntity
==
null
)
{
if
(
userEntity
==
null
)
{
return
false
;
return
false
;
}
}
// 不能改自己角色导致锁死;MQTT 仍可改
boolean
editingSelf
=
userId
.
equals
(
getUserId
());
// 目标若是系统管理员,仅超管可改
if
(
userEntity
.
getRoleType
()
!=
null
&&
userEntity
.
getRoleType
()
==
RoleTypeEnum
.
SYS_ADMIN
.
getVal
())
{
aboveSysAdminRoleAndThrowError
();
}
// 不能把别人升成系统管理员(除非自己是超管)
if
(
user
.
getRoleType
()
!=
null
&&
user
.
getRoleType
()
==
RoleTypeEnum
.
SYS_ADMIN
.
getVal
())
{
aboveSysAdminRoleAndThrowError
();
}
if
(
user
.
getMqttUsername
()
!=
null
)
{
userEntity
.
setMqttUsername
(
user
.
getMqttUsername
());
userEntity
.
setMqttUsername
(
user
.
getMqttUsername
());
}
if
(
user
.
getMqttPassword
()
!=
null
)
{
userEntity
.
setMqttPassword
(
user
.
getMqttPassword
());
userEntity
.
setMqttPassword
(
user
.
getMqttPassword
());
}
Integer
newUserType
=
parseUserType
(
user
.
getUserType
());
if
(
newUserType
!=
null
)
{
userEntity
.
setUserType
(
newUserType
);
}
Integer
newRoleType
=
user
.
getRoleType
();
if
(
newRoleType
!=
null
&&
!
editingSelf
)
{
// 用户表角色:非超管账号同步;超管账号保持 SYS_ADMIN
if
(
userEntity
.
getRoleType
()
==
null
||
userEntity
.
getRoleType
()
!=
RoleTypeEnum
.
SYS_ADMIN
.
getVal
())
{
userEntity
.
setRoleType
(
newRoleType
);
}
}
userEntity
.
setUpdateTime
(
System
.
currentTimeMillis
());
userEntity
.
setUpdateTime
(
System
.
currentTimeMillis
());
int
id
=
mapper
.
update
(
userEntity
,
new
LambdaUpdateWrapper
<
UserEntity
>()
int
id
=
mapper
.
update
(
userEntity
,
new
LambdaUpdateWrapper
<
UserEntity
>()
.
eq
(
UserEntity:
:
getUserId
,
userId
)
.
eq
(
UserEntity:
:
getUserId
,
userId
)
.
eq
(
UserEntity:
:
getWorkspaceId
,
workspaceId
));
.
eq
(
UserEntity:
:
getWorkspaceId
,
workspaceId
));
// 列表展示的是「当前团队」下 manage_user_org.role_type,必须同步
if
(
newRoleType
!=
null
&&
!
editingSelf
)
{
String
currentOrgId
=
getOrgId
();
if
(
StringUtils
.
hasText
(
currentOrgId
))
{
LambdaQueryWrapper
<
UserOrgEntity
>
orgWrapper
=
new
LambdaQueryWrapper
<>();
orgWrapper
.
eq
(
UserOrgEntity:
:
getUserId
,
userId
)
.
eq
(
UserOrgEntity:
:
getOrgId
,
currentOrgId
)
.
eq
(
UserOrgEntity:
:
getWorkspaceId
,
workspaceId
);
UserOrgEntity
relation
=
userOrgService
.
getOne
(
orgWrapper
,
false
);
if
(
relation
!=
null
)
{
relation
.
setRoleType
(
newRoleType
);
relation
.
setUpdateTime
(
System
.
currentTimeMillis
());
relation
.
setUpdaterId
(
getUserId
());
relation
.
setUpdaterName
(
getUsername
());
userOrgService
.
updateById
(
relation
);
}
else
{
// 无关系时写入当前团队
ensureUserInOrg
(
userId
,
workspaceId
,
currentOrgId
,
newRoleType
);
}
}
}
return
id
>
0
;
return
id
>
0
;
}
}
private
Integer
parseUserType
(
String
userType
)
{
if
(!
StringUtils
.
hasText
(
userType
))
{
return
null
;
}
try
{
return
Integer
.
valueOf
(
userType
.
trim
());
}
catch
(
NumberFormatException
e
)
{
// 兼容前端偶发传描述文案
if
(
"Web"
.
equalsIgnoreCase
(
userType
)
||
"WEB"
.
equalsIgnoreCase
(
userType
))
{
return
UserTypeEnum
.
WEB
.
getVal
();
}
if
(
"Pilot"
.
equalsIgnoreCase
(
userType
)
||
"PILOT"
.
equalsIgnoreCase
(
userType
))
{
return
UserTypeEnum
.
PILOT
.
getVal
();
}
return
null
;
}
}
@Override
@Override
public
Boolean
deleteUser
(
String
workspaceId
,
String
userId
)
{
public
Boolean
deleteUser
(
String
workspaceId
,
String
userId
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment