Commit 5530ed7f authored by zhouwei's avatar zhouwei

Merge branch 'dev' into 'master'

Dev

See merge request !1
parents 213a7ab7 7806733c
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.1
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
FROM openjdk:8
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENV SPRING_PROFILES_ACTIVE=prd
ENTRYPOINT ["java","-jar","/app.jar"]
\ No newline at end of file
FROM openjdk:8
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENV SPRING_PROFILES_ACTIVE=qa
ENTRYPOINT ["java","-jar","/app.jar"]
\ No newline at end of file
apiVersion: apps/v1
kind: Deployment
metadata:
name: securitylink-ai
namespace: securitylink
spec:
replicas: 1
selector:
matchLabels:
app: securitylink-ai
template:
metadata:
labels:
app: securitylink-ai
spec:
containers:
- name: securitylink-ai
image: 381492067916.dkr.ecr.ap-east-1.amazonaws.com/securitylink-ai:latest
resources:
limits:
memory: "2Gi"
cpu: "1000m"
requests:
memory: "500Mi"
cpu: "500m"
ports:
- name: httpport
containerPort: 8080
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 90
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 5
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
\ No newline at end of file
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.1
@REM
@REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution
@REM MVNW_USERNAME/MVNW_PASSWORD - jwtUser and password for downloading maven
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
@SET __MVNW_CMD__=
@SET __MVNW_ERROR__=
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
@SET PSModulePath=
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
@SET __MVNW_PSMODULEP_SAVE=
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
$ErrorActionPreference = "Stop"
if ($env:MVNW_VERBOSE -eq "true") {
$VerbosePreference = "Continue"
}
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
if (!$distributionUrl) {
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
}
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
"maven-mvnd-*" {
$USE_MVND = $true
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
$MVN_CMD = "mvnd.cmd"
break
}
default {
$USE_MVND = $false
$MVN_CMD = $script -replace '^mvnw','mvn'
break
}
}
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
if ($env:MVNW_REPOURL) {
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
exit $?
}
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}
# prepare tmp dir
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
trap {
if ($TMP_DOWNLOAD_DIR.Exists) {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
}
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
# Download and Install Apache Maven
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
Write-Verbose "Downloading from: $distributionUrl"
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
$webclient = New-Object System.Net.WebClient
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
# If specified, validate the SHA-256 sum of the Maven distribution zip file
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
if ($distributionSha256Sum) {
if ($USE_MVND) {
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
}
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
}
}
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
Write-Error "fail to move MAVEN_HOME"
}
} finally {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
This diff is collapsed.
---
apiVersion: v1
kind: Service
metadata:
namespace: securitylink
name: securitylink-ai
spec:
ports:
- port: 8080
targetPort: 8080
protocol: TCP
type: NodePort
selector:
app: securitylink-ai
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: securitylink
name: ingress-securitylink-ai
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
spec:
ingressClassName: alb
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: securitylink-ai
port:
number: 8080
\ No newline at end of file
package com.nanyan.securitylink;
import java.security.SecureRandom;
public class RandomHS256KeyGenerator {
public static String generateRandomKey() {
SecureRandom secureRandom = new SecureRandom();
byte[] keyBytes = new byte[32];
secureRandom.nextBytes(keyBytes);
return bytesToHex(keyBytes);
}
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static void main(String[] args) {
String randomKey = generateRandomKey();
System.out.println("生成的随机 HS256 密钥: " + randomKey);
}
}
package com.nanyan.securitylink;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class SecurityLinkAIApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityLinkAIApplication.class, args);
}
}
package com.nanyan.securitylink.common;
public interface Constant {
// String USER_ID = "userId";
int picType = 1;
int textType = 0;
String ACCOUNT = "account";
int SUCCESS_STATUS = 200;
String WEATHER_GROUP = "weatherType";
String ADMIN_ROLE = "admin";
String MSG_LABEL_NAME = "MesTag";
int INVALID = 1;
int EFFECTIVE = 0;
int NOT_SENT = 0;
int SENT = 1;
String COUNTRY_CODE = "country";
String CRONTAB_CODE = "crontab";
String CITY_CODE = "city";
String UTC_ZONE_ID = "UTC";
String LAT = "lat";
String LON = "lon";
String LANGUAGE = "language";
String CN = "中文";
String CN_CODE = "zh_cn";
String EN = "英文";
String EN_CODE = "en";
String indexEmergencyCode="indexEmergency";
String indexCitySafeCode="indexCitySafe";
String indexFrendlinessCode="indexFrendliness";
String indexManmadeHazardsCode="indexManmadeHazards";
String indexNaturalHazardsCode="indexNaturalHazards";
String indexPublicHealthCode="indexPublicHealth";
Integer DISABLED = 1;
Integer DELETE = 3;
Integer NON_VIP = 1;
Integer VIP = 0;
}
package com.nanyan.securitylink.common;
public enum MsgCode {
SUCCESS(200, "success"),
FAILED(1001, "access failed"),
NOT_LOGIN(401, "not login"),
LOGIN_FAILED(402, "login failed"),
LOGIN_EXPIRE(403, "token expire")
//翻译异常
,
TRANSLATE_ERROR(500, "translate error"),
//JSON字符串异常
JSON_ERROR(501, "json error")
;
int code;
String msg;
MsgCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
package com.nanyan.securitylink.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
@Slf4j
@RefreshScope
@Configuration
public class AppConfig {
@Value("${api.translate.key}")
String apiTranslateKey;
public String getApiTranslateKey() {
return apiTranslateKey;
}
}
package com.nanyan.securitylink.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DatasourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSource druidDataSource() {
return new DruidDataSource();
}
}
package com.nanyan.securitylink.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.nanyan.securitylink.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
package com.nanyan.securitylink.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
package com.nanyan.securitylink.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@Configuration
public class SchedulerConfig {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10); // 设置线程池大小
scheduler.setThreadNamePrefix("task-scheduler-"); // 设置线程名前缀
scheduler.initialize();
return scheduler;
}
}
package com.nanyan.securitylink.config;
import com.nanyan.securitylink.interceptor.HeaderInterceptor;
import com.nanyan.securitylink.interceptor.LogResponseTimeInterceptor;
import com.nanyan.securitylink.interceptor.LoginInterceptor;
import com.nanyan.securitylink.interceptor.UserAccessInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import javax.annotation.Resource;
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Resource
private LoginInterceptor loginInterceptor;
@Resource
LogResponseTimeInterceptor logResponseTimeInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
//
registry.addInterceptor(loginInterceptor);
registry.addInterceptor(logResponseTimeInterceptor);
super.addInterceptors(registry);
}
}
package com.nanyan.securitylink.controller;
import com.nanyan.securitylink.dto.TranslateDTO;
import com.nanyan.securitylink.service.AIService;
import com.nanyan.securitylink.vo.AIResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.nanyan.securitylink.vo.Response;
@RestController
public class AIController {
@Autowired
AIService aiService;
@PostMapping("/translate")
public Response<AIResponse> translate(@RequestBody TranslateDTO translateDTO) {
return Response.SUCCESS(aiService.translate(translateDTO));
}
@PostMapping("/tag")
public Response<AIResponse> newsTag(@RequestBody TranslateDTO translateDTO) {
return Response.SUCCESS(aiService.newsTags(translateDTO));
}
}
package com.nanyan.securitylink.dto;
import lombok.Data;
@Data
public class TranslateDTO {
TranslateInputDTO inputs;
String response_mode;
String user;
}
package com.nanyan.securitylink.dto;
import lombok.Data;
@Data
public class TranslateInputDTO {
String record_json;
String language;
}
package com.nanyan.securitylink.entity;
import lombok.Data;
import java.util.List;
@Data
public class ChatCompletionResponse {
private String id;
private String object;
private long created;
private String model;
private List<Choice> choices;
private Usage usage;
private String systemFingerprint;
}
package com.nanyan.securitylink.entity;
import lombok.Data;
import java.util.List;
@Data
public class ChatInputData {
private String model;
private Boolean stream = false;
private List<Message> messages;
}
package com.nanyan.securitylink.entity;
import lombok.Data;
@Data
public class Choice {
private int index;
private Message message;
private Object logprobs; // logprobs is null in the JSON
private String finishReason;
}
package com.nanyan.securitylink.entity;
import lombok.Data;
@Data
public class Message {
private String role;
private String content;
}
package com.nanyan.securitylink.entity;
import lombok.Data;
@Data
public class PromptTokensDetails {
private int cachedTokens;
}
package com.nanyan.securitylink.entity;
import lombok.Data;
@Data
public class Usage {
private int promptTokens;
private int completionTokens;
private int totalTokens;
private PromptTokensDetails promptTokensDetails;
private int promptCacheHitTokens;
private int promptCacheMissTokens;
}
package com.nanyan.securitylink.entity;
public class UserHeader {
String userId;
String accountName;
String role;
String token;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
package com.nanyan.securitylink.entity;
public class UserThreadLocal {
private static final ThreadLocal<UserHeader> userHeaderThreadLocal = new ThreadLocal<>();
public static void set(UserHeader userHeader){
userHeaderThreadLocal.set(userHeader);
}
public static UserHeader get(){
return userHeaderThreadLocal.get();
}
public static void remove(){
userHeaderThreadLocal.remove();
}
}
package com.nanyan.securitylink.execption;
import com.nanyan.securitylink.common.MsgCode;
public class BaseException extends RuntimeException {
private int code;
public BaseException(MsgCode msgCode) {
super(msgCode.getMsg());
this.code = msgCode.getCode();
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
package com.nanyan.securitylink.execption;
import com.nanyan.securitylink.common.MsgCode;
public class HeaderException extends BaseException {
public HeaderException(MsgCode msgCode) {
super(msgCode);
}
}
package com.nanyan.securitylink.interceptor;
import com.nanyan.securitylink.execption.BaseException;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import com.nanyan.securitylink.vo.Response;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BaseException.class)
public ResponseEntity<Response> businessException(BaseException e){
Response response = Response.getResponse(e.getCode(), e.getMessage());
return ResponseEntity.ok(response);
}
}
package com.nanyan.securitylink.interceptor;
import com.nanyan.securitylink.common.Constant;
import com.nanyan.securitylink.common.MsgCode;
import com.nanyan.securitylink.entity.UserHeader;
import com.nanyan.securitylink.entity.UserThreadLocal;
import com.nanyan.securitylink.execption.HeaderException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
@Component
public class HeaderInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try{
String accountName = getHeader(request, Constant.ACCOUNT);
UserHeader userHeader = new UserHeader();
userHeader.setAccountName(accountName);
UserThreadLocal.set(userHeader);
}catch (Exception e){
e.printStackTrace();
throw new HeaderException(MsgCode.LOGIN_FAILED);
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserThreadLocal.remove();
}
public String getHeader(HttpServletRequest request, String key){
String value = request.getHeader(key);
if(StringUtils.isEmpty(value)){
return "";
}
byte[] decodedBytes = Base64.getDecoder().decode(value);
value = new String(decodedBytes);
return value;
}
}
package com.nanyan.securitylink.interceptor;
import com.nanyan.securitylink.entity.UserHeader;
import com.nanyan.securitylink.entity.UserThreadLocal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class LogResponseTimeInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
Long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
// 打印接口耗时
// System.out.println("接口耗时:" + time + "ms");
String requestURI = request.getRequestURI();
UserHeader userHeader = UserThreadLocal.get();
String accountName = "";
if(userHeader != null){
accountName = UserThreadLocal.get().getAccountName();
logger.info("account:{} 接口{}耗时{} ms", accountName,requestURI, time);
}else {
logger.info("接口{}耗时{} ms",requestURI, time);
}
}
}
package com.nanyan.securitylink.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
String uri = request.getRequestURI();
String token = request.getHeader("token"); // 从请求头中获取 token
return true;
}
}
package com.nanyan.securitylink.interceptor;
import com.nanyan.securitylink.common.Constant;
import com.nanyan.securitylink.common.MsgCode;
import com.nanyan.securitylink.execption.BaseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
import java.util.regex.Pattern;
@Component
public class UserAccessInterceptor implements HandlerInterceptor {
String patternString = "^/user/.*";
Pattern userReosurceUrlPattern = Pattern.compile(patternString);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
}
package com.nanyan.securitylink.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nanyan.securitylink.model.WechatMsgDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface WechatMsgMapper extends BaseMapper<WechatMsgDO> {
}
package com.nanyan.securitylink.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@Data
@TableName("wechat_msg")
public class WechatMsgDO {
@TableId(type = IdType.AUTO)
Integer id;
String msg;
//状态 0:未发送 1:发送
Integer status;
Date createTime;
Date updateTime;
}
package com.nanyan.securitylink.schedule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Slf4j
@Component
public class RefreshConfigSync {
@Autowired
RestTemplate restTemplate;
public void autoRefreshConfig(){
try{
String url = "http://127.0.0.1:8080/api/v1/actuator/refresh";
HttpEntity header = getHeader();
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, header, String.class);
if(response.getStatusCode().is2xxSuccessful()){
log.info("配置刷新成功:{}", response.getBody());
}
}catch (Exception e){
log.error("", e);
}
}
private HttpEntity getHeader(){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Type","application/json");
return new HttpEntity<>(httpHeaders);
}
}
package com.nanyan.securitylink.schedule;
import com.nanyan.securitylink.config.AppConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service
@EnableScheduling
public class ScheduledTasksService {
private final TaskScheduler taskScheduler;
@Autowired
RefreshConfigSync refreshConfigSync;
public ScheduledTasksService(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
// @PostConstruct
public void scheduleTask() {
//定时刷新配置
taskScheduler.scheduleWithFixedDelay(() -> {
refreshConfigSync.autoRefreshConfig();
}, 60000);
}
}
package com.nanyan.securitylink.service;
import com.nanyan.securitylink.dto.TranslateDTO;
import com.nanyan.securitylink.vo.AIResponse;
public interface AIService {
AIResponse translate(TranslateDTO translateDTO);
AIResponse newsTags(TranslateDTO translateDTO);
}
package com.nanyan.securitylink.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nanyan.securitylink.common.MsgCode;
import com.nanyan.securitylink.config.AppConfig;
import com.nanyan.securitylink.dto.TranslateDTO;
import com.nanyan.securitylink.entity.ChatCompletionResponse;
import com.nanyan.securitylink.entity.ChatInputData;
import com.nanyan.securitylink.entity.Message;
import com.nanyan.securitylink.execption.BaseException;
import com.nanyan.securitylink.service.AIService;
import com.nanyan.securitylink.vo.AIResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
public class DeepSeekAIServiceImpl implements AIService {
@Autowired
AppConfig appConfig;
@Autowired
RestTemplate restTemplate;
private final static ObjectMapper objectMapper = new ObjectMapper();
// private final static String DEEP_SEEK_URL = "https://api.deepseek.com/chat/completions";
private final static String DEEP_SEEK_URL = "https://ark.cn-beijing.volces.com/api/v3/chat/completions";
@Override
public AIResponse translate(TranslateDTO translateDTO) {
ChatInputData chatInputData = new ChatInputData();
buildTranslate(chatInputData,translateDTO);
ChatCompletionResponse chatCompletionResponse = aiRequest(chatInputData, appConfig.getApiTranslateKey());
if(CollectionUtils.isNotEmpty(chatCompletionResponse.getChoices())){
AIResponse aiResponse = new AIResponse();
aiResponse.setAnswer(chatCompletionResponse.getChoices().get(0).getMessage().getContent());
return aiResponse;
}
return null;
}
public AIResponse newsTags(TranslateDTO translateDTO) {
ChatInputData chatInputData = new ChatInputData();
buildNewsTag(chatInputData,translateDTO);
ChatCompletionResponse chatCompletionResponse = aiRequest(chatInputData, appConfig.getApiTranslateKey());
if(CollectionUtils.isNotEmpty(chatCompletionResponse.getChoices())){
AIResponse aiResponse = new AIResponse();
aiResponse.setAnswer(chatCompletionResponse.getChoices().get(0).getMessage().getContent());
return aiResponse;
}
return null;
}
private void buildNewsTag(ChatInputData chatInputData, TranslateDTO translateDTO) {
chatInputData.setModel("deepseek-v3-250324");
chatInputData.setStream(false);
List<Message> messages = new ArrayList<>();
chatInputData.setMessages(messages);
Message systemRole = getSystemTagDeepSeekMessage();
messages.add(systemRole);
Message userRole = getUserTagDeepSeekMessage(translateDTO);
messages.add(userRole);
}
@NotNull
private ChatCompletionResponse aiRequest(ChatInputData chatInputData , String apiKey) {
String writeValueAsString = null;
try {
writeValueAsString = objectMapper.writeValueAsString(chatInputData);
} catch (JsonProcessingException e) {
throw new BaseException(MsgCode.JSON_ERROR);
}
log.info("translate json:{}", writeValueAsString);
HttpEntity header = getHeader(apiKey);
HttpEntity<String> entity = new HttpEntity<>(writeValueAsString,header.getHeaders());
ResponseEntity<ChatCompletionResponse> response = restTemplate.postForEntity(DEEP_SEEK_URL, entity, ChatCompletionResponse.class);
if(response.getStatusCode().is2xxSuccessful()){
log.info("translate response:{}", JSONObject.toJSONString(response.getBody()));
return response.getBody();
}
throw new BaseException(MsgCode.TRANSLATE_ERROR);
}
private void buildTranslate(ChatInputData chatInputData, TranslateDTO translateDTO) {
chatInputData.setModel("deepseek-v3-250324");
chatInputData.setStream(false);
List<Message> messages = new ArrayList<>();
chatInputData.setMessages(messages);
Message systemRole = getDeepSeekMessage(translateDTO);
messages.add(systemRole);
}
@NotNull
private static Message getDeepSeekMessage(TranslateDTO translateDTO) {
Message systemRole = new Message();
systemRole.setRole("user");
systemRole.setContent("需要翻译的JSON:"+ translateDTO.getInputs().getRecord_json() + "\n" +
"\n" +
"你是一个 JSON 翻译员,能够将给定的 JSON 中的 value 值部分翻译成 `"+ translateDTO.getInputs().getLanguage() +"`。\n" +
"\n" +
"## 限制\n" +
"- 严格按照要求进行翻译,不改变 JSON 的结构和无意义的 key 的 value。\n" +
"- 仅翻译有意义的 value 部分,确保翻译准确、通顺。"+
"- 无需任何其他说明。"+
"- 数字、特殊符号(@#%&等)连接的单词无需翻译,其他还需翻译。"
);
return systemRole;
}
private static Message getUserTagDeepSeekMessage(TranslateDTO translateDTO) {
Message userRole = new Message();
userRole.setRole("user");
userRole.setContent(translateDTO.getInputs().getRecord_json());
return userRole;
}
@NotNull
private static Message getSystemTagDeepSeekMessage() {
Message systemRole = new Message();
systemRole.setRole("system");
systemRole.setContent("```xml\n" +
"<instruction>\n" +
"对多条新闻内容进行打标签,标签分别为枪击、抢劫、爆炸、恐怖袭击、纵火、大规模伤亡事件、人质事件、毒气泄漏、自然灾害、生物危害、交通意外、建筑物倒塌、食品安全、社会冲突、群体性事件等。请按照以下步骤完成任务:\n" +
"\n" +
"1. 仔细阅读每条新闻内容,理解其核心事件。\n" +
"2. 根据新闻描述的事件类型,从给定的标签列表中选择最匹配的一个或多个标签。\n" +
"3. 确保标签与新闻内容高度相关,避免主观臆断。\n" +
"4. 如果新闻内容涉及多个事件类型,可以分配多个标签,但需确保每个标签都准确反映新闻内容。\n" +
"5. 输出结果时,仅列出标签名称,不要包含任何XML标签或其他无关内容。\n" +
"\n" +
"<examples>\n" +
"<example>\n" +
"<输入>\n" +
"\"昨日凌晨,某市一家银行发生持枪抢劫案,歹徒与警方交火后逃离现场,造成两名保安受伤。\"\n" +
"<输出>\n" +
"枪击, 抢劫\n" +
"</example>\n" +
"\n" +
"<example>\n" +
"<输入>\n" +
"\"今日上午,某化工厂发生毒气泄漏事故,附近居民已被紧急疏散,暂无人员伤亡报告。\"\n" +
"<输出>\n" +
"毒气泄漏\n" +
"</example>\n" +
"\n" +
"<example>\n" +
"<输入>\n" +
"\"近日,某地因征地问题引发大规模抗议活动,数百名民众与警方发生冲突,导致多人受伤。\"\n" +
"<输出>\n" +
"社会冲突, 群体性事件\n" +
"</example>\n" +
"</examples>\n" +
"\n" +
"注意事项:\n" +
"- 标签必须严格从给定的列表中选择,不得自行添加或修改标签名称。\n" +
"- 如果新闻内容与任何标签都不匹配,则输出\"\"。\n" +
"- 输出时标签之间用逗号分隔,不要使用编号或项目符号。\n" +
"- 确保标签的准确性和一致性,避免歧义或模糊分类。\n" +
"\n" +
"请严格按照上述要求完成任务,确保输出结果简洁、准确且符合规范。\n" +
"</instruction>\n" +
"```"
);
return systemRole;
}
private HttpEntity getHeader(String token){
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Type","application/json");
httpHeaders.set("Authorization",String.format("Bearer %s",token));
return new HttpEntity<>(httpHeaders);
}
}
This diff is collapsed.
package com.nanyan.securitylink.vo;
import lombok.Data;
@Data
public class AIResponse {
private String event;
private String task_id;
private String id;
private String message_id;
private String mode;
private String answer;
private long created_at;
}
package com.nanyan.securitylink.vo;
import com.nanyan.securitylink.common.MsgCode;
public class Response<T> {
String msg;
int code;
T data;
public static Response getResponse(MsgCode msgCode){
Response response = new Response();
response.setCode(msgCode.getCode());
response.setMsg(msgCode.getMsg());
return response;
}
public static Response getResponse(int code, String msg){
Response response = new Response();
response.setCode(code);
response.setMsg(msg);
return response;
}
public static<T> Response<T> SUCCESS(T data){
Response response = new Response<T>();
response.setCode(MsgCode.SUCCESS.getCode());
response.setMsg(MsgCode.SUCCESS.getMsg());
response.setData(data);
return response;
}
public static<T> Response<T> FAILED(T data){
Response response = new Response();
response.setCode(MsgCode.FAILED.getCode());
response.setMsg(MsgCode.FAILED.getMsg());
response.setData(data);
return response;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
spring.cloud.config.name=securitylink-ai
spring.cloud.config.profile=prd
spring.cloud.config.label=master
spring.cloud.config.enabled=true
#spring.config.import=optional:configserver:http://sl-config-center:8080
spring.config.import=optional:configserver:http://43.199.200.152:32594
\ No newline at end of file
spring.cloud.config.name=securitylink-ai
spring.cloud.config.profile=qa
spring.cloud.config.label=master
spring.cloud.config.enabled=true
#spring.config.import=optional:configserver:http://sl-config-center:8080
spring.config.import=optional:configserver:http://43.199.200.152:32594
#crime.mapping.event={"Arson":"\u7EB5\u706B\u7F6A","Assault":"\u88AD\u51FB/\u653B\u51FB","Burglary":"\u5165\u5BA4\u76D7\u7A83","Disturbing the Peace":"\u6270\u4E71\u516C\u5171\u79E9\u5E8F","Drugs / Alcohol Violations":"\u6BD2\u54C1/\u9152\u7CBE\u8FDD\u89C4","DUI":"\u9189\u9A7E","Fraud":"\u8BC8\u9A97","Homicide":"\u6740\u4EBA\u7F6A","Motor Vehicle Theft":"\u673A\u52A8\u8F66\u76D7\u7A83","Robbery":"\u62A2\u52AB","Sex Crimes":"\u6027\u72AF\u7F6A","Theft / Larceny":"\u76D7\u7A83","Vandalism":"\u6076\u610F\u7834\u574F\u8D22\u7269","Vehicle Break-In / Theft":"\u8F66\u8F86\u95EF\u5165/\u76D7\u7A83","Weapons":"\u6D89\u62A2\u6D89\u68B0\u72AF\u7F6A"}
\ No newline at end of file
spring.application.name=securityLink-ai
spring.profiles.active=qa
server.servlet.context-path=/api/v1
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
management.endpoints.web.exposure.include=refresh
logging.config=classpath:log4j2.xml
<Configuration status="WARN" monitorInterval="1800">
<!--变量配置-->
<Properties>
<!--应用名称-->
<property name="APP_NAME">securitylink-ai</property>
<!--日志存放路径-->
<property name="LOG_PATH">./logs/${APP_NAME}</property>
<!--日志备份路径-->
<property name="LOG_BACKUP_PATH">${LOG_PATH}/backup</property>
<!--日志输出格式-控制台-->
<property name="PATTERN_CONSOLE">%d{yyyy-MM-dd HH:mm:ss.SSS} | %blue{%traceId} | %highlight{%-5p} | %magenta{${sys:PID}} | %yellow{%t} | %cyan{%l} : %msg%n</property>
<!--日志输出格式-文件-->
<property name="PATTERN_FILE">%d{yyyy-MM-dd HH:mm:ss.SSS} | %traceId | %-5p | ${sys:PID} | %t | %l : %msg%n</property>
</Properties>
<!--定义日志输出目的地,内容和格式等-->
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${PATTERN_CONSOLE}"/>
</Console>
<!--可归档文件
1. fileName: 日志存储路径
2. filePattern: 历史日志封存路径。其中%d{yyyy-MM-dd}表示了日志的时间单位是天,log4j2自动识别zip等后缀,表示历史日志需要压缩
-->
<RollingFile name="RollingFile" fileName="${LOG_PATH}/${APP_NAME}.log" filePattern="${LOG_BACKUP_PATH}/$${date:yyyy-MM}/${APP_NAME}-%d{yyyy-MM-dd}_%i.log.zip">
<!--输出日志的格式, 不设置默认为:%m%n-->
<PatternLayout pattern="${PATTERN_FILE}"/>
<!--只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<!--归档设置-->
<Policies>
<!--按时间间隔归档:
1. interval=时间间隔, 单位由filePattern的%d日期格式指定, 此处配置代表每一天归档一次
2. modulate="true" 是否对interval取模,决定了下一次触发的时间点
-->
<TimeBasedTriggeringPolicy interval="1" modulate="true" />
<!-- 按照日志文件的大小: size表示当前日志文件的最大size,支持单位:KB/MB/GB-->
<SizeBasedTriggeringPolicy size="50MB"/>
</Policies>
<!-- 历史日志配置: 该属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
<DefaultRolloverStrategy max="30"/>
</RollingFile>
<!--错误信息单独归档-->
<RollingFile name="RollingFileError" fileName="${LOG_PATH}/${APP_NAME}-error.log" filePattern="${LOG_BACKUP_PATH}/$${date:yyyy-MM}/${APP_NAME}-error-%d{yyyy-MM-dd}_%i.log.zip">
<PatternLayout pattern="${PATTERN_FILE}"/>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="50MB"/>
</Policies>
</RollingFile>
</Appenders>
<!--Loggers配置-->
<Loggers>
<!--
注意点:
1. logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等:
(1). name: 用来指定该logger所适用的类或者类所在的包全路径,继承自Root节点.
(2). AppenderRef:关联的Appender, 只有定义了logger并引入的appender,appender才会生效
(3). additivity: logEvent的传递性。true LogEvent处理后传递给父Logger打印。false LogEvent处理后不再向上传递给父Logger(解决日志重复输出问题)
(4). logger配置的level必须高于或等于Appenders中ThresholdFilter配置的过滤level, 否则会造成信息丢失
2. root配置日志的根节点
-->
<!-- 同步日志配置-->
<logger name="com.sky.hello.mapper" level="debug" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="RollingFileError"/>
</logger>
<root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
<AppenderRef ref="RollingFileError"/>
</root>
</Loggers>
</Configuration>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment