ApplicationCredentialsServiceImpl.java 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. package com.ymwk.supervisionserver.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  4. import com.ymwk.supervisionserver.component.PageResult;
  5. import com.ymwk.supervisionserver.component.ResponseDTO;
  6. import com.ymwk.supervisionserver.component.SmartPageUtil;
  7. import com.ymwk.supervisionserver.entity.ApplicationCredentials;
  8. import com.ymwk.supervisionserver.mapper.ApplicationCredentialsMapper;
  9. import com.ymwk.supervisionserver.model.CredentialsAddRequest;
  10. import com.ymwk.supervisionserver.model.CredentialsListRequest;
  11. import com.ymwk.supervisionserver.model.CredentialsUpdateRequest;
  12. import com.ymwk.supervisionserver.service.ApplicationCredentialsService;
  13. import lombok.extern.slf4j.Slf4j;
  14. import org.springframework.dao.DuplicateKeyException;
  15. import org.springframework.stereotype.Service;
  16. import org.springframework.transaction.annotation.Transactional;
  17. import org.springframework.util.StringUtils;
  18. import java.util.List;
  19. import java.util.UUID;
  20. @Service
  21. @Slf4j
  22. public class ApplicationCredentialsServiceImpl implements ApplicationCredentialsService {
  23. private static final String DEFAULT_OPERATOR = "admin";
  24. private final ApplicationCredentialsMapper credentialsMapper;
  25. public ApplicationCredentialsServiceImpl(ApplicationCredentialsMapper credentialsMapper) {
  26. this.credentialsMapper = credentialsMapper;
  27. }
  28. @Override
  29. public ResponseDTO<PageResult<ApplicationCredentials>> list(CredentialsListRequest request) {
  30. LambdaQueryWrapper<ApplicationCredentials> wrapper = new LambdaQueryWrapper<>();
  31. wrapper.like(StringUtils.hasText(request.getName()), ApplicationCredentials::getName, request.getName());
  32. wrapper.like(StringUtils.hasText(request.getCredIden()), ApplicationCredentials::getCredIden, request.getCredIden());
  33. wrapper.eq(request.getStatus() != null, ApplicationCredentials::getStatus, request.getStatus());
  34. wrapper.orderByDesc(ApplicationCredentials::getId);
  35. Page<ApplicationCredentials> page = new Page<>(request.getPageNum(), request.getPageSize());
  36. Page<ApplicationCredentials> resultPage = credentialsMapper.selectPage(page, wrapper);
  37. // api_secret 脱敏:保留前4后4,中间以 * 代替
  38. List<ApplicationCredentials> records = resultPage.getRecords();
  39. records.forEach(record -> record.setApiSecret(maskSecret(record.getApiSecret())));
  40. PageResult<ApplicationCredentials> pageResult = SmartPageUtil.convert2PageResult(resultPage, records);
  41. return ResponseDTO.ok(pageResult);
  42. }
  43. @Override
  44. @Transactional(rollbackFor = Exception.class)
  45. public ResponseDTO<ApplicationCredentials> create(CredentialsAddRequest request) {
  46. String name = request.getName().trim();
  47. // name 有唯一索引,先查重给出友好提示
  48. Long dupCount = credentialsMapper.selectCount(
  49. new LambdaQueryWrapper<ApplicationCredentials>().eq(ApplicationCredentials::getName, name));
  50. if (dupCount != null && dupCount > 0) {
  51. return ResponseDTO.error(100001, "应用名称已存在:" + name);
  52. }
  53. ApplicationCredentials entity = new ApplicationCredentials();
  54. entity.setCredIden(uuid32());
  55. entity.setName(name);
  56. entity.setApiKey(uuid32());
  57. entity.setApiSecret(uuid32());
  58. entity.setHmacAlgo(ApplicationCredentials.HMAC_ALGO);
  59. entity.setStatus(ApplicationCredentials.STATUS_ENABLED);
  60. entity.setDescription(request.getDescription());
  61. entity.setCreatedBy(DEFAULT_OPERATOR);
  62. entity.setUpdatedBy(DEFAULT_OPERATOR);
  63. try {
  64. credentialsMapper.insert(entity);
  65. } catch (DuplicateKeyException e) {
  66. // 并发下撞唯一索引(credIden/apiKey 理论上不可能重复,name 则已在前面查过)
  67. log.warn("创建应用凭证撞唯一索引: name={}", name);
  68. return ResponseDTO.error(100001, "应用名称已存在:" + name);
  69. }
  70. log.info("应用凭证创建成功: id={}, name={}, credIden={}", entity.getId(), entity.getName(), entity.getCredIden());
  71. // 返回内容中秘密同样脱敏,需要明文走专用查询接口
  72. entity.setApiSecret(maskSecret(entity.getApiSecret()));
  73. return ResponseDTO.ok(entity);
  74. }
  75. @Override
  76. public ResponseDTO<Void> delete(Long id) {
  77. if (id == null) {
  78. return ResponseDTO.error(100001, "凭证ID不能为空");
  79. }
  80. ApplicationCredentials entity = credentialsMapper.selectById(id);
  81. if (entity == null) {
  82. return ResponseDTO.error(100001, "凭证不存在");
  83. }
  84. credentialsMapper.deleteById(id);
  85. log.info("应用凭证删除成功: id={}, name={}", id, entity.getName());
  86. return ResponseDTO.okMsg("删除成功");
  87. }
  88. @Override
  89. @Transactional(rollbackFor = Exception.class)
  90. public ResponseDTO<ApplicationCredentials> update(CredentialsUpdateRequest request) {
  91. if (request == null || request.getId() == null) {
  92. return ResponseDTO.error(100001, "凭证ID不能为空");
  93. }
  94. ApplicationCredentials entity = credentialsMapper.selectById(request.getId());
  95. if (entity == null) {
  96. return ResponseDTO.error(100001, "凭证不存在");
  97. }
  98. String name = request.getName().trim();
  99. // name 有唯一索引,查重时排除自己
  100. Long dupCount = credentialsMapper.selectCount(
  101. new LambdaQueryWrapper<ApplicationCredentials>()
  102. .eq(ApplicationCredentials::getName, name)
  103. .ne(ApplicationCredentials::getId, request.getId()));
  104. if (dupCount != null && dupCount > 0) {
  105. return ResponseDTO.error(100001, "应用名称已存在:" + name);
  106. }
  107. entity.setName(name);
  108. entity.setDescription(request.getDescription());
  109. entity.setUpdatedBy(DEFAULT_OPERATOR);
  110. try {
  111. credentialsMapper.updateById(entity);
  112. } catch (DuplicateKeyException e) {
  113. log.warn("修改应用凭证撞唯一索引: id={}, name={}", request.getId(), name);
  114. return ResponseDTO.error(100001, "应用名称已存在:" + name);
  115. }
  116. log.info("应用凭证修改成功: id={}, name={}", entity.getId(), name);
  117. entity.setApiSecret(maskSecret(entity.getApiSecret()));
  118. return ResponseDTO.ok(entity);
  119. }
  120. @Override
  121. @Transactional(rollbackFor = Exception.class)
  122. public ResponseDTO<ApplicationCredentials> resetSecret(Long id) {
  123. if (id == null) {
  124. return ResponseDTO.error(100001, "凭证ID不能为空");
  125. }
  126. ApplicationCredentials entity = credentialsMapper.selectById(id);
  127. if (entity == null) {
  128. return ResponseDTO.error(100001, "凭证不存在");
  129. }
  130. entity.setApiSecret(uuid32());
  131. entity.setUpdatedBy(DEFAULT_OPERATOR);
  132. credentialsMapper.updateById(entity);
  133. log.info("应用凭证密钥已重新生成: id={}, name={}", id, entity.getName());
  134. entity.setApiSecret(maskSecret(entity.getApiSecret()));
  135. return ResponseDTO.ok(entity, "api_secret 已重新生成,请通过专用查询接口获取明文");
  136. }
  137. @Override
  138. public ResponseDTO<String> getSecret(Long id) {
  139. if (id == null) {
  140. return ResponseDTO.error(100001, "凭证ID不能为空");
  141. }
  142. ApplicationCredentials entity = credentialsMapper.selectById(id);
  143. if (entity == null) {
  144. return ResponseDTO.error(100001, "凭证不存在");
  145. }
  146. return ResponseDTO.ok(entity.getApiSecret());
  147. }
  148. @Override
  149. @Transactional(rollbackFor = Exception.class)
  150. public ResponseDTO<ApplicationCredentials> changeStatus(Long id, Integer status) {
  151. if (id == null) {
  152. return ResponseDTO.error(100001, "凭证ID不能为空");
  153. }
  154. if (!ApplicationCredentials.STATUS_ENABLED.equals(status)
  155. && !ApplicationCredentials.STATUS_DISABLED.equals(status)) {
  156. return ResponseDTO.error(100001, "状态值只支持 1(启用)或 2(禁用)");
  157. }
  158. ApplicationCredentials entity = credentialsMapper.selectById(id);
  159. if (entity == null) {
  160. return ResponseDTO.error(100001, "凭证不存在");
  161. }
  162. if (!status.equals(entity.getStatus())) {
  163. entity.setStatus(status);
  164. entity.setUpdatedBy(DEFAULT_OPERATOR);
  165. credentialsMapper.updateById(entity);
  166. }
  167. entity.setApiSecret(maskSecret(entity.getApiSecret()));
  168. return ResponseDTO.ok(entity);
  169. }
  170. /**
  171. * api_secret 脱敏:保留前4后4,中间替换为 *
  172. */
  173. private String maskSecret(String secret) {
  174. if (secret == null) {
  175. return null;
  176. }
  177. int length = secret.length();
  178. if (length <= 8) {
  179. return "*".repeat(length);
  180. }
  181. return secret.substring(0, 4)
  182. + "*".repeat(length - 8)
  183. + secret.substring(length - 4);
  184. }
  185. /**
  186. * 生成32位无连字符大写UUID
  187. */
  188. private String uuid32() {
  189. return UUID.randomUUID().toString().replace("-", "").toUpperCase();
  190. }
  191. }