7.31 线上部署 frp-本地docker
This commit is contained in:
parent
528422fed6
commit
b9501daf0a
1
.gitignore
vendored
1
.gitignore
vendored
@ -74,3 +74,4 @@ application-local.*
|
||||
# 如果你用到 IDEA 自带的 File-Based Storage(8+版本默认),可以添加:
|
||||
# .idea/.name
|
||||
# .idea/gradle.xml
|
||||
/src/main/java/picturesearch/
|
||||
|
14
.mvn/settings.xml
Normal file
14
.mvn/settings.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0
|
||||
https://maven.apache.org/xsd/settings-1.1.0.xsd">
|
||||
<mirrors>
|
||||
<mirror>
|
||||
<id>aliyun</id>
|
||||
<name>aliyun maven</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
<mirrorOf>central,apache.snapshots</mirrorOf>
|
||||
</mirror>
|
||||
</mirrors>
|
||||
</settings>
|
||||
|
26
Dockerfile
Normal file
26
Dockerfile
Normal file
@ -0,0 +1,26 @@
|
||||
# —— builder 阶段 ——
|
||||
FROM maven:3.8.7-eclipse-temurin-17-alpine AS builder
|
||||
WORKDIR /workspace
|
||||
|
||||
# ① 如没有私服,可删掉这行;否则保留
|
||||
COPY .mvn/settings.xml /root/.m2/settings.xml
|
||||
|
||||
# ② 先拷 POM 并预拉依赖,提升缓存命中率
|
||||
COPY pom.xml .
|
||||
RUN mvn -B dependency:go-offline
|
||||
|
||||
# ③ 再拷源码并真正打包,显式执行 spring-boot:repackage
|
||||
COPY src src
|
||||
RUN mvn -B clean package spring-boot:repackage -DskipTests
|
||||
|
||||
# —— runtime 阶段 ——
|
||||
FROM openjdk:17-jdk-slim
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
# 指向你在 pom.xml 里定义的 finalName
|
||||
ARG JAR_FILE=smile-picture-backend.jar
|
||||
COPY --from=builder /workspace/target/${JAR_FILE} /app.jar
|
||||
|
||||
EXPOSE 8096
|
||||
ENTRYPOINT ["java", "-jar", "/app.jar"]
|
80
docs/tags/picture-v1.0/docker-compose-app-v1.0.yml
Normal file
80
docs/tags/picture-v1.0/docker-compose-app-v1.0.yml
Normal file
@ -0,0 +1,80 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
### 1. 前端 -------------------------------------------------
|
||||
smile-picture-front:
|
||||
image: nginx:alpine
|
||||
container_name: smile-picture-front
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "18096:80"
|
||||
volumes:
|
||||
- ./nginx/html:/usr/share/nginx/html
|
||||
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
networks: [smile-picture-network]
|
||||
|
||||
### 2. MySQL ------------------------------------------------
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: picture-mysql
|
||||
hostname: mysql
|
||||
restart: unless-stopped
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
MYSQL_ROOT_PASSWORD: 123456
|
||||
ports:
|
||||
- "13307:3306"
|
||||
volumes:
|
||||
# 配置文件只读挂载 ✅
|
||||
- ./mysql/my.cnf:/etc/mysql/conf.d/mysql.cnf:ro
|
||||
# 初始化脚本只读也没问题 ✅
|
||||
- ./mysql/sql:/docker-entrypoint-initdb.d:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
start_period: 15s
|
||||
networks: [smile-picture-network]
|
||||
|
||||
### 3. Redis -----------------------------------------------
|
||||
redis:
|
||||
image: redis:6.2
|
||||
container_name: picture-redis
|
||||
hostname: redis
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "36379:6379"
|
||||
volumes:
|
||||
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
|
||||
command: redis-server /usr/local/etc/redis/redis.conf
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
networks: [smile-picture-network]
|
||||
|
||||
### 4. Java 后端 -------------------------------------------
|
||||
smile-picture-backend:
|
||||
build:
|
||||
context: ../../.. # 建议把 compose 放到项目根;这里就用 `.`
|
||||
dockerfile: Dockerfile
|
||||
image: smile/smile-picture-backend:latest
|
||||
container_name: smile-picture-backend
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
environment: # ← 推荐通过环境变量注入容器名而非 localhost
|
||||
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/smile-picture?useSSL=false&serverTimezone=Asia/Shanghai
|
||||
ports:
|
||||
- "8096:8096"
|
||||
networks: [smile-picture-network]
|
||||
|
||||
networks:
|
||||
smile-picture-network:
|
||||
driver: bridge
|
24
docs/tags/picture-v1.0/mysql/my.cnf
Normal file
24
docs/tags/picture-v1.0/mysql/my.cnf
Normal file
@ -0,0 +1,24 @@
|
||||
[client]
|
||||
port = 3306
|
||||
default-character-set = utf8mb4
|
||||
|
||||
[mysqld]
|
||||
user = mysql
|
||||
port = 3306
|
||||
sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
|
||||
|
||||
default-storage-engine = InnoDB
|
||||
default-authentication-plugin = mysql_native_password
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_unicode_ci
|
||||
init_connect = 'SET NAMES utf8mb4'
|
||||
|
||||
slow_query_log
|
||||
#long_query_time = 3
|
||||
slow-query-log-file = /var/log/mysql/mysql.slow.log
|
||||
log-error = /var/log/mysql/mysql.error.log
|
||||
|
||||
default-time-zone = '+8:00'
|
||||
|
||||
[mysql]
|
||||
default-character-set = utf8mb4
|
59
docs/tags/picture-v1.0/nginx/conf/nginx.conf
Normal file
59
docs/tags/picture-v1.0/nginx/conf/nginx.conf
Normal file
@ -0,0 +1,59 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
# 上游后端定义
|
||||
upstream picture_backend {
|
||||
server smile-picture-backend:8096;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# ---------- REST API 代理 ----------
|
||||
location /api/ {
|
||||
proxy_pass http://picture_backend;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
client_max_body_size 10M; # 允许上传 10MB
|
||||
# 添加代理超时设置(单位:秒)
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# ---------- WebSocket 代理 ----------
|
||||
location /api/ws/ {
|
||||
proxy_pass http://picture_backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 86400s;
|
||||
}
|
||||
|
||||
# ---------- 前端路由兼容 ----------
|
||||
# HTML5 history-mode:找不到文件时回退到 index.html 交给前端渲染
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
@media (min-width: 1024px){.about{min-height:100vh;display:flex;align-items:center}}
|
@ -0,0 +1 @@
|
||||
import{_ as o,c as s,a as t,o as a}from"./index-CrUyCRGJ.js";const n={},c={class:"about"};function r(_,e){return a(),s("div",c,e[0]||(e[0]=[t("h1",null,"This is an about page",-1)]))}const l=o(n,[["render",r]]);export{l as default};
|
File diff suppressed because one or more lines are too long
606
docs/tags/picture-v1.0/nginx/html/assets/index-CrUyCRGJ.js
Normal file
606
docs/tags/picture-v1.0/nginx/html/assets/index-CrUyCRGJ.js
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/tags/picture-v1.0/nginx/html/favicon.ico
Normal file
BIN
docs/tags/picture-v1.0/nginx/html/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
15
docs/tags/picture-v1.0/nginx/html/index.html
Normal file
15
docs/tags/picture-v1.0/nginx/html/index.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Smile云图库</title>
|
||||
<meta name="description" content="Smile云图库,海量图片素材免费获取">
|
||||
<script type="module" crossorigin src="/assets/index-CrUyCRGJ.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Cidcg2WD.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
41
pom.xml
41
pom.xml
@ -153,7 +153,31 @@
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<!-- ① 指定最终打包出来的名称(不带后缀 .jar) -->
|
||||
<finalName>smile-picture-backend</finalName>
|
||||
|
||||
<!-- ② 资源过滤(如果你有占位符要替换才需要,否则可以删掉这一块) -->
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
</testResource>
|
||||
</testResources>
|
||||
|
||||
<plugins>
|
||||
<!-- ③ 保留你的编译配置 -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
@ -164,13 +188,27 @@
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- ④ 跳过单元测试(可选) -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- ⑤ Spring Boot 打包:去掉 skip,显式 repackage,保证 Manifest 写入 Main-Class -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<configuration>
|
||||
<!-- 你的主类全限定名 -->
|
||||
<mainClass>edu.whut.smilepicturebackend.SmilePictureBackendApplication</mainClass>
|
||||
<skip>true</skip>
|
||||
<!-- 打成可执行 JAR -->
|
||||
<layout>JAR</layout>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@ -184,4 +222,5 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
|
@ -1,63 +0,0 @@
|
||||
package picturesearch;
|
||||
import edu.whut.smilepicturebackend.exception.BusinessException;
|
||||
import edu.whut.smilepicturebackend.exception.ErrorCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import picturesearch.enums.SearchSourceEnum;
|
||||
import picturesearch.model.SearchPictureResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 以图搜图
|
||||
*
|
||||
* @author Silas Yan 2025-03-23:09:50
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractSearchPicture {
|
||||
|
||||
/**
|
||||
* 执行搜索
|
||||
*
|
||||
* @param searchSource 搜索源
|
||||
* @param sourcePicture 源图片
|
||||
* @param randomSeed 随机种子
|
||||
* @param searchCount 搜索数量
|
||||
* @return 搜索结果
|
||||
*/
|
||||
public final List<SearchPictureResult> execute(String searchSource, String sourcePicture, Integer randomSeed, Integer searchCount) {
|
||||
log.info("开始搜索图片,搜索源:{},源图片:{},随机种子:{}", searchSource, sourcePicture, randomSeed);
|
||||
// 校验
|
||||
SearchSourceEnum searchSourceEnum = SearchSourceEnum.getEnumByKey(searchSource);
|
||||
if (searchSourceEnum == null) {
|
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "不支持的搜索源");
|
||||
}
|
||||
// 执行搜索
|
||||
String requestUrl = this.executeSearch(searchSourceEnum, sourcePicture);
|
||||
List<SearchPictureResult> pictureResultList = this.sendRequestGetResponse(requestUrl, randomSeed, searchCount);
|
||||
// 如果当前结果大于 searchCount 就截取
|
||||
if (pictureResultList.size() > searchCount) {
|
||||
pictureResultList = pictureResultList.subList(0, searchCount);
|
||||
}
|
||||
log.info("搜索图片结束,返回结果数量:{}", pictureResultList.size());
|
||||
return pictureResultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据原图片获取搜索图片的列表地址
|
||||
*
|
||||
* @param searchSourceEnum 搜索源枚举
|
||||
* @param sourcePicture 源图片
|
||||
* @return 搜索图片的列表地址
|
||||
*/
|
||||
protected abstract String executeSearch(SearchSourceEnum searchSourceEnum, String sourcePicture);
|
||||
|
||||
/**
|
||||
* 发送请求获取响应
|
||||
*
|
||||
* @param requestUrl 请求地址
|
||||
* @param randomSeed 随机种子
|
||||
* @param searchCount 搜索数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
protected abstract List<SearchPictureResult> sendRequestGetResponse(String requestUrl, Integer randomSeed, Integer searchCount);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package picturesearch;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import picturesearch.impl.SoSearchPicture;
|
||||
import picturesearch.model.SearchPictureResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 以图搜图测试
|
||||
*/
|
||||
public class PictureSearchTest {
|
||||
public static void main(String[] args) {
|
||||
// 360以图搜图
|
||||
// String imageUrl1 = "https://baolong-picture-1259638363.cos.ap-shanghai.myqcloud.com//public/10000000/2025-02-15_lzn23PuxZqt8CPB1.";
|
||||
String imageUrl1 = "https://fshare.bitday.top/api/public/dl/BL9SNN2V/store/820b2a3c-fa59-472e-b3ee-572c63c2ae91.png";
|
||||
AbstractSearchPicture soSearchPicture = new SoSearchPicture();
|
||||
List<SearchPictureResult> soResultList = soSearchPicture.execute("SO", imageUrl1, 1, 21);
|
||||
System.out.println("结果列表: " + JSONUtil.parse(soResultList));
|
||||
|
||||
// // 百度以图搜图
|
||||
// String imageUrl2 = "https://www.codefather.cn/logo.png";
|
||||
// AbstractSearchPicture baiduSearchPicture = new BaiduSearchPicture();
|
||||
// List<SearchPictureResult> baiduResultList = baiduSearchPicture.execute("BAIDU", imageUrl2, 1, 31);
|
||||
// System.out.println("结果列表" + JSONUtil.parse(baiduResultList));
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package picturesearch.enums;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 搜索来源枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum SearchSourceEnum {
|
||||
|
||||
SO("SO", "360", "https://st.so.com/r?src=st&srcsp=home&img_url=%s&submittype=imgurl"),
|
||||
BAIDU("BAIDU", "百度", "https://graph.baidu.com/upload?uptime=%s");
|
||||
|
||||
private final String key;
|
||||
private final String label;
|
||||
private final String url;
|
||||
|
||||
SearchSourceEnum(String key, String label, String url) {
|
||||
this.key = key;
|
||||
this.label = label;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 KEY 获取枚举
|
||||
*
|
||||
* @param key 状态键值
|
||||
* @return 枚举对象,未找到时返回 null
|
||||
*/
|
||||
public static SearchSourceEnum of(String key) {
|
||||
if (ObjUtil.isEmpty(key)) return null;
|
||||
return ArrayUtil.firstMatch(e -> e.getKey().equals(key), values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 KEY 获取枚举
|
||||
*
|
||||
* @param key KEY
|
||||
* @return 枚举
|
||||
*/
|
||||
public static SearchSourceEnum getEnumByKey(String key) {
|
||||
if (ObjUtil.isEmpty(key)) {
|
||||
return null;
|
||||
}
|
||||
for (SearchSourceEnum anEnum : SearchSourceEnum.values()) {
|
||||
if (anEnum.key.equals(key)) {
|
||||
return anEnum;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有效的 KEY 列表
|
||||
*
|
||||
* @return 有效 KEY 集合(不可变列表)
|
||||
*/
|
||||
public static List<String> keys() {
|
||||
return Arrays.stream(values())
|
||||
.map(SearchSourceEnum::getKey)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
package picturesearch.impl;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import edu.whut.smilepicturebackend.exception.BusinessException;
|
||||
import edu.whut.smilepicturebackend.exception.ErrorCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.springframework.stereotype.Component;
|
||||
import picturesearch.AbstractSearchPicture;
|
||||
import picturesearch.enums.SearchSourceEnum;
|
||||
import picturesearch.model.SearchPictureResult;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 百度以图搜图实现
|
||||
* <p>
|
||||
* 说明: 百度的以图搜图默认返回 30 条
|
||||
*
|
||||
* @author Silas Yan 2025-03-23:11:09
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BaiduSearchPicture extends AbstractSearchPicture {
|
||||
/**
|
||||
* 根据原图片获取搜索图片的列表地址
|
||||
*
|
||||
* @param searchSourceEnum 搜索源枚举
|
||||
* @param sourcePicture 源图片
|
||||
* @return 搜索图片的列表地址
|
||||
*/
|
||||
@Override
|
||||
protected String executeSearch(SearchSourceEnum searchSourceEnum, String sourcePicture) {
|
||||
String searchUrl = String.format(searchSourceEnum.getUrl(), System.currentTimeMillis());
|
||||
log.info("[百度搜图]搜图地址:{}", searchUrl);
|
||||
try {
|
||||
String pageUrl = getPageUrl(searchUrl, sourcePicture);
|
||||
return getListUrl(pageUrl);
|
||||
} catch (Exception e) {
|
||||
log.error("[百度搜图]搜图失败", e);
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求获取响应
|
||||
*
|
||||
* @param requestUrl 请求地址
|
||||
* @param randomSeed 随机种子
|
||||
* @param searchCount 搜索数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Override
|
||||
protected List<SearchPictureResult> sendRequestGetResponse(String requestUrl, Integer randomSeed, Integer searchCount) {
|
||||
log.info("[百度搜图]搜图地址:{}, 随机种子: {}, 搜索数量: {}", requestUrl, randomSeed, searchCount);
|
||||
if (searchCount == null) searchCount = 30;
|
||||
List<SearchPictureResult> resultList = new ArrayList<>();
|
||||
int currentWhileNum = 0;
|
||||
int targetWhileNum = searchCount / 30 + 1;
|
||||
while (currentWhileNum < targetWhileNum && resultList.size() < searchCount) {
|
||||
if (randomSeed == null) randomSeed = RandomUtil.randomInt(1, 20);
|
||||
log.info("[百度搜图]当前随机种子: {}, 当前结果数量: {}", randomSeed, resultList.size());
|
||||
String URL = requestUrl + "&page=" + randomSeed;
|
||||
try (HttpResponse response = HttpUtil.createGet(URL).execute()) {
|
||||
// 判断响应状态
|
||||
if (HttpStatus.HTTP_OK != response.getStatus()) {
|
||||
log.error("[百度搜图]搜图失败,响应状态码:{}", response.getStatus());
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
// 解析响应, 处理响应结果
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
if (!body.containsKey("data")) {
|
||||
log.error("[百度搜图]搜图失败,未获取到图片数据");
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "未获取到图片列表");
|
||||
}
|
||||
JSONObject data = body.getJSONObject("data");
|
||||
if (!data.containsKey("list")) {
|
||||
log.error("[百度搜图]搜图失败,未获取到图片数据");
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "未获取到图片列表");
|
||||
}
|
||||
JSONArray baiduResult = data.getJSONArray("list");
|
||||
for (Object o : baiduResult) {
|
||||
JSONObject so = (JSONObject) o;
|
||||
SearchPictureResult pictureResult = new SearchPictureResult();
|
||||
pictureResult.setImageUrl(so.getStr("thumbUrl"));
|
||||
pictureResult.setImageKey(so.getStr("contsign"));
|
||||
resultList.add(pictureResult);
|
||||
}
|
||||
currentWhileNum++;
|
||||
} catch (Exception e) {
|
||||
log.error("[百度搜图]搜图失败", e);
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
} finally {
|
||||
randomSeed++;
|
||||
}
|
||||
}
|
||||
log.info("[百度搜图]最终结果数量: {}", resultList.size());
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片页面地址
|
||||
*
|
||||
* @param searchUrl 搜索地址
|
||||
* @param sourcePicture 源图片
|
||||
* @return 图片页面地址
|
||||
*/
|
||||
public static String getPageUrl(String searchUrl, String sourcePicture) {
|
||||
Map<String, Object> formData = new HashMap<>();
|
||||
formData.put("image", sourcePicture);
|
||||
formData.put("tn", "pc");
|
||||
formData.put("from", "pc");
|
||||
formData.put("image_source", "PC_UPLOAD_URL");
|
||||
String acsToken = "jmM4zyI8OUixvSuWh0sCy4xWbsttVMZb9qcRTmn6SuNWg0vCO7N0s6Lffec+IY5yuqHujHmCctF9BVCGYGH0H5SH/H3VPFUl4O4CP1jp8GoAzuslb8kkQQ4a21Tebge8yhviopaiK66K6hNKGPlWt78xyyJxTteFdXYLvoO6raqhz2yNv50vk4/41peIwba4lc0hzoxdHxo3OBerHP2rfHwLWdpjcI9xeu2nJlGPgKB42rYYVW50+AJ3tQEBEROlg/UNLNxY+6200B/s6Ryz+n7xUptHFHi4d8Vp8q7mJ26yms+44i8tyiFluaZAr66/+wW/KMzOhqhXCNgckoGPX1SSYwueWZtllIchRdsvCZQ8tFJymKDjCf3yI/Lw1oig9OKZCAEtiLTeKE9/CY+Crp8DHa8Tpvlk2/i825E3LuTF8EQfzjcGpVnR00Lb4/8A";
|
||||
try (HttpResponse response = HttpRequest.post(searchUrl).form(formData)
|
||||
.header("Acs-Token", acsToken).timeout(5000).execute()) {
|
||||
// 判断响应状态
|
||||
if (HttpStatus.HTTP_OK != response.getStatus()) {
|
||||
log.error("[百度搜图]搜图失败,响应状态码:{}", response.getStatus());
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
// 解析响应
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
if (!body.getInt("status").equals(0)) {
|
||||
log.error("[百度搜图]搜图失败,响应内容为空");
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
JSONObject data = JSONUtil.parseObj(body.getStr("data"));
|
||||
String rawUrl = data.getStr("url");
|
||||
if (StrUtil.isEmpty(rawUrl)) {
|
||||
log.error("[百度搜图]搜图失败,地址为空");
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
String decodeUrl = URLUtil.decode(rawUrl, StandardCharsets.UTF_8);
|
||||
if (StrUtil.isEmpty(decodeUrl)) {
|
||||
log.error("[百度搜图]搜图失败,未获取到图片页面地址");
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
return decodeUrl;
|
||||
} catch (Exception e) {
|
||||
log.error("[百度搜图]搜图失败", e);
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜索失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片列表地址
|
||||
*
|
||||
* @param resultUrl 结果页面地址
|
||||
* @return 图片列表地址
|
||||
*/
|
||||
private static String getListUrl(String resultUrl) {
|
||||
try {
|
||||
// 使用 Jsoup 获取 HTML 内容
|
||||
Document document = Jsoup.connect(resultUrl).timeout(5000).get();
|
||||
// 获取所有 <script> 标签
|
||||
Elements scriptElements = document.getElementsByTag("script");
|
||||
// 遍历找到包含 `firstUrl` 的脚本内容
|
||||
String firstUrl = null;
|
||||
for (Element script : scriptElements) {
|
||||
String scriptContent = script.html();
|
||||
if (scriptContent.contains("\"firstUrl\"")) {
|
||||
// 正则表达式提取 firstUrl 的值
|
||||
Pattern pattern = Pattern.compile("\"firstUrl\"\\s*:\\s*\"(.*?)\"");
|
||||
Matcher matcher = pattern.matcher(scriptContent);
|
||||
if (matcher.find()) {
|
||||
// 处理转义字符
|
||||
firstUrl = matcher.group(1).replace("\\/", "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StrUtil.isEmpty(firstUrl)) {
|
||||
log.error("[百度搜图]搜图失败,未找到图片元素");
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
return firstUrl;
|
||||
} catch (Exception e) {
|
||||
log.error("[百度搜图]搜图失败", e);
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
package picturesearch.impl;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import edu.whut.smilepicturebackend.exception.BusinessException;
|
||||
import edu.whut.smilepicturebackend.exception.ErrorCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.springframework.stereotype.Component;
|
||||
import picturesearch.AbstractSearchPicture;
|
||||
import picturesearch.enums.SearchSourceEnum;
|
||||
import picturesearch.model.SearchPictureResult;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 360以图搜图实现
|
||||
* <p>
|
||||
* 说明: 360的以图搜图默认返回 20 条
|
||||
*
|
||||
* @author Silas Yan 2025-03-23:10:05
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SoSearchPicture extends AbstractSearchPicture {
|
||||
/**
|
||||
* 根据原图片获取搜索图片的列表地址
|
||||
*
|
||||
* @param searchSourceEnum 搜索源枚举
|
||||
* @param sourcePicture 源图片
|
||||
* @return 搜索图片的列表地址
|
||||
*/
|
||||
@Override
|
||||
protected String executeSearch(SearchSourceEnum searchSourceEnum, String sourcePicture) {
|
||||
String searchUrl = String.format(searchSourceEnum.getUrl(), sourcePicture);
|
||||
log.info("[360搜图]搜图地址:{}", searchUrl);
|
||||
try {
|
||||
Document document = Jsoup.connect(searchUrl).timeout(5000).get();
|
||||
System.out.println(document);
|
||||
Element element = document.selectFirst(".img_img");
|
||||
if (element == null) {
|
||||
log.error("[360搜图]搜图失败,未找到图片元素");
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败,未找到图片");
|
||||
}
|
||||
String imagesUrl = "";
|
||||
// 获取当前元素的属性
|
||||
String style = element.attr("style");
|
||||
if (style.contains("background-image:url(")) {
|
||||
// 提取URL部分
|
||||
int start = style.indexOf("url(") + 4; // 从"Url("之后开始
|
||||
int end = style.indexOf(")", start); // 找到右括号的位置
|
||||
if (start > 4 && end > start) {
|
||||
imagesUrl = style.substring(start, end);
|
||||
}
|
||||
}
|
||||
if (StrUtil.isEmpty(imagesUrl)) {
|
||||
log.error("[360搜图]搜图失败,未找到图片地址");
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败,未找到图片");
|
||||
}
|
||||
return imagesUrl;
|
||||
} catch (Exception e) {
|
||||
log.error("[360搜图]搜图失败", e);
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求获取响应
|
||||
*
|
||||
* @param requestUrl 请求地址
|
||||
* @param randomSeed 随机种子
|
||||
* @param searchCount 搜索数量
|
||||
* @return 响应结果
|
||||
*/
|
||||
@Override
|
||||
protected List<SearchPictureResult> sendRequestGetResponse(String requestUrl, Integer randomSeed, Integer searchCount) {
|
||||
log.info("[360搜图]搜图地址:{}, 随机种子: {}, 搜索数量: {}", requestUrl, randomSeed, searchCount);
|
||||
if (searchCount == null) searchCount = 20;
|
||||
List<SearchPictureResult> resultList = new ArrayList<>();
|
||||
int currentWhileNum = 0;
|
||||
int targetWhileNum = searchCount / 20 + 1;
|
||||
while (currentWhileNum < targetWhileNum && resultList.size() < searchCount) {
|
||||
if (randomSeed == null) randomSeed = RandomUtil.randomInt(1, 20);
|
||||
log.info("[360搜图]当前随机种子: {}, 当前结果数量: {}", randomSeed, resultList.size());
|
||||
String URL = "https://st.so.com/stu?a=mrecomm&start=" + randomSeed;
|
||||
Map<String, Object> formData = new HashMap<>();
|
||||
formData.put("img_url", requestUrl);
|
||||
try (HttpResponse response = HttpRequest.post(URL).form(formData).timeout(5000).execute()) {
|
||||
// 判断响应状态
|
||||
if (HttpStatus.HTTP_OK != response.getStatus()) {
|
||||
log.error("[360搜图]搜图失败,响应状态码:{}", response.getStatus());
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
// 解析响应, 处理响应结果
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
if (!Integer.valueOf(0).equals(body.getInt("errno"))) {
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
}
|
||||
JSONObject data = body.getJSONObject("data");
|
||||
JSONArray soResult = data.getJSONArray("result");
|
||||
for (Object o : soResult) {
|
||||
JSONObject so = (JSONObject) o;
|
||||
SearchPictureResult pictureResult = new SearchPictureResult();
|
||||
String prefix;
|
||||
if (StrUtil.isNotBlank(so.getStr("https"))) {
|
||||
prefix = "https://" + so.getStr("https") + "/";
|
||||
} else {
|
||||
prefix = "http://" + so.getStr("http") + "/";
|
||||
}
|
||||
pictureResult.setImageUrl(prefix + so.getStr("imgkey"));
|
||||
pictureResult.setImageName(so.getStr("title"));
|
||||
pictureResult.setImageKey(so.getStr("imgkey"));
|
||||
resultList.add(pictureResult);
|
||||
}
|
||||
currentWhileNum++;
|
||||
} catch (Exception e) {
|
||||
log.error("[360搜图]搜图失败", e);
|
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "搜图失败");
|
||||
} finally {
|
||||
randomSeed++;
|
||||
}
|
||||
}
|
||||
log.info("[360搜图]最终结果数量: {}", resultList.size());
|
||||
return resultList;
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package picturesearch.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 以图搜图结果
|
||||
*
|
||||
* @author Silas Yan 2025-03-23:09:40
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class SearchPictureResult implements Serializable {
|
||||
|
||||
/**
|
||||
* 图片地址
|
||||
*/
|
||||
private String imageUrl;
|
||||
|
||||
/**
|
||||
* 图片名称
|
||||
*/
|
||||
private String imageName;
|
||||
|
||||
/**
|
||||
* 图片 KEY
|
||||
*/
|
||||
private String imageKey;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
101
src/main/resources/application-dev.yml
Normal file
101
src/main/resources/application-dev.yml
Normal file
@ -0,0 +1,101 @@
|
||||
server:
|
||||
port: 8123
|
||||
servlet:
|
||||
context-path: /api
|
||||
# cookie 30 天过期
|
||||
session:
|
||||
cookie:
|
||||
max-age: 2592000
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
active: local
|
||||
application:
|
||||
name: smile-picture-backend
|
||||
# 数据库配置
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/smile-picture
|
||||
username: root
|
||||
password: 123456
|
||||
# Redis 配置
|
||||
redis:
|
||||
database: 1
|
||||
host: localhost
|
||||
port: 6379
|
||||
password: 123456
|
||||
timeout: 5000
|
||||
# Session 配置
|
||||
session:
|
||||
store-type: redis
|
||||
# session 30 天后过期,单位是秒
|
||||
timeout: 2592000
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
max-request-size: 10MB
|
||||
# 空间图片分表
|
||||
shardingsphere:
|
||||
datasource:
|
||||
names: smile-picture
|
||||
smile-picture:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/smile-picture
|
||||
username: root
|
||||
password: 123456
|
||||
rules:
|
||||
sharding:
|
||||
tables:
|
||||
picture:
|
||||
actual-data-nodes: smile-picture.picture # 动态分表
|
||||
table-strategy:
|
||||
standard:
|
||||
sharding-column: space_id
|
||||
sharding-algorithm-name: picture_sharding_algorithm # 使用自定义分片算法
|
||||
sharding-algorithms:
|
||||
picture_sharding_algorithm:
|
||||
type: CLASS_BASED
|
||||
props:
|
||||
strategy: standard
|
||||
algorithmClassName: edu.whut.smilepicturebackend.manager.sharding.PictureShardingAlgorithm
|
||||
props:
|
||||
sql-show: true #打印实际执行的sql
|
||||
mybatis-plus:
|
||||
type-aliases-package: edu.whut.smilepicturebackend.model.entity
|
||||
configuration:
|
||||
# MyBatis 配置
|
||||
map-underscore-to-camel-case: true #是否开启驼峰转换
|
||||
# 仅在开发环境打印日志
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
enable-sql-runner: true
|
||||
db-config:
|
||||
logic-delete-field: isDelete # 全局逻辑删除的实体字段名 删除操作时表中数据不会被物理删除,只会设置isDelete为1
|
||||
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
|
||||
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
|
||||
|
||||
# 接口文档配置
|
||||
knife4j:
|
||||
enable: true
|
||||
openapi:
|
||||
title: 接口文档
|
||||
version: 1.0
|
||||
group:
|
||||
default:
|
||||
api-rule: package
|
||||
api-rule-resources:
|
||||
- edu.whut.smilepicturebackend.controller #接口地址
|
||||
|
||||
# 对象存储配置(需要从腾讯云获取)
|
||||
cos:
|
||||
client:
|
||||
host: ${smile-picture.cos.client.host}
|
||||
secretId: ${smile-picture.cos.client.secretId}
|
||||
secretKey: ${smile-picture.cos.client.secretKey}
|
||||
region: ${smile-picture.cos.client.region}
|
||||
bucket: ${smile-picture.cos.client.bucket}
|
||||
|
||||
smile-picture:
|
||||
aliyun:
|
||||
apiKey: ${smile-picture.aliyun.apiKey}
|
100
src/main/resources/application-prod.yml
Normal file
100
src/main/resources/application-prod.yml
Normal file
@ -0,0 +1,100 @@
|
||||
server:
|
||||
port: 8096
|
||||
servlet:
|
||||
context-path: /api
|
||||
# cookie 30 天过期
|
||||
session:
|
||||
cookie:
|
||||
max-age: 2592000
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: smile-picture-backend
|
||||
# 数据库配置
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://mysql:3306/smile-picture
|
||||
username: root
|
||||
password: 123456
|
||||
# Redis 配置
|
||||
redis:
|
||||
database: 1
|
||||
host: redis
|
||||
port: 6379
|
||||
password: 123456
|
||||
timeout: 5000
|
||||
# Session 配置
|
||||
session:
|
||||
store-type: redis
|
||||
# session 30 天后过期,单位是秒
|
||||
timeout: 2592000
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
max-request-size: 10MB
|
||||
|
||||
# 空间图片分表
|
||||
shardingsphere:
|
||||
datasource:
|
||||
names: smile-picture
|
||||
smile-picture:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://mysql:3306/smile-picture
|
||||
username: root
|
||||
password: 123456
|
||||
rules:
|
||||
sharding:
|
||||
tables:
|
||||
picture:
|
||||
actual-data-nodes: smile-picture.picture # 动态分表
|
||||
table-strategy:
|
||||
standard:
|
||||
sharding-column: space_id
|
||||
sharding-algorithm-name: picture_sharding_algorithm # 使用自定义分片算法
|
||||
sharding-algorithms:
|
||||
picture_sharding_algorithm:
|
||||
type: CLASS_BASED
|
||||
props:
|
||||
strategy: standard
|
||||
algorithmClassName: edu.whut.smilepicturebackend.manager.sharding.PictureShardingAlgorithm
|
||||
props:
|
||||
sql-show: false # 生产建议关闭;需要排查时可临时改 true
|
||||
mybatis-plus:
|
||||
type-aliases-package: edu.whut.smilepicturebackend.model.entity
|
||||
configuration:
|
||||
# MyBatis 配置
|
||||
map-underscore-to-camel-case: true #是否开启驼峰转换
|
||||
# 仅在开发环境打印日志
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
enable-sql-runner: true
|
||||
db-config:
|
||||
logic-delete-field: isDelete # 全局逻辑删除的实体字段名 删除操作时表中数据不会被物理删除,只会设置isDelete为1
|
||||
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
|
||||
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
|
||||
|
||||
# 接口文档配置
|
||||
knife4j:
|
||||
enable: true
|
||||
openapi:
|
||||
title: 接口文档
|
||||
version: 1.0
|
||||
group:
|
||||
default:
|
||||
api-rule: package
|
||||
api-rule-resources:
|
||||
- edu.whut.smilepicturebackend.controller #接口地址
|
||||
|
||||
# 对象存储配置(需要从腾讯云获取)
|
||||
cos:
|
||||
client:
|
||||
host: ${smile-picture.cos.client.host}
|
||||
secretId: ${smile-picture.cos.client.secretId}
|
||||
secretKey: ${smile-picture.cos.client.secretKey}
|
||||
region: ${smile-picture.cos.client.region}
|
||||
bucket: ${smile-picture.cos.client.bucket}
|
||||
|
||||
smile-picture:
|
||||
aliyun:
|
||||
apiKey: ${smile-picture.aliyun.apiKey}
|
@ -1,101 +1,3 @@
|
||||
server:
|
||||
port: 8123
|
||||
servlet:
|
||||
context-path: /api
|
||||
# cookie 30 天过期
|
||||
session:
|
||||
cookie:
|
||||
max-age: 2592000
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
active: local
|
||||
application:
|
||||
name: smile-picture-backend
|
||||
# 数据库配置
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/smile-picture
|
||||
username: root
|
||||
password: 123456
|
||||
# Redis 配置
|
||||
redis:
|
||||
database: 1
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
password: 123456
|
||||
timeout: 5000
|
||||
# Session 配置
|
||||
session:
|
||||
store-type: redis
|
||||
# session 30 天后过期,单位是秒
|
||||
timeout: 2592000
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
max-request-size: 10MB
|
||||
# 空间图片分表
|
||||
shardingsphere:
|
||||
datasource:
|
||||
names: smile-picture
|
||||
smile-picture:
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/smile-picture
|
||||
username: root
|
||||
password: 123456
|
||||
rules:
|
||||
sharding:
|
||||
tables:
|
||||
picture:
|
||||
actual-data-nodes: smile-picture.picture # 动态分表
|
||||
table-strategy:
|
||||
standard:
|
||||
sharding-column: space_id
|
||||
sharding-algorithm-name: picture_sharding_algorithm # 使用自定义分片算法
|
||||
sharding-algorithms:
|
||||
picture_sharding_algorithm:
|
||||
type: CLASS_BASED
|
||||
props:
|
||||
strategy: standard
|
||||
algorithmClassName: edu.whut.smilepicturebackend.manager.sharding.PictureShardingAlgorithm
|
||||
props:
|
||||
sql-show: true #打印实际执行的sql
|
||||
mybatis-plus:
|
||||
type-aliases-package: edu.whut.smilepicturebackend.model.entity
|
||||
configuration:
|
||||
# MyBatis 配置
|
||||
map-underscore-to-camel-case: true #是否开启驼峰转换
|
||||
# 仅在开发环境打印日志
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
enable-sql-runner: true
|
||||
db-config:
|
||||
logic-delete-field: isDelete # 全局逻辑删除的实体字段名 删除操作时表中数据不会被物理删除,只会设置isDelete为1
|
||||
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
|
||||
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
|
||||
|
||||
# 接口文档配置
|
||||
knife4j:
|
||||
enable: true
|
||||
openapi:
|
||||
title: 接口文档
|
||||
version: 1.0
|
||||
group:
|
||||
default:
|
||||
api-rule: package
|
||||
api-rule-resources:
|
||||
- edu.whut.smilepicturebackend.controller #接口地址
|
||||
|
||||
# 对象存储配置(需要从腾讯云获取)
|
||||
cos:
|
||||
client:
|
||||
host: ${smile-picture.cos.client.host}
|
||||
secretId: ${smile-picture.cos.client.secretId}
|
||||
secretKey: ${smile-picture.cos.client.secretKey}
|
||||
region: ${smile-picture.cos.client.region}
|
||||
bucket: ${smile-picture.cos.client.bucket}
|
||||
|
||||
smile-picture:
|
||||
aliyun:
|
||||
apiKey: ${smile-picture.aliyun.apiKey}
|
||||
active: prod,local
|
||||
|
Loading…
x
Reference in New Issue
Block a user