diff --git a/cc-admin-master/pom.xml b/cc-admin-master/pom.xml index 4fab5de..1f96819 100644 --- a/cc-admin-master/pom.xml +++ b/cc-admin-master/pom.xml @@ -15,10 +15,11 @@ yudao-module-system yudao-module-infra - yudao-module-bpm - yudao-module-report - yudao-module-hand + yudao-module-bpm + yudao-module-report + yudao-module-hand yudao-module-hand-mqtt + yudao-module-interphone ${project.artifactId} diff --git a/cc-admin-master/yudao-module-interphone/pom.xml b/cc-admin-master/yudao-module-interphone/pom.xml new file mode 100644 index 0000000..91698c1 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + cn.iocoder.boot + cc-admin-master + ${revision} + + + yudao-module-interphone + jar + + + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-tenant + + + + + cn.iocoder.boot + yudao-spring-boot-starter-security + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + com.baomidou + mybatis-plus-generator + + + cn.iocoder.boot + yudao-spring-boot-starter-redis + + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + + + cn.iocoder.boot + yudao-spring-boot-starter-test + test + + + + + \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/Author.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/Author.java new file mode 100644 index 0000000..53bef53 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/Author.java @@ -0,0 +1,10 @@ +package cn.iocoder.yudao.module.interphone.auth; + +import lombok.Data; + +@Data +public class Author { + private String accessToken; + private String refreshToken; + private String openId; +} diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/DefaultLoginService.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/DefaultLoginService.java new file mode 100644 index 0000000..ffcaa18 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/DefaultLoginService.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.interphone.auth; + +import cn.iocoder.yudao.module.interphone.config.HttpClientProperties; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.*; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Objects; + +@Service +public class DefaultLoginService implements LoginService { + + private final OkHttpClient okHttpClient; + private final HttpClientProperties properties; + + public DefaultLoginService(OkHttpClient okHttpClient, HttpClientProperties properties) { + this.okHttpClient = okHttpClient; + this.properties = properties; + } + + @Override + public Author loginAndGetToken() { + HttpUrl url = HttpUrl.parse(properties.getApiUrl() + "auth/skipImgVerify") + .newBuilder() + .addQueryParameter("username", properties.getUsername()) + .addQueryParameter("password", properties.getPassword()) + .addQueryParameter("user_type", properties.getUserType()) + .addQueryParameter("appId", properties.getAppId()) + .addQueryParameter("appSecret", properties.getAppSecret()) + .build(); + + Request request = new Request.Builder() + .url(url) + .get() + .build(); + + try (Response response = okHttpClient.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new RuntimeException("登录失败,http code=" + response.code()); + } + + String body = Objects.requireNonNull(response.body()).string(); + + // 这里简化处理,实际项目建议用 Jackson 解析 JSON + // 假设响应:{"code":0,"token":"abc123"} + String token = extractToken(body); + if (token == null || token.isBlank()) { + throw new RuntimeException("登录成功但未获取到 token"); + } + String openId = extraOpenId(body); + if (openId == null || openId.isBlank()) { + throw new RuntimeException("登录成功但未获取到 openId"); + } + Author author = new Author(); + author.setAccessToken(token); + author.setOpenId(openId); + return author; + } catch (IOException e) { + throw new RuntimeException("登录请求异常", e); + } + } + + private String extractToken(String body) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode rootNode = mapper.readTree(body); + JsonNode dataNode = rootNode.get("data"); + if (dataNode != null) { + JsonNode accessTokenNode = dataNode.get("access_token"); + if (accessTokenNode != null) { + return accessTokenNode.asText(); + } + } + return null; + } catch (IOException e) { + throw new RuntimeException("解析 token 失败", e); + } + } + private String extraOpenId(String body) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode rootNode = mapper.readTree(body); + JsonNode dataNode = rootNode.get("data"); + if (dataNode != null) { + JsonNode openIdNode = dataNode.get("openid"); + if (openIdNode != null) { + return openIdNode.asText(); + } + } + return null; + } catch (IOException e) { + throw new RuntimeException("解析 openId 失败", e); + } + } +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/DefaultTokenManager.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/DefaultTokenManager.java new file mode 100644 index 0000000..b80f083 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/DefaultTokenManager.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.interphone.auth; + +import org.springframework.stereotype.Component; + +import java.util.concurrent.locks.ReentrantLock; + +@Component +public class DefaultTokenManager implements TokenManager { + + private final LoginService loginService; + private volatile Author author; + private final ReentrantLock refreshLock = new ReentrantLock(); + + public DefaultTokenManager(LoginService loginService) { + this.loginService = loginService; + } + + @Override + public Author getAuthor() { + if (author == null || author.getAccessToken().isBlank()) { + refreshToken(); + } + return author; + } + + @Override + public void refreshToken() { + refreshLock.lock(); + try { + if (author != null && !author.getRefreshToken().isBlank()) { + return; + } + this.author = loginService.loginAndGetToken(); + } finally { + refreshLock.unlock(); + } + } + + @Override + public void clearAuthor() { + refreshLock.lock(); + try { + this.author = null; + } finally { + refreshLock.unlock(); + } + } + + public void forceRefreshToken() { + refreshLock.lock(); + try { + this.author = loginService.loginAndGetToken(); + } finally { + refreshLock.unlock(); + } + } +} diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/LoginService.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/LoginService.java new file mode 100644 index 0000000..bd479cd --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/LoginService.java @@ -0,0 +1,6 @@ +package cn.iocoder.yudao.module.interphone.auth; + +public interface LoginService { + + Author loginAndGetToken(); +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/TokenManager.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/TokenManager.java new file mode 100644 index 0000000..9047d40 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/auth/TokenManager.java @@ -0,0 +1,11 @@ +package cn.iocoder.yudao.module.interphone.auth; + +public interface TokenManager { + + Author getAuthor(); + + void refreshToken(); + + void clearAuthor(); + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/config/HttpClientProperties.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/config/HttpClientProperties.java new file mode 100644 index 0000000..7e0e202 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/config/HttpClientProperties.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.interphone.config; + +import lombok.Data; +import okhttp3.internal.Internal; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +/** + * 对讲机服务配置属性 + */ +@Data +@Validated +@ConfigurationProperties(prefix = "interphone") +public class HttpClientProperties { + + /** + * 对讲机服务用户名 + */ + private String username; + + /** + * 对讲机服务密码 + */ + private String password; + + /** + * 对讲机服务API地址 + */ + private String apiUrl; + + private String userType = "0"; + + private String appId = "97796fef376d41e0a7dda02720d0e3c9"; + + private String appSecret = "m597gv5h4hmdusce"; + + /** + * HTTP 客户端配置 + */ + private AppHttp http = new AppHttp(); + + @Data + public static class AppHttp { + /** + * 连接超时时间(秒) + */ + private long connectTimeoutSeconds = 5; + + /** + * 读取超时时间(秒) + */ + private long readTimeoutSeconds = 10; + + /** + * 写入超时时间(秒) + */ + private long writeTimeoutSeconds = 10; + + /** + * 调用超时时间(秒) + */ + private long callTimeoutSeconds = 15; + + /** + * 最大空闲连接数 + */ + private int maxIdleConnections = 50; + + /** + * 连接保持时间(分钟) + */ + private long keepAliveDurationMinutes = 5; + + /** + * 重试配置 + */ + private Retry retry = new Retry(); + + @Data + public static class Retry { + /** + * 最大重试次数 + */ + private int maxRetries = 2; + } + } + +} diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/config/OkHttpClientConfig.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/config/OkHttpClientConfig.java new file mode 100644 index 0000000..4ff78fb --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/config/OkHttpClientConfig.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.interphone.config; + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.TimeUnit; + +@Configuration +@EnableConfigurationProperties(HttpClientProperties.class) +public class OkHttpClientConfig { + + @Bean + public ConnectionPool connectionPool(HttpClientProperties properties) { + return new ConnectionPool( + properties.getHttp().getMaxIdleConnections(), + properties.getHttp().getKeepAliveDurationMinutes(), + TimeUnit.MINUTES + ); + } + + @Bean + public OkHttpClient okHttpClient(HttpClientProperties properties, + ConnectionPool connectionPool) { + return new OkHttpClient.Builder() + .connectTimeout(properties.getHttp().getConnectTimeoutSeconds(), TimeUnit.SECONDS) + .readTimeout(properties.getHttp().getReadTimeoutSeconds(), TimeUnit.SECONDS) + .writeTimeout(properties.getHttp().getWriteTimeoutSeconds(), TimeUnit.SECONDS) + .callTimeout(properties.getHttp().getCallTimeoutSeconds(), TimeUnit.SECONDS) + .connectionPool(connectionPool) + .retryOnConnectionFailure(false) + .build(); + } +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/controller/admin/InterphoneOpenController.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/controller/admin/InterphoneOpenController.java new file mode 100644 index 0000000..44a65d0 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/controller/admin/InterphoneOpenController.java @@ -0,0 +1,245 @@ +package cn.iocoder.yudao.module.interphone.controller.admin; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.interphone.service.InterphoneApiService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "开放接口 - 对讲平台代理") +@RestController +@RequestMapping("/interphone/open-api") +@Validated +public class InterphoneOpenController { + + @Resource + private InterphoneApiService interphoneApiService; + + @GetMapping("/profile/agent") + @Operation(summary = "获取代理商个人信息") + @PermitAll + public CommonResult getAgentProfile() { + return success(interphoneApiService.getAgentProfile()); + } + + @GetMapping("/agent/list") + @Operation(summary = "查询代理商列表") + @Parameters({ + @Parameter(name = "pageNo", description = "页码", required = true), + @Parameter(name = "pageSize", description = "每页数量", required = true), + @Parameter(name = "name", description = "代理商名称") + }) + @PermitAll + public CommonResult getAgentList(@RequestParam("pageNo") Integer pageNo, + @RequestParam("pageSize") Integer pageSize, + @RequestParam(value = "name", required = false) String name) { + return success(interphoneApiService.getAgentList(pageNo, pageSize, name)); + } + + @GetMapping("/agent/detail") + @Operation(summary = "查询代理商详情") + @Parameter(name = "id", description = "代理商 ID", required = true) + @PermitAll + public CommonResult getAgentDetail(@RequestParam("id") String id) { + return success(interphoneApiService.getAgentDetail(id)); + } + + @GetMapping("/profile/faststats") + @Operation(summary = "查询代理商单位群组用户统计") + @PermitAll + public CommonResult getFastStats() { + return success(interphoneApiService.getFastStats()); + } + + @GetMapping("/group/getGroupName") + @Operation(summary = "查询群组名称") + @Parameter(name = "orgId", description = "单位 ID", required = true) + @PermitAll + public CommonResult getGroupName(@RequestParam("orgId") String orgId) { + return success(interphoneApiService.getGroupName(orgId)); + } + + @GetMapping("/terminal/querySubordinateUser") + @Operation(summary = "查询下级用户") + @Parameters({ + @Parameter(name = "pageNo", description = "页码", required = true), + @Parameter(name = "pageSize", description = "每页数量", required = true), + @Parameter(name = "agentId", description = "代理商 ID"), + @Parameter(name = "orgId", description = "单位 ID"), + @Parameter(name = "groupId", description = "群组 ID"), + @Parameter(name = "userName", description = "用户名称"), + @Parameter(name = "account", description = "账号") + }) + @PermitAll + public CommonResult querySubordinateUser(@RequestParam("pageNo") Integer pageNo, + @RequestParam("pageSize") Integer pageSize, + @RequestParam(value = "agentId", required = false) String agentId, + @RequestParam(value = "orgId", required = false) String orgId, + @RequestParam(value = "groupId", required = false) String groupId, + @RequestParam(value = "userName", required = false) String userName, + @RequestParam(value = "account", required = false) String account) { + return success(interphoneApiService.querySubordinateUser(pageNo, pageSize, agentId, orgId, groupId, userName, account)); + } + + @GetMapping("/agent/orgs") + @Operation(summary = "查询单位列表") + @Parameters({ + @Parameter(name = "pageNo", description = "页码", required = true), + @Parameter(name = "pageSize", description = "每页数量", required = true), + @Parameter(name = "name", description = "单位名称") + }) + @PermitAll + public CommonResult getAgentOrgs(@RequestParam("pageNo") Integer pageNo, + @RequestParam("pageSize") Integer pageSize, + @RequestParam(value = "name", required = false) String name) { + return success(interphoneApiService.getAgentOrgs(pageNo, pageSize, name)); + } + + @GetMapping("/group/list") + @Operation(summary = "查询群组列表") + @Parameters({ + @Parameter(name = "pageNo", description = "页码", required = true), + @Parameter(name = "pageSize", description = "每页数量", required = true), + @Parameter(name = "name", description = "群组名称"), + @Parameter(name = "orgname", description = "单位名称") + }) + @PermitAll + public CommonResult getGroupList(@RequestParam("pageNo") Integer pageNo, + @RequestParam("pageSize") Integer pageSize, + @RequestParam(value = "name", required = false) String name, + @RequestParam(value = "orgname", required = false) String orgName) { + return success(interphoneApiService.getGroupList(pageNo, pageSize, name, orgName)); + } + + @PostMapping("/group/add") + @Operation(summary = "新增群组") + @PermitAll + public CommonResult addGroup(@RequestBody String requestBody) { + return success(interphoneApiService.addGroup(requestBody)); + } + + @GetMapping("/group/detail") + @Operation(summary = "查询群组详情") + @Parameter(name = "id", description = "群组 ID", required = true) + @PermitAll + public CommonResult getGroupDetail(@RequestParam("id") String id) { + return success(interphoneApiService.getGroupDetail(id)); + } + + @PostMapping("/group/updateGroup") + @Operation(summary = "编辑群组") + @PermitAll + public CommonResult updateGroup(@RequestBody String requestBody) { + return success(interphoneApiService.updateGroup(requestBody)); + } + + @PostMapping("/group/delete") + @Operation(summary = "删除群组") + @PermitAll + public CommonResult deleteGroup(@RequestBody String requestBody) { + return success(interphoneApiService.deleteGroup(requestBody)); + } + + @GetMapping("/group/members") + @Operation(summary = "获取群组成员") + @Parameter(name = "id", description = "群组 ID", required = true) + @PermitAll + public CommonResult getGroupMembers(@RequestParam("id") String id) { + return success(interphoneApiService.getGroupMembers(id)); + } + + @PostMapping("/group/members/add") + @Operation(summary = "添加群组成员") + @PermitAll + public CommonResult addGroupMembers(@RequestBody String requestBody) { + return success(interphoneApiService.addGroupMembers(requestBody)); + } + + @PostMapping("/group/members/remove") + @Operation(summary = "移除群组成员") + @PermitAll + public CommonResult removeGroupMembers(@RequestBody String requestBody) { + return success(interphoneApiService.removeGroupMembers(requestBody)); + } + + @GetMapping("/jsp/queryGroupByUId") + @Operation(summary = "查询用户群组") + @PermitAll + public CommonResult queryGroupByUid(@RequestParam Map queryParams) { + return success(interphoneApiService.queryGroupByUid(queryParams)); + } + + @PostMapping("/terminal/batch") + @Operation(summary = "创建对讲用户") + @PermitAll + public CommonResult createTerminalUsers(@RequestBody String requestBody) { + return success(interphoneApiService.createTerminalUsers(requestBody)); + } + + @GetMapping("/terminal/list") + @Operation(summary = "查询对讲用户列表") + @Parameters({ + @Parameter(name = "pageNo", description = "页码", required = true), + @Parameter(name = "pageSize", description = "每页数量", required = true), + @Parameter(name = "org_id", description = "单位 ID"), + @Parameter(name = "groupId", description = "群组 ID"), + @Parameter(name = "name", description = "用户名称") + }) + @PermitAll + public CommonResult getTerminalList(@RequestParam("pageNo") Integer pageNo, + @RequestParam("pageSize") Integer pageSize, + @RequestParam(value = "org_id", required = false) String orgId, + @RequestParam(value = "groupId", required = false) String groupId, + @RequestParam(value = "name", required = false) String name) { + return success(interphoneApiService.getTerminalList(pageNo, pageSize, orgId, groupId, name)); + } + + @GetMapping("/terminal/detail") + @Operation(summary = "查询对讲用户详情") + @PermitAll + public CommonResult getTerminalDetail(@RequestParam Map queryParams) { + return success(interphoneApiService.getTerminalDetail(queryParams)); + } + + @PostMapping("/terminal/updateUser") + @Operation(summary = "修改对讲用户信息") + @PermitAll + public CommonResult updateTerminalUser(@RequestBody String requestBody) { + return success(interphoneApiService.updateTerminalUser(requestBody)); + } + + @PostMapping("/terminal/deleteUser") + @Operation(summary = "删除对讲用户") + @PermitAll + public CommonResult deleteTerminalUser(@RequestBody String requestBody) { + return success(interphoneApiService.deleteTerminalUser(requestBody)); + } + + @GetMapping("/terminal/userOnlineStatus") + @Operation(summary = "查询对讲用户在线状态") + @PermitAll + public CommonResult getTerminalUserOnlineStatus(@RequestParam Map queryParams) { + return success(interphoneApiService.getTerminalUserOnlineStatus(queryParams)); + } + + @GetMapping("/record/list") + @Operation(summary = "查询录音列表") + @PermitAll + public CommonResult getRecordList(@RequestParam Map queryParams) { + return success(interphoneApiService.getRecordList(queryParams)); + } +} diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/ApiClient.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/ApiClient.java new file mode 100644 index 0000000..44ca447 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/ApiClient.java @@ -0,0 +1,119 @@ +package cn.iocoder.yudao.module.interphone.core; + +import cn.iocoder.yudao.module.interphone.auth.Author; +import cn.iocoder.yudao.module.interphone.auth.TokenManager; +import cn.iocoder.yudao.module.interphone.config.HttpClientProperties; +import cn.iocoder.yudao.module.interphone.retry.RetryContext; +import cn.iocoder.yudao.module.interphone.retry.RetryDecision; +import cn.iocoder.yudao.module.interphone.retry.RetryHandler; +import okhttp3.*; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Component +public class ApiClient { + + private final OkHttpClient okHttpClient; + private final HttpClientProperties properties; + private final TokenManager tokenManager; + private final List retryHandlers; + + public ApiClient(OkHttpClient okHttpClient, + HttpClientProperties properties, + TokenManager tokenManager, + List retryHandlers) { + this.okHttpClient = okHttpClient; + this.properties = properties; + this.tokenManager = tokenManager; + this.retryHandlers = retryHandlers; + } + + public ApiResponse execute(ApiRequest apiRequest) { + int maxRetries = properties.getHttp().getRetry().getMaxRetries(); + int attempt = 0; + + while (true) { + attempt++; + ApiResponse response = null; + Exception exception = null; + + try { + response = doExecute(apiRequest); + + if (response.isSuccess()) { + return response; + } + + RetryDecision decision = decideRetry(apiRequest, response, null, attempt); + if (decision.shouldRetry() && attempt <= maxRetries) { + continue; + } + + return response; + } catch (Exception e) { + exception = e; + RetryDecision decision = decideRetry(apiRequest, null, e, attempt); + if (decision.shouldRetry() && attempt <= maxRetries) { + continue; + } + throw new RuntimeException("HTTP 请求失败,path=" + apiRequest.getPath(), e); + } + } + } + + private ApiResponse doExecute(ApiRequest apiRequest) throws IOException { + String rawUrl = QueryStringBuilder.build( + properties.getApiUrl(), + apiRequest.getPath(), + apiRequest.getQueryParams() + ); + + HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(rawUrl), "无效请求地址").newBuilder(); + + if (apiRequest.isNeedToken()) { + Author author = tokenManager.getAuthor(); + urlBuilder.addQueryParameter("access_token", author.getAccessToken()); + urlBuilder.addQueryParameter("openid", author.getOpenId()); + } + + Request.Builder builder = new Request.Builder().url(urlBuilder.build()); + + if (apiRequest.getHeaders() != null) { + for (Map.Entry entry : apiRequest.getHeaders().entrySet()) { + builder.header(entry.getKey(), entry.getValue()); + } + } + + if (apiRequest.getMethod() == HttpMethod.GET) { + builder.get(); + } else if (apiRequest.getMethod() == HttpMethod.POST) { + RequestBody requestBody = RequestBody.create( + apiRequest.getJsonBody() == null ? "" : apiRequest.getJsonBody(), + MediaType.parse("application/json; charset=utf-8") + ); + builder.post(requestBody); + } else { + throw new RuntimeException("不支持的请求方法: " + apiRequest.getMethod()); + } + + try (Response response = okHttpClient.newCall(builder.build()).execute()) { + String body = response.body() == null ? null : Objects.requireNonNull(response.body()).string(); + return new ApiResponse(response.code(), body, response.headers().toMultimap()); + } + } + + private RetryDecision decideRetry(ApiRequest request, ApiResponse response, Exception exception, int attempt) { + RetryContext context = new RetryContext(request, response, exception, attempt); + for (RetryHandler retryHandler : retryHandlers) { + RetryDecision decision = retryHandler.decide(context); + if (decision.shouldRetry()) { + return decision; + } + } + return RetryDecision.noRetry(); + } +} diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/ApiRequest.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/ApiRequest.java new file mode 100644 index 0000000..08e5dd9 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/ApiRequest.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.interphone.core; + + +import lombok.Getter; + +import java.util.HashMap; +import java.util.Map; + + +@Getter +public class ApiRequest { + + private HttpMethod method; + private String path; + private Map queryParams = new HashMap<>(); + private Map headers = new HashMap<>(); + private String jsonBody; + private boolean needToken = true; + + + public void setMethod(HttpMethod method) { + this.method = method; + } + + public void setPath(String path) { + this.path = path; + } + + public void setQueryParams(Map queryParams) { + this.queryParams = queryParams; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public void setJsonBody(String jsonBody) { + this.jsonBody = jsonBody; + } + + public void setNeedToken(boolean needToken) { + this.needToken = needToken; + } + + public static ApiRequest get(String path, Map queryParams) { + ApiRequest request = new ApiRequest(); + request.setMethod(HttpMethod.GET); + request.setPath(path); + if (queryParams != null) { + request.setQueryParams(queryParams); + } + return request; + } + + public static ApiRequest get(String path) { + ApiRequest request = new ApiRequest(); + request.setMethod(HttpMethod.GET); + request.setPath(path); + request.setQueryParams(null); + return request; + } + + public static ApiRequest postJson(String path, String jsonBody) { + ApiRequest request = new ApiRequest(); + request.setMethod(HttpMethod.POST); + request.setPath(path); + request.setJsonBody(jsonBody); + return request; + } +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/ApiResponse.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/ApiResponse.java new file mode 100644 index 0000000..90c9e88 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/ApiResponse.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.interphone.core; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class ApiResponse { + + private int code; + private String body; + private Map> headers; + + public ApiResponse(int code, String body, Map> headers) { + this.code = code; + this.body = body; + this.headers = headers; + } + + public int getCode() { + return code; + } + + public String getBody() { + return body; + } + + public Map> getHeaders() { + return headers; + } + + public boolean isSuccess() { + return code >= 200 && code < 300; + } + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/HttpMethod.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/HttpMethod.java new file mode 100644 index 0000000..167d5eb --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/HttpMethod.java @@ -0,0 +1,5 @@ +package cn.iocoder.yudao.module.interphone.core; + +public enum HttpMethod { + GET, POST +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/QueryStringBuilder.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/QueryStringBuilder.java new file mode 100644 index 0000000..ed132fd --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/core/QueryStringBuilder.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.interphone.core; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.StringJoiner; + +@Data +public final class QueryStringBuilder { + + public static String build(String baseUrl, String path, Map params) { + StringBuilder url = new StringBuilder(); + url.append(trimRightSlash(baseUrl)); + if (!path.startsWith("/")) { + url.append("/"); + } + url.append(path); + + if (params != null && !params.isEmpty()) { + StringJoiner joiner = new StringJoiner("&"); + for (Map.Entry entry : params.entrySet()) { + if (entry.getValue() == null) { + continue; + } + joiner.add(encode(entry.getKey()) + "=" + encode(entry.getValue())); + } + String query = joiner.toString(); + if (!query.isEmpty()) { + url.append("?").append(query); + } + } + return url.toString(); + } + + private static String trimRightSlash(String text) { + if (text == null || text.isEmpty()) { + return ""; + } + return text.endsWith("/") ? text.substring(0, text.length() - 1) : text; + } + + private static String encode(String value) { + try { + return URLEncoder.encode(value, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("URL encode failed", e); + } + } +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/RetryContext.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/RetryContext.java new file mode 100644 index 0000000..87e086d --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/RetryContext.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.interphone.retry; + +import cn.iocoder.yudao.module.interphone.core.ApiRequest; +import cn.iocoder.yudao.module.interphone.core.ApiResponse; + + +public class RetryContext { + + private final ApiRequest request; + private final ApiResponse response; + private final Exception exception; + private final int attempt; + + public RetryContext(ApiRequest request, ApiResponse response, Exception exception, int attempt) { + this.request = request; + this.response = response; + this.exception = exception; + this.attempt = attempt; + } + + public ApiRequest getRequest() { + return request; + } + + public ApiResponse getResponse() { + return response; + } + + public Exception getException() { + return exception; + } + + public int getAttempt() { + return attempt; + } + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/RetryDecision.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/RetryDecision.java new file mode 100644 index 0000000..780067a --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/RetryDecision.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.interphone.retry; + + + +public class RetryDecision { + + private final boolean shouldRetry; + private final boolean tokenRefreshed; + + private RetryDecision(boolean shouldRetry, boolean tokenRefreshed) { + this.shouldRetry = shouldRetry; + this.tokenRefreshed = tokenRefreshed; + } + + public static RetryDecision noRetry() { + return new RetryDecision(false, false); + } + + public static RetryDecision retry() { + return new RetryDecision(true, false); + } + + public static RetryDecision retryAfterTokenRefresh() { + return new RetryDecision(true, true); + } + + public boolean shouldRetry() { + return shouldRetry; + } + + public boolean isTokenRefreshed() { + return tokenRefreshed; + } + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/RetryHandler.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/RetryHandler.java new file mode 100644 index 0000000..9f4ffda --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/RetryHandler.java @@ -0,0 +1,6 @@ +package cn.iocoder.yudao.module.interphone.retry; + +public interface RetryHandler { + + RetryDecision decide(RetryContext context); +} diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/TokenExpiredRetryHandler.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/TokenExpiredRetryHandler.java new file mode 100644 index 0000000..30dd696 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/retry/TokenExpiredRetryHandler.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.interphone.retry; + +import cn.iocoder.yudao.module.interphone.auth.DefaultTokenManager; +import cn.iocoder.yudao.module.interphone.core.ApiResponse; +import org.springframework.stereotype.Component; + +@Component +public class TokenExpiredRetryHandler implements RetryHandler { + + private final DefaultTokenManager tokenManager; + + public TokenExpiredRetryHandler(DefaultTokenManager tokenManager) { + this.tokenManager = tokenManager; + } + + @Override + public RetryDecision decide(RetryContext context) { + ApiResponse response = context.getResponse(); + if (response == null) { + return RetryDecision.noRetry(); + } + + // 方式1:HTTP 状态码 401 / 403 + if (response.getCode() == 401 || response.getCode() == 403 || response.getCode() == 40102) { + tokenManager.forceRefreshToken(); + return RetryDecision.retryAfterTokenRefresh(); + } + + // 方式2:业务码判断 + String body = response.getBody(); + if (body != null && + (body.contains("\"code\":40102") || body.contains("token expired") || body.contains("登录过期"))) { + tokenManager.forceRefreshToken(); + return RetryDecision.retryAfterTokenRefresh(); + } + + return RetryDecision.noRetry(); + } +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/service/InterphoneApiService.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/service/InterphoneApiService.java new file mode 100644 index 0000000..7651c45 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/service/InterphoneApiService.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.interphone.service; + +import java.util.Map; + +public interface InterphoneApiService { + + String getAgentProfile(); + + String getAgentList(Integer pageNo, Integer pageSize, String name); + + String getAgentDetail(String id); + + String getFastStats(); + + String getGroupName(String orgId); + + String querySubordinateUser(Integer pageNo, Integer pageSize, String agentId, String orgId, + String groupId, String userName, String account); + + String getAgentOrgs(Integer pageNo, Integer pageSize, String name); + + String getGroupList(Integer pageNo, Integer pageSize, String name, String orgName); + + String addGroup(String requestBody); + + String getGroupDetail(String id); + + String updateGroup(String requestBody); + + String deleteGroup(String requestBody); + + String getGroupMembers(String id); + + String addGroupMembers(String requestBody); + + String removeGroupMembers(String requestBody); + + String queryGroupByUid(Map queryParams); + + String createTerminalUsers(String requestBody); + + String getTerminalList(Integer pageNo, Integer pageSize, String orgId, String groupId, String name); + + String getTerminalDetail(Map queryParams); + + String updateTerminalUser(String requestBody); + + String deleteTerminalUser(String requestBody); + + String getTerminalUserOnlineStatus(Map queryParams); + + String getRecordList(Map queryParams); +} diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/service/apidoc.md b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/service/apidoc.md new file mode 100644 index 0000000..9961fde --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/service/apidoc.md @@ -0,0 +1,470 @@ +# 管理平台 API 文档 + +Base URL + +```text +https://chat.zdhlcn.com:9443/api +``` + +--- + +# 通用返回格式 + +```json +{ + "code": 20001, + "msg": "OK", + "data": {}, + "count": 0 +} +``` + +--- + +# 1 用户信息 + +--- + +# 1.1 个人信息 - 代理商 + +### 接口 + +``` +GET /v1/profile/agent +``` + +### 参数 + +| 参数 | 类型 | 必填 | 说明 | +| ------------ | ------ | -- | ---- | +| access_token | String | 是 | 登录凭证 | +| openid | String | 是 | 用户ID | + +--- + +### 返回示例 + +```json +{ + "code":20001, + "msg":"OK", + "data":{ + "loginname":"broadtest", + "usertype":0, + "agentinfo":{ + "id":"efad38ced1bb4ac0bf97665041db752a", + "name":"测试平台", + "contact":"测试测试", + "country_id":86, + "blance":939 + } + } +} +``` + +--- + +# 2 代理商管理 + +--- + +# 2.1 代理商查询 + +``` +GET /v1/agent/list +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| pageNo | int | 是 | +| pageSize | int | 是 | +| name | string | 否 | + +--- + +# 2.2 查询代理商详情 + +``` +GET /v1/agent/detail +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| id | String | 是 | + +--- + +# 2.3 查询代理商单位群组用户数 + +``` +GET /v1/profile/faststats +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | + +--- + +### 返回示例 + +```json +{ + "agent_count":232, + "org_count":107, + "group_count":107, + "user_count":488 +} +``` + +--- + +# 3 群组相关 + +--- + +# 3.1 查询群组名称 + +``` +GET /v1/group/getGroupName +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| orgId | String | 是 | + +--- + +# 3.2 查询下级用户 + +``` +GET /v1/terminal/querySubordinateUser +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| pageNo | int | 是 | +| pageSize | int | 是 | +| agentId | String | 否 | +| orgId | String | 否 | +| groupId | String | 否 | +| userName | String | 否 | +| account | String | 否 | + +--- + +# 4 单位管理 + +--- + +# 4.1 单位查询 + +``` +GET /v1/agent/orgs +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| pageNo | int | 是 | +| pageSize | int | 是 | +| name | string | 否 | + +--- + +# 5 群组管理 + +--- + +# 5.1 群组查询 + +``` +GET /v1/group/list +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| pageNo | int | 是 | +| pageSize | int | 是 | +| name | string | 否 | +| orgname | string | 否 | + +--- + +# 5.2 添加群组 + +``` +POST /v1/group/add +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ---------------------- | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| orgId | String | 是 | +| cgName | String | 是 | +| cg_speech_limit_second | int | 是 | +| remarks | String | 否 | + +--- + +# 5.3 群组详情 + +``` +GET /v1/group/detail +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| id | String | 是 | + +--- + +# 5.4 编辑群组 + +``` +POST /v1/group/updateGroup +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ---------------------- | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| id | String | 是 | +| cgName | String | 是 | +| cg_speech_limit_second | int | 是 | +| remarks | String | 是 | + +--- + +# 5.5 删除群组 + +``` +POST /v1/group/delete +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| id | String | 是 | + +--- + +# 6 群组成员 + +--- + +# 6.1 获取群组成员 + +``` +GET /v1/group/members +``` + +### 参数 + +| 参数 | 类型 | 必填 | +| ------------ | ------ | -- | +| access_token | String | 是 | +| openid | String | 是 | +| id | String | 是 | + +--- + +# 6.2 添加群组成员 + +``` +POST /v1/group/members/add +``` + +### 示例 + +```json +{ + "id":"groupId", + "members":[ + { + "id":"userUuid", + "user_id":5583024, + "priorities":1 + } + ] +} +``` + +--- + +# 6.3 移除群组成员 + +``` +POST /v1/group/members/remove +``` + +--- + +# 6.4 获取用户群组 + +``` +GET /v1/jsp/queryGroupByUId +``` + +--- + +# 7 对讲用户 + +--- + +# 7.1 创建用户 + +``` +POST /v1/terminal/batch +``` + +### 示例 + +```json +{ + "orgId":"uuid", + "groups":[{"id":"groupId"}], + "prefix":"test", + "accounts":["imei1","imei2"], + "cardTypes":[0,2] +} +``` + +--- + +# 7.2 用户查询 + +``` +GET /v1/terminal/list +``` + +### 参数 + +| 参数 | 类型 | +| -------- | ------ | +| pageNo | int | +| pageSize | int | +| org_id | String | +| groupId | String | +| name | String | + +--- + +# 7.3 用户详情 + +``` +GET /v1/terminal/detail +``` + +--- + +# 7.4 修改用户信息 + +``` +POST /v1/terminal/updateUser +``` + +--- + +# 7.5 删除用户 + +``` +POST /v1/terminal/deleteUser +``` + +--- + +# 7.6 查询用户在线状态 + +``` +GET /v1/terminal/userOnlineStatus +``` + +--- + +# 8 录音 + +--- + +# 8.1 查询录音 + +``` +GET /v1/record/list +``` + +--- + +# 接口模块结构 + +``` +用户 +├─ 个人信息 + +代理商 +├─ 查询 +├─ 详情 +├─ 统计 + +单位 +├─ 查询 + +群组 +├─ 查询 +├─ 创建 +├─ 编辑 +├─ 删除 + +群组成员 +├─ 获取 +├─ 添加 +├─ 删除 + +对讲用户 +├─ 创建 +├─ 查询 +├─ 详情 +├─ 修改 +├─ 删除 +├─ 在线状态 + +录音 +├─ 查询 +``` diff --git a/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/service/impl/InterphoneApiServiceImpl.java b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/service/impl/InterphoneApiServiceImpl.java new file mode 100644 index 0000000..d0fac1c --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/main/java/cn/iocoder/yudao/module/interphone/service/impl/InterphoneApiServiceImpl.java @@ -0,0 +1,207 @@ +package cn.iocoder.yudao.module.interphone.service.impl; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.interphone.core.ApiClient; +import cn.iocoder.yudao.module.interphone.core.ApiRequest; +import cn.iocoder.yudao.module.interphone.core.ApiResponse; +import cn.iocoder.yudao.module.interphone.service.InterphoneApiService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.LinkedHashMap; +import java.util.Map; + +@Service +@Validated +public class InterphoneApiServiceImpl implements InterphoneApiService { + + private final ApiClient apiClient; + + public InterphoneApiServiceImpl(ApiClient apiClient) { + this.apiClient = apiClient; + } + + @Override + public String getAgentProfile() { + return executeGet("/profile/agent"); + } + + @Override + public String getAgentList(Integer pageNo, Integer pageSize, String name) { + Map queryParams = new LinkedHashMap<>(); + putIfNotNull(queryParams, "pageNo", pageNo); + putIfNotNull(queryParams, "pageSize", pageSize); + putIfHasText(queryParams, "name", name); + return executeGet("/agent/list", queryParams); + } + + @Override + public String getAgentDetail(String id) { + return executeGet("/agent/detail", Map.of("id", id)); + } + + @Override + public String getFastStats() { + return executeGet("/profile/faststats"); + } + + @Override + public String getGroupName(String orgId) { + return executeGet("/group/getGroupName", Map.of("orgId", orgId)); + } + + @Override + public String querySubordinateUser(Integer pageNo, Integer pageSize, String agentId, String orgId, + String groupId, String userName, String account) { + Map queryParams = new LinkedHashMap<>(); + putIfNotNull(queryParams, "pageNo", pageNo); + putIfNotNull(queryParams, "pageSize", pageSize); + putIfHasText(queryParams, "agentId", agentId); + putIfHasText(queryParams, "orgId", orgId); + putIfHasText(queryParams, "groupId", groupId); + putIfHasText(queryParams, "userName", userName); + putIfHasText(queryParams, "account", account); + return executeGet("/terminal/querySubordinateUser", queryParams); + } + + @Override + public String getAgentOrgs(Integer pageNo, Integer pageSize, String name) { + Map queryParams = new LinkedHashMap<>(); + putIfNotNull(queryParams, "pageNo", pageNo); + putIfNotNull(queryParams, "pageSize", pageSize); + putIfHasText(queryParams, "name", name); + return executeGet("/agent/orgs", queryParams); + } + + @Override + public String getGroupList(Integer pageNo, Integer pageSize, String name, String orgName) { + Map queryParams = new LinkedHashMap<>(); + putIfNotNull(queryParams, "pageNo", pageNo); + putIfNotNull(queryParams, "pageSize", pageSize); + putIfHasText(queryParams, "name", name); + putIfHasText(queryParams, "orgname", orgName); + return executeGet("/group/list", queryParams); + } + + @Override + public String addGroup(String requestBody) { + return executePost("/group/add", requestBody); + } + + @Override + public String getGroupDetail(String id) { + return executeGet("/group/detail", Map.of("id", id)); + } + + @Override + public String updateGroup(String requestBody) { + return executePost("/group/updateGroup", requestBody); + } + + @Override + public String deleteGroup(String requestBody) { + return executePost("/group/delete", requestBody); + } + + @Override + public String getGroupMembers(String id) { + return executeGet("/group/members", Map.of("id", id)); + } + + @Override + public String addGroupMembers(String requestBody) { + return executePost("/group/members/add", requestBody); + } + + @Override + public String removeGroupMembers(String requestBody) { + return executePost("/group/members/remove", requestBody); + } + + @Override + public String queryGroupByUid(Map queryParams) { + return executeGet("/jsp/queryGroupByUId", queryParams); + } + + @Override + public String createTerminalUsers(String requestBody) { + return executePost("/terminal/batch", requestBody); + } + + @Override + public String getTerminalList(Integer pageNo, Integer pageSize, String orgId, String groupId, String name) { + Map queryParams = new LinkedHashMap<>(); + putIfNotNull(queryParams, "pageNo", pageNo); + putIfNotNull(queryParams, "pageSize", pageSize); + putIfHasText(queryParams, "org_id", orgId); + putIfHasText(queryParams, "groupId", groupId); + putIfHasText(queryParams, "name", name); + return executeGet("/terminal/list", queryParams); + } + + @Override + public String getTerminalDetail(Map queryParams) { + return executeGet("/terminal/detail", queryParams); + } + + @Override + public String updateTerminalUser(String requestBody) { + return executePost("/terminal/updateUser", requestBody); + } + + @Override + public String deleteTerminalUser(String requestBody) { + return executePost("/terminal/deleteUser", requestBody); + } + + @Override + public String getTerminalUserOnlineStatus(Map queryParams) { + return executeGet("/terminal/userOnlineStatus", queryParams); + } + + @Override + public String getRecordList(Map queryParams) { + return executeGet("/record/list", queryParams); + } + + private String executeGet(String path) { + return executeGet(path, null); + } + + private String executeGet(String path, Map queryParams) { + ApiRequest request = ApiRequest.get(path, sanitizeQueryParams(queryParams)); + ApiResponse response = apiClient.execute(request); + return response.getBody(); + } + + private String executePost(String path, String requestBody) { + ApiRequest request = ApiRequest.postJson(path, StrUtil.emptyToDefault(requestBody, "")); + ApiResponse response = apiClient.execute(request); + return response.getBody(); + } + + private Map sanitizeQueryParams(Map queryParams) { + if (queryParams == null || queryParams.isEmpty()) { + return null; + } + Map sanitized = new LinkedHashMap<>(); + queryParams.forEach((key, value) -> { + if (value != null) { + sanitized.put(key, value); + } + }); + return sanitized.isEmpty() ? null : sanitized; + } + + private void putIfHasText(Map queryParams, String key, String value) { + if (StrUtil.isNotBlank(value)) { + queryParams.put(key, value); + } + } + + private void putIfNotNull(Map queryParams, String key, Object value) { + if (value != null) { + queryParams.put(key, String.valueOf(value)); + } + } +} diff --git a/cc-admin-master/yudao-module-interphone/src/test/java/cn/iocoder/yudao/module/interphone/demo/DemoRemoteClientTest.java b/cc-admin-master/yudao-module-interphone/src/test/java/cn/iocoder/yudao/module/interphone/demo/DemoRemoteClientTest.java new file mode 100644 index 0000000..900da27 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/test/java/cn/iocoder/yudao/module/interphone/demo/DemoRemoteClientTest.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.interphone.demo; + +import cn.iocoder.yudao.framework.test.core.ut.BaseRedisUnitTest; +import cn.iocoder.yudao.module.interphone.auth.DefaultLoginService; +import cn.iocoder.yudao.module.interphone.auth.DefaultTokenManager; +import cn.iocoder.yudao.module.interphone.auth.LoginService; +import cn.iocoder.yudao.module.interphone.config.HttpClientProperties; +import cn.iocoder.yudao.module.interphone.config.OkHttpClientConfig; +import cn.iocoder.yudao.module.interphone.core.ApiClient; +import cn.iocoder.yudao.module.interphone.core.ApiRequest; +import cn.iocoder.yudao.module.interphone.core.ApiResponse; +import cn.iocoder.yudao.module.interphone.retry.TokenExpiredRetryHandler; +import jakarta.annotation.Resource; +import okhttp3.OkHttpClient; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + + +@Import({ + OkHttpClientConfig.class, + DefaultLoginService.class, + DefaultTokenManager.class, + ApiClient.class, + TokenExpiredRetryHandler.class, + OkHttpClient.class +}) +public class DemoRemoteClientTest extends BaseRedisUnitTest { + + + @Resource + private ApiClient apiClient; + + @Test + public void testGetUserInfo_Success() { + ApiRequest request = ApiRequest.get("/profile/agent"); + ApiResponse response = apiClient.execute(request); + System.out.println(response); + } +} diff --git a/cc-admin-master/yudao-module-interphone/src/test/java/cn/iocoder/yudao/module/interphone/demo/TestConfig.java b/cc-admin-master/yudao-module-interphone/src/test/java/cn/iocoder/yudao/module/interphone/demo/TestConfig.java new file mode 100644 index 0000000..345132c --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/test/java/cn/iocoder/yudao/module/interphone/demo/TestConfig.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.interphone.demo; + +import cn.iocoder.yudao.module.interphone.auth.DefaultLoginService; +import cn.iocoder.yudao.module.interphone.auth.LoginService; +import cn.iocoder.yudao.module.interphone.config.HttpClientProperties; +import okhttp3.OkHttpClient; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +@TestConfiguration +public class TestConfig { + @Bean + public LoginService loginService(OkHttpClient okHttpClient,HttpClientProperties httpClientProperties) { + return new DefaultLoginService(okHttpClient, httpClientProperties); + } +} diff --git a/cc-admin-master/yudao-module-interphone/src/test/resources/application-unit-test.yaml b/cc-admin-master/yudao-module-interphone/src/test/resources/application-unit-test.yaml new file mode 100644 index 0000000..ff20cb1 --- /dev/null +++ b/cc-admin-master/yudao-module-interphone/src/test/resources/application-unit-test.yaml @@ -0,0 +1,504 @@ +spring: + application: + name: gas_mobile + + main: + allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 + allow-bean-definition-overriding: true + # Servlet 配置 + servlet: + # 文件上传相关配置项 + multipart: + max-file-size: 16MB # 单个文件大小 + max-request-size: 32MB # 设置总上传的文件大小 + + # Jackson 配置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳 + write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 + fail-on-empty-beans: false # 允许序列化无属性的 Bean + + # Cache 配置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 小时 + +server: + servlet: + encoding: + enabled: true + charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题 + force: true + +--- #################### 接口文档配置 #################### + +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui + default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 + setting: + language: zh_cn + +# 工作流 Flowable 配置 +flowable: + # 1. false: 默认值,Flowable 启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常 + # 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表 + # 3. create_drop: 启动时自动创建表,关闭时自动删除表 + # 4. drop_create: 启动时,删除旧表,再创建新表 + database-schema-update: true # 设置为 false,可通过 https://github.com/flowable/flowable-sql 初始化 + db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置 + check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程 + history-level: audit # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 + +# MyBatis Plus 的配置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + # id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库 + # id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 + # id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制台的 Banner 打印 + type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 + +mybatis-plus-join: + banner: false # 是否打印 mybatis plus join banner,默认true + sub-table-logic: true # 全局启用副表逻辑删除,默认true。关闭后关联查询不会加副表逻辑删除 + ms-cache: true # 拦截器MappedStatement缓存,默认 true + table-alias: t # 表别名(默认 t) + logic-del-type: on # 副表逻辑删除条件的位置,支持 WHERE、ON,默认 ON + +# Spring Data Redis 配置 +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度 + +# VO 转换(数据翻译)相关 +easy-trans: + is-enable-global: true # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口 + +--- #################### 验证码相关配置 #################### + +aj: + captcha: + jigsaw: classpath:images/jigsaw # 滑动验证,底图路径,不配置将使用默认图片;以 classpath: 开头,取 resource 目录下路径 + pic-click: classpath:images/pic-click # 滑动验证,底图路径,不配置将使用默认图片;以 classpath: 开头,取 resource 目录下路径 + cache-type: redis # 缓存 local/redis... + cache-number: 1000 # local 缓存的阈值,达到这个值,清除缓存 + timing-clear: 180 # local定时清除过期缓存(单位秒),设置为0代表不执行 + type: blockPuzzle # 验证码类型 default两种都实例化。 blockPuzzle 滑块拼图 clickWord 文字点选 + water-mark: 1 # 右下角水印文字(我的水印),可使用 https://tool.chinaz.com/tools/unicode.aspx 中文转 Unicode,Linux 可能需要转 unicode + interference-options: 0 # 滑动干扰项(0/1/2) + req-frequency-limit-enable: false # 接口请求次数一分钟限制是否开启 true|false + req-get-lock-limit: 5 # 验证失败 5 次,get接口锁定 + req-get-lock-seconds: 10 # 验证失败后,锁定时间间隔 + req-get-minute-limit: 30 # get 接口一分钟内请求数限制 + req-check-minute-limit: 60 # check 接口一分钟内请求数限制 + req-verify-minute-limit: 60 # verify 接口一分钟内请求数限制 + +--- #################### 消息队列相关 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + # Producer 配置项 + producer: + group: ${spring.application.name}_PRODUCER # 生产者分组 + +spring: + kafka: + producer: + acks: 1 + retries: 3 + batch-size: 16384 + key-serializer: org.apache.kafka.common.serialization.StringSerializer + value-serializer: org.apache.kafka.common.serialization.StringSerializer + properties: + linger.ms: 10 + buffer.memory: 33554432 + consumer: + enable-auto-commit: false + auto-offset-reset: earliest + max-poll-records: 1000 + fetch-max-wait: 3000 + + # 【修正3】改为 StringDeserializer + key-deserializer: org.apache.kafka.common.serialization.StringDeserializer + value-deserializer: org.apache.kafka.common.serialization.StringDeserializer + + group-id: consumer-${spring.application.name} + + listener: + missing-topics-fatal: false + +--- #################### 芋道相关配置 #################### + +yudao: + info: + version: 1.0.0 + base-package: cn.iocoder.yudao + web: + admin-ui: + url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址 + xss: + enable: false + exclude-urls: # 如下两个 url,仅仅是为了演示,去掉配置也没关系 + - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + security: + permit-all_urls: + - /admin-api/mp/open/** # 微信公众号开放平台,微信回调接口,不需要登录 + websocket: + enable: true # websocket的开关 + path: /infra/ws # 路径 + sender-type: local # 消息发送的类型,可选值为 local、redis、rocketmq、kafka、rabbitmq + sender-rocketmq: + topic: ${spring.application.name}-websocket # 消息发送的 RocketMQ Topic + consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 RocketMQ Consumer Group + sender-rabbitmq: + exchange: ${spring.application.name}-websocket-exchange # 消息发送的 RabbitMQ Exchange + queue: ${spring.application.name}-websocket-queue # 消息发送的 RabbitMQ Queue + sender-kafka: + topic: ${spring.application.name}-websocket # 消息发送的 Kafka Topic + consumer-group: ${spring.application.name}-websocket-consumer # 消息发送的 Kafka Consumer Group + swagger: + title: 芋道快速开发平台 + description: 提供管理后台、用户 App 的所有功能 + version: ${yudao.info.version} + url: ${yudao.web.admin-ui.url} + email: xingyu4j@vip.qq.com + license: MIT + license-url: https://gitee.com/zhijiantianya/ruoyi-vue-pro/blob/master/LICENSE + codegen: + base-package: ${yudao.info.base-package} + db-schemas: ${spring.datasource.dynamic.datasource.master.name} + front-type: 20 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类 + vo-type: 10 # VO 的类型,参见 CodegenVOTypeEnum 枚举类 + delete-batch-enable: true # 是否生成批量删除接口 + unit-test-enable: false # 是否生成单元测试 + tenant: # 多租户相关配置项 + enable: true + ignore-urls: + - /jmreport/* # 积木报表,无法携带租户编号 + ignore-visit-urls: + - /admin-api/system/user/profile/** + - /admin-api/system/auth/** + ignore-tables: + ignore-caches: + - user_role_ids + - permission_menu_ids + - oauth_client + - notify_template + - mail_account + - mail_template + - sms_template + - iot:device + - iot:thing_model_list + sms-code: # 短信验证码相关的配置项 + expire-times: 10m + send-frequency: 1m + send-maximum-quantity-per-day: 10 + begin-code: 9999 # 这里配置 9999 的原因是,测试方便。 + end-code: 9999 # 这里配置 9999 的原因是,测试方便。 + trade: + order: + pay-expire-time: 2h # 支付的过期时间 + receive-expire-time: 14d # 收货的过期时间 + comment-expire-time: 7d # 评论的过期时间 + status-sync-to-wxa-enable: true # 是否同步订单状态到微信小程序 + express: + client: kd_100 + kd-niao: + api-key: cb022f1e-48f1-4c4a-a723-9001ac9676b8 + business-id: 1809751 + request-type: 1002 # 免费版 1002;付费版 8001 + kd100: + key: pLXUGAwK5305 + customer: E77DF18BE109F454A5CD319E44BF5177 + +debug: false +# 插件配置 TODO 芋艿:【IOT】需要处理下 +pf4j: + pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录 + +server: + port: 48081 + +--- #################### 数据库相关配置 #################### +spring: + autoconfigure: + # noinspection SpringBootApplicationYaml + exclude: + # - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置 + # - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 + # - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 + # - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 + - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + # 数据源配置项 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://127.0.0.1:13307/hand_alarm_dev?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: root + password: Gsking164411 + driver-class-name: com.mysql.cj.jdbc.Driver # MySQL Connector/J 8.X 连接的示例 + tdengine: + url: jdbc:TAOS-RS://127.0.0.1:6042/hand_alarm + username: root + password: Gsking164411 + driver-class-name: com.taosdata.jdbc.rs.RestfulDriver # TDengine 连接的示例 + druid: + validation-query: SELECT 1 # TDengine 的验证 SQL + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 127.0.0.1 # 地址 + port: 6379 # 端口 + database: 5 # 数据库索引 + # password: dev # 密码,建议生产环境开启 + +--- #################### 定时任务相关配置 #################### + +# Quartz 配置项,对应 QuartzProperties 配置类 +spring: + quartz: + auto-startup: true # 本地开发环境,尽量不要开启 Job + scheduler-name: schedulerName # Scheduler 名字。默认为 schedulerName + job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。 + wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true + properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档 + org: + quartz: + # Scheduler 相关配置 + scheduler: + instanceName: schedulerName + instanceId: AUTO # 自动生成 instance ID + # JobStore 相关配置 + jobStore: + # JobStore 实现类。可见博客:https://blog.csdn.net/weixin_42458219/article/details/122247162 + class: org.springframework.scheduling.quartz.LocalDataSourceJobStore + isClustered: true # 是集群模式 + clusterCheckinInterval: 15000 # 集群检查频率,单位:毫秒。默认为 15000,即 15 秒 + misfireThreshold: 60000 # misfire 阀值,单位:毫秒。 + # 线程池相关配置 + threadPool: + threadCount: 25 # 线程池大小。默认为 10 。 + threadPriority: 5 # 线程优先级 + class: org.quartz.simpl.SimpleThreadPool # 线程池类型 + jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置 + initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。 + +--- #################### 消息队列相关 #################### + +# rocketmq 配置项,对应 RocketMQProperties 配置类 +rocketmq: + name-server: 127.0.0.1:9876 # RocketMQ Namesrv + +spring: + # RabbitMQ 配置项,对应 RabbitProperties 配置类 + rabbitmq: + host: 127.0.0.1 # RabbitMQ 服务的地址 + port: 5672 # RabbitMQ 服务的端口 + username: rabbit # RabbitMQ 服务的账号 + password: rabbit # RabbitMQ 服务的密码 + # Kafka 配置项,对应 KafkaProperties 配置类 + kafka: + bootstrap-servers: video.zdhlcn.com:9092 # 或者内网地址 172.21.16.6:9091,或者测试地址video.zdhlcn.com:9092,zdmq.zdhlcn.com:9092 + properties: + security.protocol: SASL_PLAINTEXT + sasl.mechanism: SCRAM-SHA-512 + sasl.jaas.config: 'org.apache.kafka.common.security.scram.ScramLoginModule required username="zdkafka" password="Zdhl@2025";' +##################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址 + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + # Spring Boot Admin Server 服务端的相关配置 + context-path: /admin # 配置 Spring + +# 日志文件配置 +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + level: + # 配置自己写的 MyBatis Mapper 打印日志 + cn.iocoder.yudao.module.bpm.dal.mysql: debug + cn.iocoder.yudao.module.infra.dal.mysql: debug + cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper: INFO # 配置 ApiErrorLogMapper 的日志级别为 info,避免和 GlobalExceptionHandler 重复打印 + cn.iocoder.yudao.module.infra.dal.mysql.job.JobLogMapper: INFO # 配置 JobLogMapper 的日志级别为 info + cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper: INFO # 配置 FileConfigMapper 的日志级别为 info + cn.iocoder.yudao.module.pay.dal.mysql: debug + cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper: INFO # 配置 PayNotifyTaskMapper 的日志级别为 info + cn.iocoder.yudao.module.system.dal.mysql: debug + cn.iocoder.yudao.module.mqtt: info + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + # 打开 dynamic-datasource 框架的 DEBUG 日志 + #com.baomidou.dynamic.datasource: DEBUG +debug: false + +--- #################### 微信公众号、小程序相关配置 #################### +wx: + mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 + app-id: wxf56b1542b9e85f8a # 测试号(Kongdy 提供的) + secret: 496379dcef1ba869e9234de8d598cfd3 + # 存储配置,解决 AccessToken 的跨节点的共享 + cp: + # 你的企业ID + corpId: ww6e1eee0a8ae45397 + agentId: 1000002 + corpSecret: ITbfuoZkmUifGoDL5ZB8SyuMzVM8VXZNkfZJzYn5sGo + + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wx # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 + appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) + secret: 4a1a04e07f6a4a0751b39c3064a92c8b + config-storage: + type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 + key-prefix: wa # Redis Key 的前缀 + http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台 + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + captcha: + enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试; + security: + mock-enable: true + access-log: # 访问日志的配置项 + enable: false + demo: false # 关闭演示模式 + wxa-code: + env-version: develop # 小程序版本: 正式版为 "release";体验版为 "trial";开发版为 "develop" + wxa-subscribe-message: + miniprogram-state: developer # 跳转小程序类型:开发版为 “developer”;体验版为 “trial”为;正式版为 “formal” + tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc + + + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 + +--- #################### iot相关配置 TODO 芋艿【IOT】:再瞅瞅 #################### +pf4j: + # pluginsDir: /tmp/ + pluginsDir: ../plugins + +mqtt: + enable: true + url: tcp://127.0.0.1:1883 + username: root + password: roomasd111 + client: + id: cc-admin-qg-dev + connectionTimeout: 10 + keepAliveInterval: 60 + cleanSession: true + subscribe: + # $share/hand_alarm/+/zds_up //MQTT 共享订阅,不是多台服务器的情况下不要开启 + topic: +/zds_up,+/zds_down + qos: 1,1 + default: + publishQos: 0 + offlineTime: 180 # 超过 180 秒无数据则判为数据超时 + pool: + coreSize: 10 + maxSize: 20 + queueSize: 100 + alarm: + rate-limit: 3000.0 # 报警 MQTT 推送限流速率(每秒最大请求数) + + +interphone: + username: zdhladmin + password: 123456a. + apiUrl: https://chat.zdhlcn.com:9443/api/v1/ + userType: 0 + appId: 97796fef376d41e0a7dda02720d0e3c9 + appSecret: m597gv5h4hmdusce + + http: + connect-timeout-seconds: 5 + read-timeout-seconds: 10 + write-timeout-seconds: 10 + call-timeout-seconds: 15 + max-idle-connections: 50 + keep-alive-duration-minutes: 5 + retry: + max-retries: 2 \ No newline at end of file diff --git a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/TenantInterphoneController.java b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/TenantInterphoneController.java new file mode 100644 index 0000000..a08c400 --- /dev/null +++ b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/TenantInterphoneController.java @@ -0,0 +1,104 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenantinterphone; + +import org.springframework.web.bind.annotation.*; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import jakarta.validation.constraints.*; +import jakarta.validation.*; +import jakarta.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.system.controller.admin.tenantinterphone.vo.*; +import cn.iocoder.yudao.module.system.dal.dataobject.tenantinterphone.TenantInterphoneDO; +import cn.iocoder.yudao.module.system.service.tenantinterphone.TenantInterphoneService; + +@Tag(name = "管理后台 - 租户对讲平台对应") +@RestController +@RequestMapping("/system/tenant-interphone") +@Validated +public class TenantInterphoneController { + + @Resource + private TenantInterphoneService tenantInterphoneService; + + @PostMapping("/create") + @Operation(summary = "创建租户对讲平台对应") + @PreAuthorize("@ss.hasPermission('system:tenant-interphone:create')") + public CommonResult createTenantInterphone(@Valid @RequestBody TenantInterphoneSaveReqVO createReqVO) { + return success(tenantInterphoneService.createTenantInterphone(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新租户对讲平台对应") + @PreAuthorize("@ss.hasPermission('system:tenant-interphone:update')") + public CommonResult updateTenantInterphone(@Valid @RequestBody TenantInterphoneSaveReqVO updateReqVO) { + tenantInterphoneService.updateTenantInterphone(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除租户对讲平台对应") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('system:tenant-interphone:delete')") + public CommonResult deleteTenantInterphone(@RequestParam("id") Long id) { + tenantInterphoneService.deleteTenantInterphone(id); + return success(true); + } + + @DeleteMapping("/delete-list") + @Parameter(name = "ids", description = "编号", required = true) + @Operation(summary = "批量删除租户对讲平台对应") + @PreAuthorize("@ss.hasPermission('system:tenant-interphone:delete')") + public CommonResult deleteTenantInterphoneList(@RequestParam("ids") List ids) { + tenantInterphoneService.deleteTenantInterphoneListByIds(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得租户对讲平台对应") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:tenant-interphone:query')") + public CommonResult getTenantInterphone(@RequestParam("id") Long id) { + TenantInterphoneDO tenantInterphone = tenantInterphoneService.getTenantInterphone(id); + return success(BeanUtils.toBean(tenantInterphone, TenantInterphoneRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得租户对讲平台对应分页") + @PreAuthorize("@ss.hasPermission('system:tenant-interphone:query')") + public CommonResult> getTenantInterphonePage(@Valid TenantInterphonePageReqVO pageReqVO) { + PageResult pageResult = tenantInterphoneService.getTenantInterphonePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, TenantInterphoneRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出租户对讲平台对应 Excel") + @PreAuthorize("@ss.hasPermission('system:tenant-interphone:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportTenantInterphoneExcel(@Valid TenantInterphonePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = tenantInterphoneService.getTenantInterphonePage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "租户对讲平台对应.xls", "数据", TenantInterphoneRespVO.class, + BeanUtils.toBean(list, TenantInterphoneRespVO.class)); + } + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/vo/TenantInterphonePageReqVO.java b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/vo/TenantInterphonePageReqVO.java new file mode 100644 index 0000000..a44f23d --- /dev/null +++ b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/vo/TenantInterphonePageReqVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenantinterphone.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 租户对讲平台对应分页 Request VO") +@Data +public class TenantInterphonePageReqVO extends PageParam { + + @Schema(description = "租户id", example = "18871") + private Long tendId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "对讲平台单位id", example = "16985") + private Long corgId; + + @Schema(description = "对讲平台单位名称", example = "张三") + private String corgName; + + @Schema(description = "对讲平台用户数量", example = "11267") + private Long userCount; + + @Schema(description = "对讲平台群组数量", example = "27649") + private Long groupCount; + + @Schema(description = "对讲平台账号", example = "张三") + private String loginname; + + @Schema(description = "对讲平台联系人") + private String contact; + + @Schema(description = "对讲平台单位是否绑定平台") + private Integer bindingPlatform; + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/vo/TenantInterphoneRespVO.java b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/vo/TenantInterphoneRespVO.java new file mode 100644 index 0000000..568c118 --- /dev/null +++ b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/vo/TenantInterphoneRespVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenantinterphone.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; + +@Schema(description = "管理后台 - 租户对讲平台对应 Response VO") +@Data +@ExcelIgnoreUnannotated +public class TenantInterphoneRespVO { + + @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "8781") + @ExcelProperty("id") + private Long id; + + @Schema(description = "租户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "18871") + @ExcelProperty("租户id") + private Long tendId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + + @Schema(description = "对讲平台单位id", requiredMode = Schema.RequiredMode.REQUIRED, example = "16985") + @ExcelProperty("对讲平台单位id") + private Long corgId; + + @Schema(description = "对讲平台单位名称", example = "张三") + @ExcelProperty("对讲平台单位名称") + private String corgName; + + @Schema(description = "对讲平台用户数量", example = "11267") + @ExcelProperty("对讲平台用户数量") + private Long userCount; + + @Schema(description = "对讲平台群组数量", example = "27649") + @ExcelProperty("对讲平台群组数量") + private Long groupCount; + + @Schema(description = "对讲平台账号", example = "张三") + @ExcelProperty("对讲平台账号") + private String loginname; + + @Schema(description = "对讲平台联系人") + @ExcelProperty("对讲平台联系人") + private String contact; + + @Schema(description = "对讲平台单位是否绑定平台") + @ExcelProperty("对讲平台单位是否绑定平台") + private Integer bindingPlatform; + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/vo/TenantInterphoneSaveReqVO.java b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/vo/TenantInterphoneSaveReqVO.java new file mode 100644 index 0000000..df94ccb --- /dev/null +++ b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenantinterphone/vo/TenantInterphoneSaveReqVO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenantinterphone.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import jakarta.validation.constraints.*; + +@Schema(description = "管理后台 - 租户对讲平台对应新增/修改 Request VO") +@Data +public class TenantInterphoneSaveReqVO { + + @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "8781") + private Long id; + + @Schema(description = "租户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "18871") + @NotNull(message = "租户id不能为空") + private Long tendId; + + @Schema(description = "对讲平台单位id", requiredMode = Schema.RequiredMode.REQUIRED, example = "16985") + @NotNull(message = "对讲平台单位id不能为空") + private Long corgId; + + @Schema(description = "对讲平台单位名称", example = "张三") + private String corgName; + + @Schema(description = "对讲平台用户数量", example = "11267") + private Long userCount; + + @Schema(description = "对讲平台群组数量", example = "27649") + private Long groupCount; + + @Schema(description = "对讲平台账号", example = "张三") + private String loginname; + + @Schema(description = "对讲平台密码") + private String password; + + @Schema(description = "对讲平台联系人") + private String contact; + + @Schema(description = "对讲平台单位是否绑定平台") + private Integer bindingPlatform; + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenantinterphone/TenantInterphoneDO.java b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenantinterphone/TenantInterphoneDO.java new file mode 100644 index 0000000..d78c1a5 --- /dev/null +++ b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenantinterphone/TenantInterphoneDO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.tenantinterphone; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 租户对讲平台对应 DO + * + * @author 超级管理员 + */ +@TableName("system_tenant_interphone") +@KeySequence("system_tenant_interphone_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TenantInterphoneDO extends BaseDO { + + /** + * id + */ + @TableId + private Long id; + /** + * 租户id + */ + private Long tendId; + /** + * 对讲平台单位id + */ + private Long corgId; + /** + * 对讲平台单位名称 + */ + private String corgName; + /** + * 对讲平台用户数量 + */ + private Long userCount; + /** + * 对讲平台群组数量 + */ + private Long groupCount; + /** + * 对讲平台账号 + */ + private String loginname; + /** + * 对讲平台密码 + */ + private String password; + /** + * 对讲平台联系人 + */ + private String contact; + /** + * 对讲平台单位是否绑定平台 + */ + private Integer bindingPlatform; + + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenantinterphone/TenantInterphoneMapper.java b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenantinterphone/TenantInterphoneMapper.java new file mode 100644 index 0000000..6a61328 --- /dev/null +++ b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenantinterphone/TenantInterphoneMapper.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.dal.mysql.tenantinterphone; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.tenantinterphone.TenantInterphoneDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.system.controller.admin.tenantinterphone.vo.*; + +/** + * 租户对讲平台对应 Mapper + * + * @author 超级管理员 + */ +@Mapper +public interface TenantInterphoneMapper extends BaseMapperX { + + default PageResult selectPage(TenantInterphonePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(TenantInterphoneDO::getTendId, reqVO.getTendId()) + .betweenIfPresent(TenantInterphoneDO::getCreateTime, reqVO.getCreateTime()) + .eqIfPresent(TenantInterphoneDO::getCorgId, reqVO.getCorgId()) + .likeIfPresent(TenantInterphoneDO::getCorgName, reqVO.getCorgName()) + .eqIfPresent(TenantInterphoneDO::getUserCount, reqVO.getUserCount()) + .eqIfPresent(TenantInterphoneDO::getGroupCount, reqVO.getGroupCount()) + .likeIfPresent(TenantInterphoneDO::getLoginname, reqVO.getLoginname()) + .eqIfPresent(TenantInterphoneDO::getContact, reqVO.getContact()) + .eqIfPresent(TenantInterphoneDO::getBindingPlatform, reqVO.getBindingPlatform()) + .orderByDesc(TenantInterphoneDO::getId)); + } + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java index c89a5dc..8976a67 100644 --- a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -168,4 +168,6 @@ public interface ErrorCodeConstants { // ========== 站内信发送 1-002-028-000 ========== ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失"); + ErrorCode TENANT_INTERPHONE_NOT_EXISTS = new ErrorCode(1_002_028_001, "租户对讲平台错误"); + } diff --git a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenantinterphone/TenantInterphoneService.java b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenantinterphone/TenantInterphoneService.java new file mode 100644 index 0000000..abf97c9 --- /dev/null +++ b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenantinterphone/TenantInterphoneService.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.system.service.tenantinterphone; + +import java.util.*; +import jakarta.validation.*; +import cn.iocoder.yudao.module.system.controller.admin.tenantinterphone.vo.*; +import cn.iocoder.yudao.module.system.dal.dataobject.tenantinterphone.TenantInterphoneDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 租户对讲平台对应 Service 接口 + * + * @author 超级管理员 + */ +public interface TenantInterphoneService { + + /** + * 创建租户对讲平台对应 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createTenantInterphone(@Valid TenantInterphoneSaveReqVO createReqVO); + + /** + * 更新租户对讲平台对应 + * + * @param updateReqVO 更新信息 + */ + void updateTenantInterphone(@Valid TenantInterphoneSaveReqVO updateReqVO); + + /** + * 删除租户对讲平台对应 + * + * @param id 编号 + */ + void deleteTenantInterphone(Long id); + + /** + * 批量删除租户对讲平台对应 + * + * @param ids 编号 + */ + void deleteTenantInterphoneListByIds(List ids); + + /** + * 获得租户对讲平台对应 + * + * @param id 编号 + * @return 租户对讲平台对应 + */ + TenantInterphoneDO getTenantInterphone(Long id); + + /** + * 获得租户对讲平台对应分页 + * + * @param pageReqVO 分页查询 + * @return 租户对讲平台对应分页 + */ + PageResult getTenantInterphonePage(TenantInterphonePageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenantinterphone/TenantInterphoneServiceImpl.java b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenantinterphone/TenantInterphoneServiceImpl.java new file mode 100644 index 0000000..e2b0861 --- /dev/null +++ b/cc-admin-master/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenantinterphone/TenantInterphoneServiceImpl.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.module.system.service.tenantinterphone; + +import cn.hutool.core.collection.CollUtil; +import org.springframework.stereotype.Service; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.system.controller.admin.tenantinterphone.vo.*; +import cn.iocoder.yudao.module.system.dal.dataobject.tenantinterphone.TenantInterphoneDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.system.dal.mysql.tenantinterphone.TenantInterphoneMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 租户对讲平台对应 Service 实现类 + * + * @author 超级管理员 + */ +@Service +@Validated +public class TenantInterphoneServiceImpl implements TenantInterphoneService { + + @Resource + private TenantInterphoneMapper tenantInterphoneMapper; + + @Override + public Long createTenantInterphone(TenantInterphoneSaveReqVO createReqVO) { + // 插入 + TenantInterphoneDO tenantInterphone = BeanUtils.toBean(createReqVO, TenantInterphoneDO.class); + tenantInterphoneMapper.insert(tenantInterphone); + + // 返回 + return tenantInterphone.getId(); + } + + @Override + public void updateTenantInterphone(TenantInterphoneSaveReqVO updateReqVO) { + // 校验存在 + validateTenantInterphoneExists(updateReqVO.getId()); + // 更新 + TenantInterphoneDO updateObj = BeanUtils.toBean(updateReqVO, TenantInterphoneDO.class); + tenantInterphoneMapper.updateById(updateObj); + } + + @Override + public void deleteTenantInterphone(Long id) { + // 校验存在 + validateTenantInterphoneExists(id); + // 删除 + tenantInterphoneMapper.deleteById(id); + } + + @Override + public void deleteTenantInterphoneListByIds(List ids) { + // 删除 + tenantInterphoneMapper.deleteByIds(ids); + } + + + private void validateTenantInterphoneExists(Long id) { + if (tenantInterphoneMapper.selectById(id) == null) { + throw exception(TENANT_INTERPHONE_NOT_EXISTS); + } + } + + @Override + public TenantInterphoneDO getTenantInterphone(Long id) { + return tenantInterphoneMapper.selectById(id); + } + + @Override + public PageResult getTenantInterphonePage(TenantInterphonePageReqVO pageReqVO) { + return tenantInterphoneMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/cc-admin-master/yudao-module-system/src/main/resources/mapper/tenantinterphone/TenantInterphoneMapper.xml b/cc-admin-master/yudao-module-system/src/main/resources/mapper/tenantinterphone/TenantInterphoneMapper.xml new file mode 100644 index 0000000..493955c --- /dev/null +++ b/cc-admin-master/yudao-module-system/src/main/resources/mapper/tenantinterphone/TenantInterphoneMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/cc-admin-master/yudao-server/pom.xml b/cc-admin-master/yudao-server/pom.xml index 37302f3..2defb97 100644 --- a/cc-admin-master/yudao-server/pom.xml +++ b/cc-admin-master/yudao-server/pom.xml @@ -56,6 +56,13 @@ yudao-module-hand-mqtt ${revision} + + + cn.iocoder.boot + yudao-module-interphone + ${revision} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 搜索 + 重置 + + 新增 + + + 导出 + + + 批量删除 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file