增加代理商,活动,代理商员工关,代理商活动参与者相关功能

This commit is contained in:
Wang 2025-12-31 17:34:52 +08:00
parent 14a17fb4e8
commit cd0aace6dc
24 changed files with 291 additions and 41 deletions

View File

@ -75,8 +75,7 @@
<resource>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>**.xml</include>
<include>**.yml</include>
<include>**</include>
</includes>
</resource>
</resources>

View File

@ -0,0 +1,49 @@
package com.seer.teach.filter.cors;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class CorsConfig {
@Bean
@ConfigurationProperties(prefix = "zs.cors")
public Cors cors() {
return new Cors();
}
@Bean
public CorsWebFilter corsWebFilter(Cors cors) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOriginPatterns(cors.getOrigins());
// 允许的HTTP方法
config.addAllowedMethod("*");
// 允许的请求头
config.addAllowedHeader("*");
config.setAllowCredentials(true);
// 暴露的响应头
config.addExposedHeader("token");
config.addExposedHeader("Content-Disposition");
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
@Getter
@Setter
public static class Cors {
private List<String> origins = new ArrayList<>();
}
}

View File

@ -1,8 +1,10 @@
package com.seer.teach.filter.cors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
@ -13,7 +15,7 @@ import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class CorsFilter implements WebFilter {
@ -23,17 +25,21 @@ public class CorsFilter implements WebFilter {
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
if (!CorsUtils.isCorsRequest(request)) {
log.info("Not a CORS request");
return chain.filter(exchange);
}
RequestPath path = exchange.getRequest().getPath();
log.info("request path: {},method : {}", path, request.getMethod());
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, CorsConfiguration.ALL);
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, CorsConfiguration.ALL);
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, CorsConfiguration.ALL);
headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, CorsConfiguration.ALL);
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
if (request.getMethod() == HttpMethod.OPTIONS) {
response.setStatusCode(HttpStatus.OK);
log.info("CORS preflight requestOK");
return Mono.empty();
}
return chain.filter(exchange);

View File

@ -0,0 +1,50 @@
package com.seer.teach.filter.cors;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 解决 Spring Cloud Gateway 2.x 跨域时出现重复 Origin BUG
*
* 参考文档<a href="https://blog.csdn.net/zimou5581/article/details/90043178" />
*
*/
@Component
public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.defer(() -> {
List<String> keysToModify = exchange.getResponse().getHeaders().entrySet().stream()
.filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
.filter(kv -> (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
|| kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
keysToModify.forEach(key->{
List<String> values = exchange.getResponse().getHeaders().get(key);
if (values != null && !values.isEmpty()) {
exchange.getResponse().getHeaders().put(key, Collections.singletonList(values.get(0)));
}
});
return chain.filter(exchange);
}));
}
}

View File

@ -82,3 +82,21 @@ logging:
org.springframework.cloud.gateway: DEBUG # 查看 Gateway 过滤器链
org.springframework.security: DEBUG # 检查 Spring Security 拦截
reactor.netty.http.client: DEBUG # 查看网络层交互
zs:
decryption:
enabled: true
encryption:
enabled: true
cors:
origins:
- http://localhost:*
- http://192.168.*:*
- http://10.10.*:*
- http://10.10.4.143:8020
- http://10.10.3.134:8020
- https://admin-api.seerteach.net:*
- http://admin-api.seerteach.net:*
- https://admin.seerteach.net:*
- https://admin.seerteach.net

View File

