diff --git a/admin/pom.xml b/admin/pom.xml
index fdb215c..c77f4c9 100644
--- a/admin/pom.xml
+++ b/admin/pom.xml
@@ -35,11 +35,6 @@
-
- redis.clients
- jedis
- ${jedis.version}
-
org.elasticsearch.client
elasticsearch-rest-client
@@ -50,6 +45,23 @@
jakarta.json-api
${jakartajson.version}
+
+ commons-io
+ commons-io
+ 2.19.0
+
+
+ org.apache.pdfbox
+ pdfbox
+ 3.0.5
+
+
+
+ com.itextpdf
+ itext7-core
+ 9.2.0
+ pom
+
diff --git a/admin/src/main/java/com/jinrui/reference/admin/.gitignore b/admin/src/main/java/com/jinrui/reference/admin/.gitignore
new file mode 100644
index 0000000..d69442e
--- /dev/null
+++ b/admin/src/main/java/com/jinrui/reference/admin/.gitignore
@@ -0,0 +1,2 @@
+/PDFTextSearcher.java
+/PDFTextSearcherItext.java
diff --git a/admin/src/main/java/com/jinrui/reference/admin/AdminApplication.java b/admin/src/main/java/com/jinrui/reference/admin/AdminApplication.java
index 5ae9552..4a1b77c 100644
--- a/admin/src/main/java/com/jinrui/reference/admin/AdminApplication.java
+++ b/admin/src/main/java/com/jinrui/reference/admin/AdminApplication.java
@@ -1,14 +1,10 @@
package com.jinrui.reference.admin;
import org.mybatis.spring.annotation.MapperScan;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.annotation.Bean;
-import org.springframework.util.StringUtils;
-
-import redis.clients.jedis.JedisPool;
-import redis.clients.jedis.JedisPoolConfig;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication(scanBasePackages = {
"com.jinrui.reference.admin",
@@ -17,21 +13,11 @@ import redis.clients.jedis.JedisPoolConfig;
@MapperScan({
"com.jinrui.reference.admin.mapper",
"com.jinrui.reference.core.mapper"})
+@EnableScheduling
+@EnableAspectJAutoProxy
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
-
- @Bean
- public JedisPool jedisPool(@Value("${redis.host}") String host,
- @Value("${redis.port}") int port,
- @Value("${redis.timeout:1000}") int timeout,
- @Value("${redis.password:}") String password) {
- if (StringUtils.hasText(password)) {
- JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
- return new JedisPool(jedisPoolConfig, host, port, timeout, password);
- }
- return new JedisPool(host, port);
- }
}
diff --git a/admin/src/main/java/com/jinrui/reference/admin/ApiClient.java b/admin/src/main/java/com/jinrui/reference/admin/ApiClient.java
new file mode 100644
index 0000000..6c5219c
--- /dev/null
+++ b/admin/src/main/java/com/jinrui/reference/admin/ApiClient.java
@@ -0,0 +1,68 @@
+package com.jinrui.reference.admin;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.io.IOUtils;
+
+public class ApiClient {
+ public final static String ak = "6a91a5070b044eb0b798f9420ccae15d";
+ public final static String sk = "clkky/Ad6d4DXDZ0UzemOKUKroLwSmc5vlIb2Sjh6YM=";
+ public final static String baseUrl = "http://127.0.0.1:13579";
+
+ public ApiClient() {
+ }
+
+ public static void main(String[] args) throws Exception {
+ String path = "/admin/api/news";
+ Map params = new HashMap<>();
+ params.put("num", String.valueOf(20));
+ String result = callApi(path, params);
+ System.out.println("+++++++++++++++++++++++++++++++++++++: " + result);
+ }
+
+ public static String callApi(String path, Map params) throws Exception {
+ String timestamp = String.valueOf(System.currentTimeMillis());
+ String nonce = UUID.randomUUID().toString();
+ String sortedParams = sortParams(params);
+ String url = baseUrl + path + "?" + sortedParams;
+
+ // 1. 计算签名
+ String data = "GET" + path + sortedParams + timestamp + nonce;
+ String signature = calculateSignature(data, sk);
+
+ // 2. 设置请求头
+ HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
+ conn.setRequestMethod("GET");
+ conn.setRequestProperty("X-Api-Key", ak);
+ conn.setRequestProperty("X-Timestamp", timestamp);
+ conn.setRequestProperty("X-Nonce", nonce);
+ conn.setRequestProperty("X-Signature", signature);
+
+ // 3. 发送请求
+ return IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8);
+ }
+
+ public static String calculateSignature(String data, String sk) throws Exception {
+ Mac sha256 = Mac.getInstance("HmacSHA256");
+ sha256.init(new SecretKeySpec(sk.getBytes(), "HmacSHA256"));
+ byte[] signBytes = sha256.doFinal(data.getBytes(StandardCharsets.UTF_8));
+ return Hex.encodeHexString(signBytes);
+ }
+
+ public static String sortParams(Map params) {
+ return params.entrySet().stream()
+ .sorted(Map.Entry.comparingByKey())
+ .map(e -> e.getKey() + "=" + e.getValue())
+ .collect(Collectors.joining("&"));
+ }
+}
diff --git a/admin/src/main/java/com/jinrui/reference/admin/annotation/OperationInfo.java b/admin/src/main/java/com/jinrui/reference/admin/annotation/OperationInfo.java
new file mode 100644
index 0000000..2e479c3
--- /dev/null
+++ b/admin/src/main/java/com/jinrui/reference/admin/annotation/OperationInfo.java
@@ -0,0 +1,18 @@
+package com.jinrui.reference.admin.annotation;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Documented
+@Retention(RUNTIME)
+@Target({ METHOD, ANNOTATION_TYPE })
+public @interface OperationInfo {
+ String type() default "";
+
+ String behavior();
+}
diff --git a/admin/src/main/java/com/jinrui/reference/admin/aspect/UserOperationLogAspect.java b/admin/src/main/java/com/jinrui/reference/admin/aspect/UserOperationLogAspect.java
new file mode 100644
index 0000000..f33fd90
--- /dev/null
+++ b/admin/src/main/java/com/jinrui/reference/admin/aspect/UserOperationLogAspect.java
@@ -0,0 +1,66 @@
+package com.jinrui.reference.admin.aspect;
+
+import java.lang.reflect.Method;
+
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.jinrui.reference.admin.annotation.OperationInfo;
+import com.jinrui.reference.admin.model.dto.news.PublishNewsDTO;
+import com.jinrui.reference.admin.service.UserOperationLogService;
+import com.jinrui.reference.core.model.vo.ResultObject;
+
+@Component
+@Aspect
+public class UserOperationLogAspect {
+ private static final Logger LOGGER = LoggerFactory.getLogger(UserOperationLogAspect.class);
+
+ @Autowired
+ private UserOperationLogService userOperationLogService;
+
+ @Pointcut("@annotation(com.jinrui.reference.admin.annotation.OperationInfo)")
+ public void userOperation() {}
+
+ @AfterThrowing(pointcut = "userOperation()", throwing = "e")
+ public void handleThrowing(JoinPoint point, Exception e) {
+
+ }
+
+ @Around("userOperation()")
+ public Object handleSuccess(ProceedingJoinPoint point) throws Throwable {
+ Object result = point.proceed();
+ OperationInfo operationInfo = getOperationInfo(point);
+ String behavior = operationInfo.behavior();
+ String type = operationInfo.type();
+ Object[] args = point.getArgs();
+
+ if (result instanceof ResultObject) {
+ int code = ((ResultObject)result).getCode();
+ if (code != 200) {
+ return result;
+ }
+ }
+ if ("news".equals(type)) {
+ userOperationLogService.logUserOperation(type, behavior, (String)args[0], ((PublishNewsDTO)args[1]).getId());
+ }
+
+
+ return result;
+ }
+
+ private OperationInfo getOperationInfo(JoinPoint point) {
+ MethodSignature ms = (MethodSignature)point.getSignature();
+ Method method = ms.getMethod();
+ return method.getAnnotation(OperationInfo.class);
+ }
+
+}
diff --git a/admin/src/main/java/com/jinrui/reference/admin/controller/ApiController.java b/admin/src/main/java/com/jinrui/reference/admin/controller/ApiController.java
new file mode 100644
index 0000000..8f44510
--- /dev/null
+++ b/admin/src/main/java/com/jinrui/reference/admin/controller/ApiController.java
@@ -0,0 +1,80 @@
+package com.jinrui.reference.admin.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.jinrui.reference.admin.model.entity.AdminUser;
+import com.jinrui.reference.admin.service.AdminJwtService;
+import com.jinrui.reference.core.model.vo.ResultObject;
+import com.jinrui.reference.core.model.vo.news.NewsApiVO;
+import com.jinrui.reference.core.service.ApiKeyService;
+import com.jinrui.reference.core.service.NewsService;
+
+@RestController
+@RequestMapping("/api")
+public class ApiController {
+ private static final Logger log = LoggerFactory.getLogger(ApiController.class);
+
+ private final NewsService newsService;
+ private final ApiKeyService apiKeyService;
+
+ public ApiController(NewsService newsService, ApiKeyService apiKeyService) {
+ this.newsService = newsService;
+ this.apiKeyService = apiKeyService;
+ }
+
+ @GetMapping("/news")
+ public ResultObject> getNews(@RequestParam(name = "num", required = true, defaultValue = "10") Integer num, @RequestParam(name = "last", required = false) Long last) {
+ return newsService.requestNewsByApi(num, last);
+ }
+
+ @PostMapping("/news")
+ public ResultObject> queryNews(@RequestParam(name = "num", required = true, defaultValue = "10") Integer num, @RequestParam(name = "last", required = false) Long last) {
+ return newsService.requestNewsByApi(num, last);
+ }
+
+ @PostMapping("/key")
+ public ResultObject