@ -76,7 +76,7 @@ public class AdminMpAccountController {
@PostMapping("/generate-qr-code")
@Operation(summary = "生成公众号二维码")
@SaCheckPermission("admin:mp:account:qr-code'")
public ResultBean<Boolean> generateAccountQrCode(@RequestParam("id") Integer id) {
return ResultBean.success(mpAccountService.generateAccountQrCode(id));
public ResultBean<Boolean> generateAccountQrCode(@RequestParam("id") Integer id,@RequestParam("场景值") String sceneStr) {
return ResultBean.success(mpAccountService.generateAccountQrCode(id,sceneStr));
}
}

View File

@ -141,12 +141,12 @@ public class AdminMpAccountService {
* @param id 公众号账号ID
* @return 是否生成成功
*/
public Boolean generateAccountQrCode(Integer id) {
public Boolean generateAccountQrCode(Integer id,String sceneStr) {
MpAccountEntity account = mpAccountService.getById(id);
WxMpService mpService = mpServiceFactory.getRequiredMpService(account.getAppId());
String qrCodeUrl;
try {
WxMpQrCodeTicket qrCodeTicket = mpService.getQrcodeService().qrCodeCreateLastTicket("default");
WxMpQrCodeTicket qrCodeTicket = mpService.getQrcodeService().qrCodeCreateLastTicket(sceneStr);
qrCodeUrl = mpService.getQrcodeService().qrCodePictureUrl(qrCodeTicket.getTicket());
} catch (WxErrorException e) {
log.error("生成公众号二维码失败", e);

View File

@ -7,7 +7,12 @@ import lombok.Data;
@Data
public class MpGenerateQrCodeReq {
private String userId;
@Schema(description = "场景值")
private String sceneStr;
@Schema(description = "小程序appid")
private String appId;
@Schema(description = "二维码类型 1-临时 2-永久")
private Integer type;
}

View File

@ -8,6 +8,7 @@ import com.seer.teach.mp.factory.MpServiceFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpQrcodeService;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import org.springframework.beans.factory.annotation.Value;
@ -30,8 +31,14 @@ public class AppOfficialQrCodeService {
WxMpService wxMpService = mpServiceFactory.getRequiredMpService(req.getAppId());
Integer expireSeconds = mpQrCodeExpireTime * 60 *60;
WxMpQrCodeTicket ticket = wxMpService.getQrcodeService().qrCodeCreateTmpTicket(req.getUserId(), expireSeconds);;
WxMpQrcodeService qrcodeService = wxMpService.getQrcodeService();
WxMpQrCodeTicket ticket;
if(req.getType() == 1) {
ticket = qrcodeService.qrCodeCreateTmpTicket(req.getSceneStr(), expireSeconds);
}else{
ticket = qrcodeService.qrCodeCreateLastTicket(req.getSceneStr());
}
String qrCodeUrl = wxMpService.getQrcodeService().qrCodePictureUrl(ticket.getTicket());
MpQrCodeResp resp = new MpQrCodeResp();

View File

@ -49,3 +49,7 @@ zs:
enabled: false
encryption:
enabled: false
cors:
origins:
- http://10.10.3.134:8020
- http://10.10.4.143:8020

View File

@ -22,7 +22,12 @@ public enum LoginType {
/**
* 仓库登录
*/
WAREHOUSE_ACCOUNT("warehouse_account", "仓库登录");
WAREHOUSE_ACCOUNT("warehouse_account", "仓库登录"),
/**
* 微信公众号账号密码登录
*/
WECHAT_OAUTH_WITH_ACCOUNT("wechat_oauth_with_account", "微信公众号账号密码登录");
private String code;

View File

@ -10,7 +10,7 @@ import com.seer.teach.common.annotation.EncryptionAnnotation;
import com.seer.teach.common.annotation.LogOperation;
import com.seer.teach.common.annotation.LogPrint;
import com.seer.teach.common.enums.ModuleEnum;
import com.seer.teach.user.app.auth.request.WeChatLoginParam;
import com.seer.teach.user.app.auth.request.LoginParam;
import com.seer.teach.user.app.auth.response.LoginResp;
import com.seer.teach.user.app.auth.service.AppAuthService;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -37,7 +37,7 @@ public class AppAuthController {
private AppAuthService userAuthService;
@PostMapping("/login")
public ResultBean<LoginResp> login(@RequestBody WeChatLoginParam request) {
public ResultBean<LoginResp> login(@RequestBody LoginParam request) {
return ResultBean.success(userAuthService.authenticate(request));
}

View File

@ -2,24 +2,24 @@ package com.seer.teach.user.app.auth.request;
import com.seer.teach.user.app.auth.LoginType;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @Author: Captain
* @Description: 微信小程序登录请求参数
* @Date: 2025-05-19 17:46
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Builder
@Schema(description = "微信登录请求实体类")
public class WeChatLoginParam {
@Schema(description = "登录请求实体类")
public class LoginParam {
@Schema(description = "登录方式不可为空")
@NotBlank(message = "loginType不可为空")
LoginType loginType;
@Schema(description = "jsCode不可为空")

View File

@ -5,7 +5,7 @@ import cn.hutool.core.collection.CollectionUtil;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.user.app.auth.LoginType;
import com.seer.teach.user.app.auth.request.WeChatLoginParam;
import com.seer.teach.user.app.auth.request.LoginParam;
import com.seer.teach.user.app.auth.response.LoginResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@ -43,7 +43,7 @@ public class AppAuthService {
* @return
*/
@Transactional(rollbackFor = Exception.class,timeout = 120)
public LoginResp authenticate(WeChatLoginParam request) {
public LoginResp authenticate(LoginParam request) {
LoginStrategy strategy = strategyMap.get(request.getLoginType());
AssertUtils.notNull(strategy, ResultCodeEnum.LOGIN_TYPE_ERROR);

View File

@ -1,7 +1,7 @@
package com.seer.teach.user.app.auth.service;
import com.seer.teach.user.app.auth.LoginType;
import com.seer.teach.user.app.auth.request.WeChatLoginParam;
import com.seer.teach.user.app.auth.request.LoginParam;
public interface LoginStrategy {
@ -13,5 +13,5 @@ public interface LoginStrategy {
* @param request
* @return
*/
LoginUser authenticate(WeChatLoginParam request);
LoginUser authenticate(LoginParam request);
}

View File

@ -2,6 +2,8 @@ package com.seer.teach.user.app.auth.service;
import lombok.Data;
import java.util.List;
@Data
public class LoginUser {
@ -17,6 +19,8 @@ public class LoginUser {
*/
private Integer applyStatus;
private List<String> roles;
public LoginUser(Integer userId) {
this.userId = userId;
}

View File

@ -11,7 +11,7 @@ import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.mp.api.WechatMiniProgramApi;
import com.seer.teach.mp.api.resp.WxMiniProgramSessionDTO;
import com.seer.teach.user.app.auth.LoginType;
import com.seer.teach.user.app.auth.request.WeChatLoginParam;
import com.seer.teach.user.app.auth.request.LoginParam;
import com.seer.teach.user.app.auth.service.LoginStrategy;
import com.seer.teach.user.app.auth.service.LoginUser;
import com.seer.teach.user.entity.UserAuthEntity;
@ -69,7 +69,7 @@ public class MiniProgramLoginStrategy extends AbstractLoginStrategy implements L
}
@Override
public LoginUser authenticate(WeChatLoginParam request) {
public LoginUser authenticate(LoginParam request) {
WxMiniProgramSessionDTO wxMiniProgramSessionDTO = wechatMiniProgramApi.code2Session(request.getJsCode());
AssertUtils.isTrue(wxMiniProgramSessionDTO.isSuccess(), ResultCodeEnum.WX_OAUTH_USED_ERROR);
UserAuthEntity userAuth = userAuthService.getOneByOpenIdAndUnionId(wxMiniProgramSessionDTO.getOpenid(), wxMiniProgramSessionDTO.getUnionid());
@ -156,7 +156,7 @@ public class MiniProgramLoginStrategy extends AbstractLoginStrategy implements L
* @param request 登录参数
* @return boolean true 是扫码登录false 密码登录
*/
private boolean isScanQrCodeLogin(WeChatLoginParam request) {
private boolean isScanQrCodeLogin(LoginParam request) {
if (!StringUtils.hasText(request.getScene())) {
return false;
}

View File

@ -5,7 +5,7 @@ import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.mp.api.WechatOfficialAccountApi;
import com.seer.teach.mp.api.resp.WxOAuth2AccessTokenDTO;
import com.seer.teach.user.app.auth.LoginType;
import com.seer.teach.user.app.auth.request.WeChatLoginParam;
import com.seer.teach.user.app.auth.request.LoginParam;
import com.seer.teach.user.app.auth.service.LoginStrategy;
import com.seer.teach.user.app.auth.service.LoginUser;
import com.seer.teach.user.entity.UserAuthEntity;
@ -28,7 +28,6 @@ public class OfficialAccountLoginStrategy extends AbstractLoginStrategy implemen
@Autowired
private WechatOfficialAccountApi wechatOfficialAccountApi;
@Autowired
private IUserService userService;
@ -41,7 +40,7 @@ public class OfficialAccountLoginStrategy extends AbstractLoginStrategy implemen
}
@Override
public LoginUser authenticate(WeChatLoginParam request) {
public LoginUser authenticate(LoginParam request) {
log.info("微信公众号登录:{}",request);
WxOAuth2AccessTokenDTO wxOAuth2AccessToken = wechatOfficialAccountApi.getUserWxOAuth2AccessToken(request.getAppId(), request.getJsCode());
log.debug("获取用户信息:{}",wxOAuth2AccessToken);
@ -61,7 +60,8 @@ public class OfficialAccountLoginStrategy extends AbstractLoginStrategy implemen
}else{
userAuth.setAppId(request.getAppId());
updateUserAuth(userAuth);
}
return new LoginUser(userAuth.getUserId());
LoginUser loginUser = new LoginUser(userAuth.getUserId());
return loginUser;
}
}
}

View File

@ -0,0 +1,75 @@
package com.seer.teach.user.app.auth.service.strategy;
import cn.dev33.satoken.stp.StpUtil;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.exception.CommonException;
import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.common.utils.CommonUtils;
import com.seer.teach.mp.api.WechatOfficialAccountApi;
import com.seer.teach.mp.api.resp.WxOAuth2AccessTokenDTO;
import com.seer.teach.user.app.auth.LoginType;
import com.seer.teach.user.app.auth.request.LoginParam;
import com.seer.teach.user.app.auth.service.LoginStrategy;
import com.seer.teach.user.app.auth.service.LoginUser;
import com.seer.teach.user.entity.UserAuthEntity;
import com.seer.teach.user.entity.UserEntity;
import com.seer.teach.user.service.IUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* 账号密码登录策略
* 实现传统的用户名/手机号和密码登录并生成token
*/
@RequiredArgsConstructor
@Service
@Slf4j
public class OfficialOauthWithAccountLoginStrategy extends AbstractLoginStrategy implements LoginStrategy {
private final IUserService userService;
@Autowired
private WechatOfficialAccountApi wechatOfficialAccountApi;
@Override
public LoginType getType() {
return LoginType.WECHAT_OAUTH_WITH_ACCOUNT;
}
@Override
public LoginUser authenticate(LoginParam request) {
UserEntity accountUser = null;
if (request.getUserName() != null && !request.getUserName().isEmpty()) {
accountUser = userService.getOneByUserName(request.getUserName());
} else if (request.getPhoneNumber() != null && !request.getPhoneNumber().isEmpty()) {
accountUser = userService.getOneByMobile(request.getPhoneNumber());
}
AssertUtils.notNull(accountUser, ResultCodeEnum.USERNAME_OR_PASSWORD_IS_ERROR);
String password = CommonUtils.encryptPassword(request.getPassword());
if (password.equals(accountUser.getPassword())) {
WxOAuth2AccessTokenDTO wxOAuth2AccessToken = wechatOfficialAccountApi.getUserWxOAuth2AccessToken(request.getAppId(), request.getJsCode());
log.debug("获取用户信息:{}",wxOAuth2AccessToken);
AssertUtils.notNull(wxOAuth2AccessToken, ResultCodeEnum.WX_OAUTH_USED_ERROR);
String userOpenId = wxOAuth2AccessToken.getOpenId();
UserAuthEntity userAuth = userAuthService.getOneByOpenId(userOpenId);
StpUtil.login(accountUser.getId());
if(Objects.isNull(userAuth)){
boolean userAuthSaved = saveUserAuth(getType().getCode(), accountUser.getId(), wxOAuth2AccessToken, request.getAppId());
log.info("保存用户授权信息:{}", userAuthSaved);
}else{
userAuth.setAppId(request.getAppId());
updateUserAuth(userAuth);
}
log.info("用户[{}]账号密码登录成功", accountUser.getId());
return new LoginUser(accountUser.getId());
}else{
log.info("用户[{}]账号密码登录失败", accountUser.getId());
throw new CommonException(ResultCodeEnum.USERNAME_OR_PASSWORD_IS_ERROR);
}
}
}

View File

@ -8,7 +8,7 @@ import com.seer.teach.common.utils.CommonUtils;
import com.seer.teach.mp.api.WechatOfficialAccountApi;
import com.seer.teach.mp.api.resp.WxOAuth2AccessTokenDTO;
import com.seer.teach.user.app.auth.LoginType;
import com.seer.teach.user.app.auth.request.WeChatLoginParam;
import com.seer.teach.user.app.auth.request.LoginParam;
import com.seer.teach.user.app.auth.service.LoginStrategy;
import com.seer.teach.user.app.auth.service.LoginUser;
import com.seer.teach.user.entity.RoleEntity;
@ -52,7 +52,7 @@ public class WarehouseAccountLoginStrategy extends AbstractLoginStrategy impleme
}
@Override
public LoginUser authenticate(WeChatLoginParam request) {
public LoginUser authenticate(LoginParam request) {
AssertUtils.notNull(request.getPassword(), ResultCodeEnum.USERNAME_OR_PASSWORD_IS_ERROR);
UserEntity accountUser = userService.getOneByUserName(request.getUserName());
AssertUtils.notNull(accountUser, ResultCodeEnum.USERNAME_OR_PASSWORD_IS_ERROR);

View File

@ -9,7 +9,7 @@ import com.seer.teach.iot.api.UserDeviceServiceApi;
import com.seer.teach.mp.api.WechatMiniProgramApi;
import com.seer.teach.mp.api.resp.WxMiniProgramSessionDTO;
import com.seer.teach.user.app.auth.LoginType;
import com.seer.teach.user.app.auth.request.WeChatLoginParam;
import com.seer.teach.user.app.auth.request.LoginParam;
import com.seer.teach.user.app.auth.service.LoginStrategy;
import com.seer.teach.user.app.auth.service.LoginUser;
import com.seer.teach.user.entity.UserAuthEntity;
@ -49,7 +49,7 @@ public class WechatChildrenAccountLoginStrategy extends AbstractLoginStrategy im
}
@Override
public LoginUser authenticate(WeChatLoginParam request) {
public LoginUser authenticate(LoginParam request) {
UserEntity accountUser =userService.getOneByMobile(request.getPhoneNumber());
AssertUtils.notNull(accountUser, ResultCodeEnum.USERNAME_OR_PASSWORD_IS_ERROR);
String password = CommonUtils.encryptPassword(request.getPassword());

View File

@ -55,4 +55,6 @@ public class AppUserInfoResp {
@Schema(description = "是否是主监护人")
private boolean isPrimaryGuardian;
}

View File

@ -18,8 +18,21 @@ import java.util.Set;
*/
public interface IUserRoleService extends IService<UserRoleEntity> {
/**
* 根据用户ID获取用户角色列表
*
* @param userId 用户ID
* @return 用户角色列表
*/
List<UserRoleEntity> getListByUserId(Integer userId);
/**
* 获取用户角色编码列表
*
* @param userId 用户编号
* @return 角色编码列表
*/
List<String> getRoleCodeListByUserId(Integer userId);
/**
* 为用户分配角色

View File

@ -44,6 +44,19 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRoleEnt
return list(new LambdaQueryWrapper<>(UserRoleEntity.class).eq(UserRoleEntity::getUserId, userId));
}
@Override
public List<String> getRoleCodeListByUserId(Integer userId) {
List<UserRoleEntity> userRoleList = this.getListByUserId(userId);
if(CollectionUtil.isNotEmpty(userRoleList)){
List<Integer> roleIds = userRoleList.stream().map(UserRoleEntity::getRoleId).collect(Collectors.toList());
if(CollectionUtil.isNotEmpty(roleIds)){
Collection<RoleEntity> roles = roleService.getRoleListByRoleIds(roleIds);
return roles.stream().map(RoleEntity::getRoleCode).collect(Collectors.toList());
}
}
return List.of();
}
@Override
public void assignUserRole(Integer userId, Set<Integer> roleIds) {
List<UserRoleEntity> userRoleList = this.getListByUserId(userId);