Compare commits

62 Commits

Author SHA1 Message Date
c7ddb1a8b0 Merge pull request 'v1.0.1' (#5) from dev into master
Reviewed-on: #5
2026-04-12 16:17:02 +00:00
Timi
b46e9079d5 v1.0.1
All checks were successful
CI / build-deploy (pull_request) Successful in 26s
CI / notify-on-failure (pull_request) Has been skipped
2026-04-13 00:09:55 +08:00
Timi
dc20070bf8 update status api 2026-04-13 00:09:48 +08:00
45c9fc814a Merge pull request 'v1.0.0' (#4) from dev into master
Reviewed-on: #4
2026-04-09 05:16:04 +00:00
Timi
78163441dd v1.0.0
All checks were successful
CI / build-deploy (pull_request) Successful in 16s
CI / notify-on-failure (pull_request) Has been skipped
2026-04-09 13:14:20 +08:00
407dc13ac4 Merge pull request 'v1.0.0' (#3) from dev into master
Reviewed-on: #3
2026-04-09 04:09:16 +00:00
Timi
9762be1244 v1.0.0
Some checks failed
CI / build-deploy (pull_request) Failing after 51s
CI / notify-on-failure (pull_request) Successful in 0s
2026-04-09 12:08:24 +08:00
0c06bf16c2 Merge pull request 'v1.0.0' (#2) from dev into master
Reviewed-on: #2
2026-04-09 04:03:43 +00:00
Timi
971cad7365 v1.0.0
Some checks failed
CI / build-deploy (pull_request) Failing after 5s
CI / notify-on-failure (pull_request) Successful in 0s
2026-04-09 12:03:15 +08:00
1db39e77d3 Merge pull request 'v1.0.0' (#1) from dev into master
Reviewed-on: #1
2026-04-08 08:48:12 +00:00
Timi
b5e9da0e9b v1.0.0
Some checks failed
CI / build-deploy (pull_request) Failing after 3s
CI / notify-on-failure (pull_request) Successful in 0s
2026-04-08 16:30:10 +08:00
Timi
34e1ac6264 remove gson 2026-04-08 12:00:52 +08:00
Timi
ef192daa93 add export config 2026-04-08 11:56:07 +08:00
Timi
8947269351 remove gson 2026-04-08 11:25:45 +08:00
Timi
b6a58b7376 update system status api, add UPS/Docker status api 2026-04-07 20:12:52 +08:00
Timi
cd7bc31e6b add upload permission 2026-01-29 16:27:52 +08:00
Timi
e619f3a1e2 add ClipboardService 2026-01-15 13:47:38 +08:00
Timi
230864fa43 fix comment page 2026-01-15 13:03:57 +08:00
Timi
fa11b388db refactor TempFileService, use Attachment and mongodb 2026-01-05 14:35:38 +08:00
Timi
b6eb7980e4 remove AttachmentRequest and AttachmentView 2026-01-04 19:17:42 +08:00
Timi
3410c540ab remove Attachment.ext 2026-01-04 19:13:02 +08:00
Timi
040a46934d update BaseMapper list rename to select 2026-01-04 19:12:47 +08:00
Timi
7eb409f6d0 add MALL travel location type 2025-12-23 14:46:21 +08:00
Timi
7b3d4e2a65 nullable ttl arg for temp file 2025-12-22 11:01:21 +08:00
Timi
4e113e8fae update firstNotNull 2025-12-22 11:00:52 +08:00
Timi
7a34a481aa add ttl arg for tempFileUpload 2025-12-19 20:15:48 +08:00
Timi
a402ea86ef fix circular dependency 2025-12-19 19:47:45 +08:00
Timi
3eaa560aec sync travel updatedAt 2025-12-18 21:09:21 +08:00
Timi
e61da7d8f9 fix AttachmentService.createMedia fail 2025-12-18 14:46:10 +08:00
Timi
355b192071 page transportationType and days 2025-12-18 14:45:50 +08:00
Timi
c732f7cead add FOOD,PLAY,LIFE and remove RESTAURANT 2025-12-18 14:45:37 +08:00
Timi
812e33693b nullable TravelLocation.score and importance 2025-12-18 14:44:58 +08:00
Timi
a7b490240c fix fileName for temp file download 2025-12-18 14:44:18 +08:00
Timi
959fc30758 remove travelLocation.firstTraveledAt,lastTraveledAt,travelCount 2025-12-17 10:21:42 +08:00
Timi
fe86a84204 fix travel location update fail 2025-12-17 10:21:16 +08:00
Timi
42bcfcf70e add db.sql 2025-12-15 10:30:55 +08:00
Timi
d82c5200e7 refactor travel 2025-12-15 10:30:45 +08:00
Timi
658765df6f refactor travel 2025-12-13 18:45:50 +08:00
Timi
d12c76fe03 add Attachment.mimeType,metadata 2025-12-11 18:41:29 +08:00
Timi
2262eabe07 fix JournalController.delete 2025-12-10 15:37:48 +08:00
Timi
81c1a14228 add JournalController.listByIds 2025-12-10 13:37:44 +08:00
Timi
fdfb0439d9 update Journal 2025-12-09 17:43:54 +08:00
Timi
e2a3c193d8 remove pageByBizId and add deleteByBizId 2025-12-09 17:43:27 +08:00
Timi
40760ed517 fix GsonHandler dbConfig 2025-12-08 16:59:11 +08:00
Timi
6839dafdb9 update Page.size to long 2025-12-08 16:58:42 +08:00
Timi
bc2c920705 update timi-lang 2025-12-08 16:58:13 +08:00
Timi
59b0153f3e fix not match key exception code 2025-12-08 16:34:13 +08:00
Timi
ffaf7f84b4 upper GsonHandler.java to timi-spring 2025-12-05 15:33:29 +08:00
Timi
289ba499d3 update BaseMapper.page 2025-12-05 12:35:47 +08:00
Timi
723992f360 fix Regex update for timi-utils 2025-11-06 14:53:46 +08:00
Timi
05e15998f5 update timi-lang 2025-11-06 14:52:13 +08:00
Timi
44d55c0ed6 add SettingService.clearCache 2025-11-06 14:51:42 +08:00
Timi
06bb86ccd5 fix list comment error 2025-11-06 14:51:29 +08:00
Timi
6f1cf2083d fix article page sort 2025-11-06 14:50:44 +08:00
Timi
fd71f330d2 add Journal module 2025-11-06 14:50:28 +08:00
Timi
cdff62b3b5 update minecraft server/client 2025-11-06 14:48:58 +08:00
Timi
2ba868b3b6 support add media thumb attachment 2025-11-06 14:47:48 +08:00
Timi
c270ae177d add TempFileService 2025-11-06 14:46:26 +08:00
Timi
323e038e86 rename com.imyeyu.server to com.imyeyu.api 2025-07-22 15:26:14 +08:00
Timi
e816b885b2 add GIT_ABOUT_ARTICLE setting 2025-07-22 14:16:35 +08:00
Timi
53230943e8 remove Article ABOUT type,classId and add canList attr 2025-07-22 14:16:24 +08:00
Timi
2de29cb3a8 support RequestRange for system file download 2025-07-15 11:44:57 +08:00
444 changed files with 10061 additions and 4237 deletions

333
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,333 @@
name: CI
on:
pull_request:
branches:
- master
types:
- closed
jobs:
build-deploy:
runs-on: act_runner_java
if: ${{ github.event.pull_request.merged == true }}
outputs:
deployment_status: ${{ steps.set_status.outputs.status }}
env:
JAVA_HOME: /usr/lib/jvm/java-21-openjdk
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up environment
run: |
echo "PR #${{ github.event.number }} merged into master"
echo "Source branch: ${{ github.event.pull_request.head.ref }}"
echo "Target branch: ${{ github.event.pull_request.base.ref }}"
- name: Run tests
run: |
echo "Running test suite..."
- name: Setup Maven settings
run: |
if [ -z "${{ vars.TIMI_NEXUS_USERNAME }}" ] || [ -z "${{ vars.TIMI_NEXUS_PASSWORD }}" ]; then
echo "Missing vars.TIMI_NEXUS_USERNAME or vars.TIMI_NEXUS_PASSWORD"
exit 1
fi
mkdir -p ~/.m2
cat > ~/.m2/settings.xml <<EOF
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>timi_nexus</id>
<username>${{ vars.TIMI_NEXUS_USERNAME }}</username>
<password>${{ vars.TIMI_NEXUS_PASSWORD }}</password>
</server>
</servers>
</settings>
EOF
- name: Build project
run: |
mvn -B -DskipTests clean package -P prod-linux
- name: Deploy service
if: success()
env:
CONTAINER_NAME: ${{ vars.CONTAINER_NAME }}
CONTAINER_TARGET_PATH: ${{ vars.CONTAINER_TARGET_PATH }}
MAX_RETRIES: 3
RETRY_DELAY: 10
run: |
if [ -z "$CONTAINER_NAME" ] || [ -z "$CONTAINER_TARGET_PATH" ]; then
echo "Missing production environment variables"
echo "Required: CONTAINER_NAME, CONTAINER_TARGET_PATH"
exit 1
fi
retry_command() {
local cmd="$1"
local desc="$2"
local attempt=1
while [ $attempt -le $MAX_RETRIES ]; do
echo "[$desc] Attempt $attempt/$MAX_RETRIES..."
if eval "$cmd"; then
echo "OK: $desc succeeded"
return 0
fi
echo "FAIL: $desc failed (attempt $attempt/$MAX_RETRIES)"
if [ $attempt -lt $MAX_RETRIES ]; then
echo "Retrying in ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
fi
attempt=$((attempt + 1))
done
echo "FAIL: $desc failed after $MAX_RETRIES attempts"
return 1
}
version=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.version)
artifact_id=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.artifactId)
jar_file="target/${artifact_id}-${version}.jar"
if [ ! -f "$jar_file" ]; then
echo "Build artifact not found: $jar_file"
exit 1
fi
if ! command -v docker >/dev/null 2>&1; then
echo "docker command not found in runner environment"
exit 1
fi
if ! docker inspect "$CONTAINER_NAME" >/dev/null 2>&1; then
echo "Docker container not found: $CONTAINER_NAME"
exit 1
fi
target_jar="${artifact_id}.jar"
container_target="${CONTAINER_TARGET_PATH%/}/$target_jar"
echo "Deploying $jar_file to container $CONTAINER_NAME:$container_target"
if ! retry_command "docker cp \"$jar_file\" \"$CONTAINER_NAME:$container_target\"" "Docker copy"; then
exit 1
fi
echo "Restarting Docker container: $CONTAINER_NAME"
if ! retry_command "docker restart \"$CONTAINER_NAME\"" "Docker restart"; then
exit 1
fi
echo "Deployment completed successfully"
- name: Create release
if: ${{ success() && startsWith(github.event.pull_request.title, 'v') }}
env:
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
GITEA_SERVER_URL: ${{ github.server_url }}
GITEA_INTERNAL_URL: ${{ vars.TIMI_GITEA_INTERNAL_URL }}
GITEA_REPOSITORY: ${{ github.repository }}
RELEASE_TAG: ${{ github.event.pull_request.title }}
RELEASE_TARGET: ${{ github.sha }}
MAX_RETRIES: 3
RETRY_DELAY: 10
run: |
if [ -z "$GITEA_TOKEN" ]; then
echo "Missing secrets.RUNNER_TOKEN"
exit 1
fi
if [ -n "$GITEA_INTERNAL_URL" ]; then
api_base_url="$GITEA_INTERNAL_URL"
echo "Using internal Gitea URL: $api_base_url"
else
api_base_url="$GITEA_SERVER_URL"
echo "Using public Gitea URL: $api_base_url"
fi
version=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.version)
artifact_id=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.artifactId)
jar_file="target/${artifact_id}-${version}.jar"
if [ ! -f "$jar_file" ]; then
echo "Build artifact not found: $jar_file"
exit 1
fi
file_size=$(stat -c%s "$jar_file" 2>/dev/null || stat -f%z "$jar_file" 2>/dev/null || echo "unknown")
echo "Found fat jar: $jar_file (size: $file_size bytes)"
api_url="$api_base_url/api/v1/repos/$GITEA_REPOSITORY/releases"
payload=$(cat <<EOF
{
"tag_name": "$RELEASE_TAG",
"name": "$RELEASE_TAG",
"target_commitish": "$RELEASE_TARGET",
"draft": false,
"prerelease": false
}
EOF
)
echo "Creating release with tag: $RELEASE_TAG"
echo "API URL: $api_url"
echo "Target commit: $RELEASE_TARGET"
release_response_file=$(mktemp /tmp/release_response_XXXXXX.json)
trap "rm -f $release_response_file" EXIT
release_id=""
attempt=1
while [ $attempt -le $MAX_RETRIES ] && [ -z "$release_id" ]; do
echo "[Create release] Attempt $attempt/$MAX_RETRIES..."
> "$release_response_file"
http_code=$(curl -sS -w "%{http_code}" -o "$release_response_file" -X POST "$api_url" \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
--connect-timeout 30 \
--max-time 60 \
-d "$payload" 2>/dev/null) || http_code="000"
response=$(cat "$release_response_file" 2>/dev/null || echo "{}")
echo "HTTP Status: $http_code"
if [ "$http_code" = "201" ]; then
if command -v jq >/dev/null 2>&1; then
release_id=$(echo "$response" | jq -r '.id' 2>/dev/null)
else
release_id=$(echo "$response" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2 | tr -d '\n\r')
fi
echo "OK: Release created: id=$release_id"
elif [ "$http_code" = "409" ]; then
echo "Release already exists (HTTP 409), fetching existing release..."
existing=$(curl -sS "$api_url" -H "Authorization: token $GITEA_TOKEN" --connect-timeout 30 2>/dev/null || echo "[]")
if command -v jq >/dev/null 2>&1; then
release_id=$(echo "$existing" | jq -r ".[] | select(.tag_name==\"$RELEASE_TAG\") | .id" 2>/dev/null | head -1)
else
release_id=$(echo "$existing" | grep -o '"id":[0-9]*' | head -1 | cut -d: -f2 | tr -d '\n\r')
fi
if [ -n "$release_id" ]; then
echo "OK: Found existing release: id=$release_id"
else
echo "FAIL: Could not find existing release id"
fi
else
echo "FAIL: Create release failed (HTTP $http_code)"
if [ $attempt -lt $MAX_RETRIES ]; then
echo "Retrying in ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
fi
fi
attempt=$((attempt + 1))
done
if [ -z "$release_id" ]; then
echo "FAIL: Failed to create or find release after $MAX_RETRIES attempts"
exit 1
fi
asset_name=$(basename "$jar_file")
echo "Uploading asset: $asset_name (size: $file_size bytes)"
upload_url="$api_url/$release_id/assets?name=$asset_name"
echo "Upload URL: $upload_url"
asset_response_file=$(mktemp /tmp/asset_response_XXXXXX.json)
trap "rm -f $release_response_file $asset_response_file" EXIT
upload_success=false
attempt=1
while [ $attempt -le $MAX_RETRIES ] && [ "$upload_success" = "false" ]; do
echo "[Upload asset] Attempt $attempt/$MAX_RETRIES..."
> "$asset_response_file"
http_code=$(curl -sS -w "%{http_code}" -o "$asset_response_file" -X POST "$upload_url" \
-H "Authorization: token $GITEA_TOKEN" \
--connect-timeout 30 \
--max-time 300 \
-F "attachment=@$jar_file" 2>/dev/null) || http_code="000"
if [ "$http_code" = "201" ]; then
upload_success=true
echo "OK: Successfully uploaded: $asset_name"
else
echo "FAIL: Upload failed (HTTP $http_code)"
cat "$asset_response_file" 2>/dev/null || true
fi
if [ "$upload_success" = "false" ] && [ $attempt -lt $MAX_RETRIES ]; then
echo "Retrying in ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
fi
attempt=$((attempt + 1))
done
if [ "$upload_success" = "false" ]; then
echo "FAIL: Failed to upload asset after $MAX_RETRIES attempts"
exit 1
fi
- name: Mark deployment success
id: set_status
if: always()
run: |
echo "status=success" >> $GITHUB_OUTPUT
notify-on-failure:
runs-on: act_runner_java
needs: build-deploy
if: ${{ always() && github.event.pull_request.merged == true && needs.build-deploy.result == 'failure' }}
steps:
- name: Notify CI failure
env:
PR_NUMBER: ${{ github.event.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
SOURCE_BRANCH: ${{ github.event.pull_request.head.ref }}
AUTHOR: ${{ github.event.pull_request.user.login }}
COMMIT_SHA: ${{ github.sha }}
REPO: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
WEBHOOK_URL: ${{ vars.NOTIFY_WEBHOOK_URL }}
run: |
echo "========================================="
echo "CI Pipeline Failed - Manual Review Required"
echo "========================================="
echo ""
echo "PR: #$PR_NUMBER - $PR_TITLE"
echo "Branch: $SOURCE_BRANCH"
echo "Author: $AUTHOR"
echo "Commit: $COMMIT_SHA"
echo ""
echo "Actions:"
echo " 1. Re-run CI: $SERVER_URL/$REPO/actions"
echo " 2. Revert PR: $PR_URL (click 'Revert' button)"
echo ""
echo "========================================="
if [ -n "$WEBHOOK_URL" ]; then
message="CI 部署失败\n\nPR: #$PR_NUMBER - $PR_TITLE\n分支: $SOURCE_BRANCH\n提交者: $AUTHOR\n\n请检查并决定:\n- 重试 CI\n- 回滚合并"
payload=$(cat <<EOF
{
"msgtype": "text",
"text": {
"content": "$message"
}
}
EOF
)
curl -sS -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$payload" || echo "Warning: Failed to send notification"
echo "OK: Notification sent"
else
echo "Note: Set vars.NOTIFY_WEBHOOK_URL to enable webhook notifications"
fi

5
.gitignore vendored
View File

@@ -1,7 +1,12 @@
/config /config
/data /data
/docs
/logs /logs
/target /target
/temp
/.claude
/CLAUDE.md
/AGENTS.md
multilingualField/ multilingualField/
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar

2
.idea/.gitignore generated vendored
View File

@@ -1,3 +1,5 @@
# Default ignored files # Default ignored files
/shelf/ /shelf/
/workspace.xml /workspace.xml
CopilotChatHistory.xml
developer-tools.xml

131
pom.xml
View File

@@ -5,30 +5,88 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version> <version>3.5.11</version>
<relativePath/> <relativePath/>
</parent> </parent>
<groupId>com.imyeyu.timiserverapi</groupId> <groupId>com.imyeyu.timiserverapi</groupId>
<artifactId>TimiServerAPI</artifactId> <artifactId>TimiServerAPI</artifactId>
<version>1.0.0</version> <version>1.0.1</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>TimiServerAPI</name> <name>TimiServerAPI</name>
<description>imyeyu.com API</description> <description>imyeyu.com API</description>
<properties> <properties>
<springboot.version>3.4.0</springboot.version> <springboot.version>3.5.11</springboot.version>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.multilingualField>21</maven.compiler.multilingualField> <maven.compiler.multilingualField>21</maven.compiler.multilingualField>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<repositories> <profiles>
<repository> <profile>
<id>apache-maven</id> <id>dev-windows</id>
<url>https://repo.maven.apache.org/maven2/</url> <activation>
</repository> <activeByDefault>true</activeByDefault>
</repositories> </activation>
<properties>
<native.classifier>windows-x86_64</native.classifier>
</properties>
<dependencies>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>7.1.1-1.5.12</version>
<classifier>${native.classifier}</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.11.0-1.5.12</version>
<classifier>${native.classifier}</classifier>
</dependency>
</dependencies>
</profile>
<profile>
<id>prod-linux</id>
<dependencies>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>7.1.1-1.5.12</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.11.0-1.5.12</version>
<classifier>linux-x86_64</classifier>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<classifier>windows-x86_64</classifier>
</exclude>
<exclude>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<classifier>windows-x86_64</classifier>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build> <build>
<defaultGoal>compile</defaultGoal> <defaultGoal>compile</defaultGoal>
@@ -46,28 +104,44 @@
</executions> </executions>
<configuration> <configuration>
<excludeDevtools>true</excludeDevtools> <excludeDevtools>true</excludeDevtools>
<mainClass>com.imyeyu.server.TimiServerAPI</mainClass> <mainClass>com.imyeyu.api.TimiServerAPI</mainClass>
<finalName>${project.artifactId}</finalName>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<repositories>
<repository>
<id>apache-maven</id>
<url>https://repo.maven.apache.org/maven2/</url>
</repository>
<repository>
<id>timi_nexus</id>
<url>https://nexus.imyeyu.com/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.imyeyu.spring</groupId> <groupId>com.imyeyu.spring</groupId>
<artifactId>timi-spring</artifactId> <artifactId>timi-spring</artifactId>
<version>0.0.1</version> <version>0.0.10</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.imyeyu.network</groupId> <groupId>com.imyeyu.network</groupId>
<artifactId>timi-network</artifactId> <artifactId>timi-network</artifactId>
<version>0.0.1</version> <version>0.0.8</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.imyeyu.lang</groupId> <groupId>com.imyeyu.lang</groupId>
<artifactId>timi-lang</artifactId> <artifactId>timi-lang</artifactId>
<version>0.0.1</version> <version>0.0.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -119,12 +193,12 @@
<dependency> <dependency>
<groupId>org.eclipse.jgit</groupId> <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId> <artifactId>org.eclipse.jgit</artifactId>
<version>6.7.0.202309050840-r</version> <version>7.2.1.202505142326-r</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jgit</groupId> <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.archive</artifactId> <artifactId>org.eclipse.jgit.archive</artifactId>
<version>6.7.0.202309050840-r</version> <version>7.2.1.202505142326-r</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
@@ -155,7 +229,7 @@
<dependency> <dependency>
<groupId>org.apache.tika</groupId> <groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId> <artifactId>tika-core</artifactId>
<version>2.9.2</version> <version>3.2.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jcodec</groupId> <groupId>org.jcodec</groupId>
@@ -167,6 +241,23 @@
<artifactId>jcodec-javase</artifactId> <artifactId>jcodec-javase</artifactId>
<version>0.2.5</version> <version>0.2.5</version>
</dependency> </dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.5.12</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>7.1.1-1.5.12</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.11.0-1.5.12</version>
<scope>provided</scope>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency> <dependency>
<groupId>org.jsoup</groupId> <groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId> <artifactId>jsoup</artifactId>
@@ -177,5 +268,11 @@
<artifactId>commons-codec</artifactId> <artifactId>commons-codec</artifactId>
<version>1.17.0</version> <version>1.17.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.9.0</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,10 +1,6 @@
package com.imyeyu.server; package com.imyeyu.api;
import com.imyeyu.io.IO; import com.imyeyu.io.IO;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.Language;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.spring.TimiSpring;
import com.imyeyu.utils.OS; import com.imyeyu.utils.OS;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -13,7 +9,6 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.io.File; import java.io.File;
@@ -27,12 +22,10 @@ import java.io.File;
* @since 2021-02-23 21:35 * @since 2021-02-23 21:35
*/ */
@Slf4j @Slf4j
@SpringBootApplication(scanBasePackages = {"com.imyeyu.server", "com.imyeyu.spring"}) @SpringBootApplication(scanBasePackages = {"com.imyeyu.api", "com.imyeyu.spring"})
@EnableTransactionManagement @EnableTransactionManagement
public class TimiServerAPI implements OS.FileSystem, ApplicationContextAware { public class TimiServerAPI implements OS.FileSystem, ApplicationContextAware {
private static final String DEV_LANG_CONFIG = "dev.lang";
public static ApplicationContext applicationContext; public static ApplicationContext applicationContext;
@Override @Override
@@ -40,32 +33,18 @@ public class TimiServerAPI implements OS.FileSystem, ApplicationContextAware {
TimiServerAPI.applicationContext = applicationContext; TimiServerAPI.applicationContext = applicationContext;
} }
public static Language getUserLanguage() {
Language userLanguage = TimiSpring.getLanguage();
Environment env = applicationContext.getBean(Environment.class);
if (env.containsProperty(DEV_LANG_CONFIG)) {
String property = env.getProperty(DEV_LANG_CONFIG);
if (TimiJava.isNotEmpty(property)) {
userLanguage = Ref.toType(Language.class, property);
}
}
return userLanguage;
}
public static void main(String[] args) { public static void main(String[] args) {
try { try {
{ {
// 导出配置 File application = new File("config" + SEP + "application.yml");
String[] files = {"application.yml", "logback.xml"}; if (!application.exists()) {
for (int i = 0; i < files.length; i++) { IO.resourceToDisk(TimiServerAPI.class, "application_export.yml", application.getAbsolutePath());
File file = new File("config" + SEP + files[i]); }
if (!file.exists() || !file.isFile()) { File logback = new File("config" + SEP + "logback.xml");
log.info("exporting default config at {}", file.getAbsolutePath()); if (!logback.exists()) {
IO.resourceToDisk(TimiServerAPI.class, files[i], file.getAbsolutePath()); IO.resourceToDisk(TimiServerAPI.class, "logback.xml", logback.getAbsolutePath());
} }
} }
}
// 启动 SpringBoot // 启动 SpringBoot
SpringApplication.run(TimiServerAPI.class, args); SpringApplication.run(TimiServerAPI.class, args);
} catch (Exception e) { } catch (Exception e) {

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.annotation; package com.imyeyu.api.annotation;
import com.imyeyu.server.bean.CaptchaFrom; import com.imyeyu.api.bean.CaptchaFrom;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;

View File

@@ -1,8 +1,8 @@
package com.imyeyu.server.annotation; package com.imyeyu.api.annotation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import com.imyeyu.server.bean.CaptchaFrom; import com.imyeyu.api.bean.CaptchaFrom;
import com.imyeyu.server.util.CaptchaManager; import com.imyeyu.api.util.CaptchaManager;
import com.imyeyu.spring.bean.CaptchaData; import com.imyeyu.spring.bean.CaptchaData;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
@@ -33,7 +33,7 @@ public class CaptchaValidInterceptor {
private CaptchaManager captchaManager; private CaptchaManager captchaManager;
/** 注入注解 */ /** 注入注解 */
@Pointcut("@annotation(com.imyeyu.server.annotation.CaptchaValid)") @Pointcut("@annotation(com.imyeyu.api.annotation.CaptchaValid)")
public void captchaPointCut() { public void captchaPointCut() {
} }

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.annotation; package com.imyeyu.api.annotation;
import com.imyeyu.server.modules.common.bean.SettingKey; import com.imyeyu.api.modules.common.bean.SettingKey;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;

View File

@@ -1,11 +1,11 @@
package com.imyeyu.server.annotation; package com.imyeyu.api.annotation;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import com.imyeyu.java.bean.timi.TimiCode; import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.server.modules.common.service.SettingService; import com.imyeyu.api.modules.common.service.SettingService;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.annotation; package com.imyeyu.api.annotation;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;

View File

@@ -1,11 +1,11 @@
package com.imyeyu.server.annotation; package com.imyeyu.api.annotation;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import com.imyeyu.java.bean.timi.TimiCode; import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.server.modules.blog.util.UserToken; import com.imyeyu.api.modules.blog.util.UserToken;
import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.TimiSpring;
import com.imyeyu.spring.annotation.RequiredToken; import com.imyeyu.spring.annotation.RequiredToken;
import com.imyeyu.spring.annotation.RequiredTokenAbstractInterceptor; import com.imyeyu.spring.annotation.RequiredTokenAbstractInterceptor;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.bean; package com.imyeyu.api.bean;
/** /**
* 验证码来源 * 验证码来源

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.bean; package com.imyeyu.api.bean;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;

View File

@@ -0,0 +1,29 @@
package com.imyeyu.api.bean;
import com.imyeyu.spring.bean.Page;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author 夜雨
* @since 2025-12-12 23:07
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class PreviewPage<T> extends Page<T> {
/**
*
*
* @author 夜雨
* @since 2025-12-12 23:09
*/
public enum Type {
NORMAL,
PREVIEW
}
protected Type type;
}

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.bean; package com.imyeyu.api.bean;
import lombok.Data; import lombok.Data;
import com.imyeyu.utils.OS; import com.imyeyu.utils.OS;

View File

@@ -0,0 +1,15 @@
package com.imyeyu.api.bean.wechat;
import lombok.Data;
/**
* @author 夜雨
* @since 2025-09-27 11:33
*/
@Data
public class InitCodeResponse {
private String session_key;
private String openid;
}

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.config; package com.imyeyu.api.config;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;

View File

@@ -0,0 +1,31 @@
package com.imyeyu.api.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.imyeyu.utils.Time;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.yaml.snakeyaml.Yaml;
/**
* @author 夜雨
* @since 2025-05-16 18:53
*/
@Configuration
public class BeanConfig {
@Bean
public ObjectMapper jackson() {
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(Time.dateTime);
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return mapper;
}
@Bean
public Yaml yaml() {
return new Yaml();
}
}

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.config; package com.imyeyu.api.config;
import jakarta.servlet.Filter; import jakarta.servlet.Filter;
import lombok.Data; import lombok.Data;
@@ -29,21 +29,12 @@ public class CORSConfig {
/** 允许跨域的地址 */ /** 允许跨域的地址 */
private String[] allowOrigin; private String[] allowOrigin;
/** 是否允许请求带有验证信息 */
private boolean allowCredentials;
/** 允许请求的方法 */
private String allowMethods;
/** 允许服务端访问的客户端请求头 */
private String allowHeaders;
@Bean @Bean
public FilterRegistrationBean<Filter> corsFilter() { public FilterRegistrationBean<Filter> corsFilter() {
CorsConfiguration config = new CorsConfiguration(); CorsConfiguration config = new CorsConfiguration();
config.addAllowedHeader(allowHeaders); config.addAllowedHeader("*");
config.addAllowedMethod(allowMethods); config.addAllowedMethod("*");
config.setAllowCredentials(allowCredentials); config.setAllowCredentials(true);
config.setAllowedOriginPatterns(Arrays.asList(allowOrigin)); config.setAllowedOriginPatterns(Arrays.asList(allowOrigin));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.config; package com.imyeyu.api.config;
import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClient;
import com.mongodb.client.gridfs.GridFSBucket; import com.mongodb.client.gridfs.GridFSBucket;

View File

@@ -1,13 +1,17 @@
package com.imyeyu.server.config; package com.imyeyu.api.config;
import lombok.Data; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.EqualsAndHashCode; import com.imyeyu.api.modules.blog.entity.ArticleRanking;
import com.imyeyu.server.modules.blog.entity.ArticleRanking; import com.imyeyu.api.modules.common.entity.Multilingual;
import com.imyeyu.server.modules.common.entity.Multilingual;
import com.imyeyu.spring.bean.RedisConfigParams; import com.imyeyu.spring.bean.RedisConfigParams;
import com.imyeyu.spring.config.AbstractRedisConfig; import com.imyeyu.spring.config.AbstractRedisConfig;
import com.imyeyu.spring.util.Redis; import com.imyeyu.spring.util.Redis;
import com.imyeyu.spring.util.RedisSerializers; import com.imyeyu.spring.util.RedisSerializers;
import com.imyeyu.utils.Time;
import io.lettuce.core.api.StatefulConnection;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -32,9 +36,12 @@ import java.time.Duration;
@Configuration @Configuration
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@EnableAutoConfiguration @EnableAutoConfiguration
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "spring.redis") @ConfigurationProperties(prefix = "spring.redis")
public class RedisConfig extends AbstractRedisConfig { public class RedisConfig extends AbstractRedisConfig {
private final ObjectMapper jackson;
// ---------- 连接配置 ---------- // ---------- 连接配置 ----------
/** 地址 */ /** 地址 */
@@ -46,50 +53,9 @@ public class RedisConfig extends AbstractRedisConfig {
/** 密码 */ /** 密码 */
private String password; private String password;
/** 超时(毫秒) */
private int timeout;
/** 连接池 */
private Lettuce lettuce;
/** 数据库 */ /** 数据库 */
private Database database; private Database database;
/**
* 连接池
*
* @author 夜雨
* @since 2023-08-21 16:23
*/
@Data
public static class Lettuce {
/** 配置 */
private Pool pool;
/**
* 配置
*
* @author 夜雨
* @since 2023-08-21 16:23
*/
@Data
public static class Pool {
/** 最大活跃连接 */
private int maxActive;
/** 最小空闲连接 */
private int minIdle;
/** 最大空闲连接 */
private int maxIdle;
/** 最大等待时间(秒) */
private int maxWait;
}
}
/** /**
* 数据库 * 数据库
* *
@@ -134,6 +100,9 @@ public class RedisConfig extends AbstractRedisConfig {
/** Minecraft 登录 */ /** Minecraft 登录 */
private int fmcPlayerToken; private int fmcPlayerToken;
/** 共享剪切板 */
private int clipboard;
} }
@Override @Override
@@ -142,22 +111,23 @@ public class RedisConfig extends AbstractRedisConfig {
setHost(host); setHost(host);
setPort(port); setPort(port);
setPassword(password); setPassword(password);
setTimeout(timeout); setTimeout(Time.SI * 8);
setMaxActive(lettuce.pool.maxActive); setMaxActive(8);
setMinIdle(lettuce.pool.minIdle); setMinIdle(1);
setMaxIdle(lettuce.pool.maxIdle); setMaxIdle(8);
}}; }};
} }
/** @return 连接池配置 */ /** @return 连接池配置 */
@Bean @Bean
@Override @Override
public GenericObjectPoolConfig<?> getPoolConfig() { public GenericObjectPoolConfig<StatefulConnection<?, ?>> getPoolConfig() {
GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>(); RedisConfigParams configArgs = configParams();
config.setMaxTotal(lettuce.pool.maxActive); GenericObjectPoolConfig<StatefulConnection<?, ?>> config = new GenericObjectPoolConfig<>();
config.setMinIdle(lettuce.pool.minIdle); config.setMaxTotal(8);
config.setMaxIdle(lettuce.pool.maxIdle); config.setMinIdle(configArgs.getMinIdle());
config.setMaxWait(Duration.ofMillis(lettuce.pool.maxWait)); config.setMaxIdle(configArgs.getMaxIdle());
config.setMaxWait(Duration.ofMillis(-1));
return config; return config;
} }
@@ -221,7 +191,7 @@ public class RedisConfig extends AbstractRedisConfig {
/** @return 文章访问统计,文章 ID: {@link ArticleRanking}(JSON) */ /** @return 文章访问统计,文章 ID: {@link ArticleRanking}(JSON) */
@Bean("redisArticleRanking") @Bean("redisArticleRanking")
public Redis<Long, ArticleRanking> getArticleRankingRedisTemplate() { public Redis<Long, ArticleRanking> getArticleRankingRedisTemplate() {
return getRedis(database.articleRanking, RedisSerializers.LONG, RedisSerializers.gsonSerializer(ArticleRanking.class)); return getRedis(database.articleRanking, RedisSerializers.LONG, RedisSerializers.jacksonSerializer(jackson, ArticleRanking.class));
} }
/** @return 文章访问记录IP: [文章 ID] */ /** @return 文章访问记录IP: [文章 ID] */
@@ -265,4 +235,10 @@ public class RedisConfig extends AbstractRedisConfig {
public Redis<String, Long> getMCPlayerLoginRedisTemplate() { public Redis<String, Long> getMCPlayerLoginRedisTemplate() {
return getRedis(database.fmcPlayerToken, RedisSerializers.STRING, RedisSerializers.LONG); return getRedis(database.fmcPlayerToken, RedisSerializers.STRING, RedisSerializers.LONG);
} }
/** @return 共享剪切板,会话 ID: 内容 */
@Bean("redisClipboard")
public Redis<String, String> getClipboardRedisTemplate() {
return getRedis(database.clipboard, RedisSerializers.STRING, RedisSerializers.STRING);
}
} }

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.config; package com.imyeyu.api.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;

View File

@@ -0,0 +1,35 @@
package com.imyeyu.api.config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置
*
* @author 夜雨
* @since 2023-08-21 16:31
*/
@Data
@Slf4j
@Configuration
public class ThreadPoolConfig {
@Bean(name = "threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(16);
executor.setMaxPoolSize(32);
executor.setQueueCapacity(16);
executor.setKeepAliveSeconds(60);
executor.setAwaitTerminationSeconds(60);
executor.setThreadNamePrefix("thread-pool-task-executor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}

View File

@@ -0,0 +1,87 @@
package com.imyeyu.api.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.imyeyu.api.annotation.EnableSettingInterceptor;
import com.imyeyu.api.annotation.RequestRateLimitInterceptor;
import com.imyeyu.api.annotation.RequiredTokenInterceptor;
import com.imyeyu.api.modules.journal.util.JournalAPIInterceptor;
import com.imyeyu.api.modules.minecraft.annotation.RequiredFMCServerTokenInterceptor;
import com.imyeyu.api.modules.system.util.SystemAPIInterceptor;
import com.imyeyu.spring.annotation.RequestBodyValueArgumentResolver;
import com.imyeyu.utils.Time;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* 系统配置
*
* @author 夜雨
* @since 2021-07-20 16:44
*/
@EnableWebMvc
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final ObjectMapper jackson;
private final SystemAPIInterceptor systemAPIInterceptor;
private final JournalAPIInterceptor journalAPIInterceptor;
private final RequiredTokenInterceptor requiredTokenInterceptor;
private final EnableSettingInterceptor enableSettingInterceptor;
private final RequestRateLimitInterceptor requestRateLimitInterceptor;
private final RequiredFMCServerTokenInterceptor requiredFMCServerTokenInterceptor;
/**
* 过滤器
*
* @param registry 注册表
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(systemAPIInterceptor).addPathPatterns(SystemAPIInterceptor.PATH);
registry.addInterceptor(journalAPIInterceptor).addPathPatterns(JournalAPIInterceptor.PATH);
registry.addInterceptor(requiredFMCServerTokenInterceptor).addPathPatterns("/fmc/server/**");
registry.addInterceptor(requiredTokenInterceptor).addPathPatterns("/**");
registry.addInterceptor(enableSettingInterceptor).addPathPatterns("/**");
registry.addInterceptor(requestRateLimitInterceptor).addPathPatterns("/**");
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new RequestBodyValueArgumentResolver(jackson));
}
/**
* 通信消息转换
*
* @param converters 转换器
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
JsonMapper jsonMapper = JsonMapper.builder()
// 设置默认属性包含规则:忽略 null 值
.serializationInclusion(JsonInclude.Include.NON_NULL)
// 日期格式化
.defaultDateFormat(Time.dateTime)
// 忽略不存在字段
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
// 启用默认视图包含
.enable(MapperFeature.DEFAULT_VIEW_INCLUSION).build();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(jsonMapper);
converter.setDefaultCharset(StandardCharsets.UTF_8);
converters.add(converter);
}
}

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.config.dbsource; package com.imyeyu.api.config.dbsource;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import com.imyeyu.utils.Time; import com.imyeyu.utils.Time;
@@ -24,7 +24,7 @@ import java.sql.SQLException;
* @since 2022-11-29 22:39 * @since 2022-11-29 22:39
*/ */
@Configuration @Configuration
@MapperScan(basePackages = "com.imyeyu.server.modules.forevermc.mapper", sqlSessionFactoryRef = "foreverMCSqlSessionFactory") @MapperScan(basePackages = "com.imyeyu.api.modules.forevermc.mapper", sqlSessionFactoryRef = "foreverMCSqlSessionFactory")
public class ForeverMCDBConfig { public class ForeverMCDBConfig {
public static final String ROLLBACKER = "foreverMCTransactionManager"; public static final String ROLLBACKER = "foreverMCTransactionManager";
@@ -58,7 +58,7 @@ public class ForeverMCDBConfig {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(datasource); bean.setDataSource(datasource);
bean.setTypeAliasesPackage("com.imyeyu.server.modules.forevermc.entity"); bean.setTypeAliasesPackage("com.imyeyu.api.modules.forevermc.entity");
bean.setConfiguration(config); bean.setConfiguration(config);
return bean.getObject(); return bean.getObject();
} }

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.config.dbsource; package com.imyeyu.api.config.dbsource;
import com.imyeyu.utils.Time; import com.imyeyu.utils.Time;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
@@ -31,7 +31,7 @@ import java.util.List;
*/ */
@Configuration @Configuration
@MapperScan(basePackages = { @MapperScan(basePackages = {
"com.imyeyu.server.modules.gitea.mapper", "com.imyeyu.api.modules.gitea.mapper",
}, sqlSessionFactoryRef = "giteaSqlSessionFactory") }, sqlSessionFactoryRef = "giteaSqlSessionFactory")
public class GiteaDBConfig { public class GiteaDBConfig {
@@ -70,15 +70,15 @@ public class GiteaDBConfig {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List<String> mapperLocations = new ArrayList<>(); List<String> mapperLocations = new ArrayList<>();
mapperLocations.add("classpath:mapper/gitea/**/*.xml"); mapperLocations.add("classpath:mapper/gitea/**/*.xml");
for (int i = 0; i < mapperLocations.size(); i++) { for (String mapperLocation : mapperLocations) {
resources.addAll(List.of(resourceResolver.getResources(mapperLocations.get(i)))); resources.addAll(List.of(resourceResolver.getResources(mapperLocation)));
} }
} }
String[] typeAliases = { String[] typeAliases = {
"com.imyeyu.server.modules.gitea.entity", "com.imyeyu.api.modules.gitea.entity",
}; };
String[] typeHandlers = { String[] typeHandlers = {
"com.imyeyu.server.handler" "com.imyeyu.spring.handler"
}; };
SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); SqlSessionFactoryBean bean = new SqlSessionFactoryBean();

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.config.dbsource; package com.imyeyu.api.config.dbsource;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import com.imyeyu.utils.Time; import com.imyeyu.utils.Time;
@@ -31,14 +31,15 @@ import java.util.List;
*/ */
@Configuration @Configuration
@MapperScan(basePackages = { @MapperScan(basePackages = {
"com.imyeyu.server.modules.git.mapper", "com.imyeyu.api.modules.git.mapper",
"com.imyeyu.server.modules.bill.mapper", "com.imyeyu.api.modules.bill.mapper",
"com.imyeyu.server.modules.blog.mapper", "com.imyeyu.api.modules.blog.mapper",
"com.imyeyu.server.modules.lyric.mapper", "com.imyeyu.api.modules.lyric.mapper",
"com.imyeyu.server.modules.mirror.mapper", "com.imyeyu.api.modules.mirror.mapper",
"com.imyeyu.server.modules.system.mapper", "com.imyeyu.api.modules.system.mapper",
"com.imyeyu.server.modules.common.mapper", "com.imyeyu.api.modules.common.mapper",
"com.imyeyu.server.modules.minecraft.mapper" "com.imyeyu.api.modules.journal.mapper",
"com.imyeyu.api.modules.minecraft.mapper"
}, sqlSessionFactoryRef = "timiServerSqlSessionFactory") }, sqlSessionFactoryRef = "timiServerSqlSessionFactory")
public class TimiServerDBConfig { public class TimiServerDBConfig {
@@ -80,23 +81,25 @@ public class TimiServerDBConfig {
mapperLocations.add("classpath:mapper/blog/**/*.xml"); mapperLocations.add("classpath:mapper/blog/**/*.xml");
mapperLocations.add("classpath:mapper/common/**/*.xml"); mapperLocations.add("classpath:mapper/common/**/*.xml");
mapperLocations.add("classpath:mapper/system/**/*.xml"); mapperLocations.add("classpath:mapper/system/**/*.xml");
mapperLocations.add("classpath:mapper/journal/**/*.xml");
mapperLocations.add("classpath:mapper/minecraft/**/*.xml"); mapperLocations.add("classpath:mapper/minecraft/**/*.xml");
for (int i = 0; i < mapperLocations.size(); i++) { for (String mapperLocation : mapperLocations) {
resources.addAll(List.of(resourceResolver.getResources(mapperLocations.get(i)))); resources.addAll(List.of(resourceResolver.getResources(mapperLocation)));
} }
} }
String[] typeAliases = { String[] typeAliases = {
"com.imyeyu.server.modules.git.entity", "com.imyeyu.api.modules.git.entity",
"com.imyeyu.server.modules.bill.entity", "com.imyeyu.api.modules.bill.entity",
"com.imyeyu.server.modules.blog.entity", "com.imyeyu.api.modules.blog.entity",
"com.imyeyu.server.modules.lyric.entity", "com.imyeyu.api.modules.lyric.entity",
"com.imyeyu.server.modules.mirror.entity", "com.imyeyu.api.modules.mirror.entity",
"com.imyeyu.server.modules.system.entity", "com.imyeyu.api.modules.system.entity",
"com.imyeyu.server.modules.common.entity", "com.imyeyu.api.modules.common.entity",
"com.imyeyu.server.modules.minecraft.entity" "com.imyeyu.api.modules.journal.entity",
"com.imyeyu.api.modules.minecraft.entity"
}; };
String[] typeHandlers = { String[] typeHandlers = {
"com.imyeyu.server.handler" "com.imyeyu.spring.handler"
}; };
SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); SqlSessionFactoryBean bean = new SqlSessionFactoryBean();

View File

@@ -1,10 +1,10 @@
package com.imyeyu.server.modules.blog.controller; package com.imyeyu.api.modules.blog.controller;
import com.imyeyu.server.modules.blog.entity.Article; import com.imyeyu.api.modules.blog.entity.Article;
import com.imyeyu.server.modules.blog.entity.ArticleRanking; import com.imyeyu.api.modules.blog.entity.ArticleRanking;
import com.imyeyu.server.modules.blog.service.ArticleService; import com.imyeyu.api.modules.blog.service.ArticleService;
import com.imyeyu.server.modules.blog.vo.article.ArticleView; import com.imyeyu.api.modules.blog.vo.article.ArticleView;
import com.imyeyu.server.modules.blog.vo.article.KeywordPage; import com.imyeyu.api.modules.blog.vo.article.KeywordPage;
import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.Page;

View File

@@ -1,9 +1,9 @@
package com.imyeyu.server.modules.blog.controller; package com.imyeyu.api.modules.blog.controller;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import com.imyeyu.server.modules.blog.entity.Friend; import com.imyeyu.api.modules.blog.entity.Friend;
import com.imyeyu.server.modules.blog.service.FriendService; import com.imyeyu.api.modules.blog.service.FriendService;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;

View File

@@ -1,7 +1,7 @@
package com.imyeyu.server.modules.blog.entity; package com.imyeyu.api.modules.blog.entity;
import com.google.gson.JsonElement; import com.fasterxml.jackson.databind.JsonNode;
import com.imyeyu.server.modules.common.bean.CommentSupport; import com.imyeyu.api.modules.common.bean.CommentSupport;
import com.imyeyu.spring.entity.Destroyable; import com.imyeyu.spring.entity.Destroyable;
import com.imyeyu.spring.entity.Entity; import com.imyeyu.spring.entity.Entity;
import lombok.Data; import lombok.Data;
@@ -25,9 +25,6 @@ public class Article extends Entity implements CommentSupport, Destroyable {
*/ */
public enum Type { public enum Type {
/** 关于 */
ABOUT,
/** 公版 */ /** 公版 */
PUBLIC, PUBLIC,
@@ -44,9 +41,6 @@ public class Article extends Entity implements CommentSupport, Destroyable {
/** 类型 */ /** 类型 */
protected Type type; protected Type type;
/** 分类 ID */
protected long classId;
/** 摘要 */ /** 摘要 */
protected String digest; protected String digest;
@@ -54,7 +48,7 @@ public class Article extends Entity implements CommentSupport, Destroyable {
protected String data; protected String data;
/** 扩展数据 */ /** 扩展数据 */
protected JsonElement extendData; protected JsonNode extendData;
/** 阅读数量 */ /** 阅读数量 */
protected int reads; protected int reads;
@@ -62,7 +56,7 @@ public class Article extends Entity implements CommentSupport, Destroyable {
/** 喜欢数量 */ /** 喜欢数量 */
protected int likes; protected int likes;
/** 是否显示评论 */ /** true 为显示评论 */
protected boolean showComment; protected boolean showComment;
/** true 为可评论 */ /** true 为可评论 */
@@ -71,6 +65,9 @@ public class Article extends Entity implements CommentSupport, Destroyable {
/** true 为可排位 */ /** true 为可排位 */
protected boolean canRanking; protected boolean canRanking;
/** true 为可通过列表查询 */
protected boolean canList;
/** @return true 为可评论 */ /** @return true 为可评论 */
@Override @Override
public boolean canComment() { public boolean canComment() {

View File

@@ -1,8 +1,9 @@
package com.imyeyu.server.modules.blog.entity; package com.imyeyu.api.modules.blog.entity;
import com.imyeyu.spring.entity.Entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import com.imyeyu.spring.entity.Entity; import lombok.NoArgsConstructor;
/** /**
* 访问排行每周 * 访问排行每周
@@ -12,6 +13,7 @@ import com.imyeyu.spring.entity.Entity;
* @since 2021-03-01 17:10 * @since 2021-03-01 17:10
*/ */
@Data @Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ArticleRanking extends Entity { public class ArticleRanking extends Entity {

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.blog.entity; package com.imyeyu.api.modules.blog.entity;
import com.imyeyu.server.modules.common.vo.comment.CommentReplyView; import com.imyeyu.api.modules.common.entity.CommentReply;
import com.imyeyu.spring.annotation.table.AutoUUID; import com.imyeyu.spring.annotation.table.AutoUUID;
import com.imyeyu.spring.annotation.table.Id; import com.imyeyu.spring.annotation.table.Id;
import lombok.Data; import lombok.Data;
@@ -32,5 +32,5 @@ public class CommentRemindQueue {
private Long replyId; private Long replyId;
private CommentReplyView reply; private CommentReply reply;
} }

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.blog.entity; package com.imyeyu.api.modules.blog.entity;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;

View File

@@ -1,6 +1,7 @@
package com.imyeyu.server.modules.blog.mapper; package com.imyeyu.api.modules.blog.mapper;
import com.imyeyu.server.modules.blog.entity.Article; import com.imyeyu.api.modules.blog.entity.Article;
import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
@@ -14,9 +15,15 @@ import java.util.List;
*/ */
public interface ArticleMapper extends BaseMapper<Article, Long> { public interface ArticleMapper extends BaseMapper<Article, Long> {
@Override
long countByPage(Page page);
@Override
List<Article> selectByPage(Page page);
long countByKeyword(String keyword); long countByKeyword(String keyword);
List<Article> selectByKeyword(String keyword, Long offset, int limit); List<Article> selectByKeyword(String keyword, Long offset, long limit);
@Select("UPDATE `article` SET `likes` = `likes` + 1 WHERE `id` = #{articleId}") @Select("UPDATE `article` SET `likes` = `likes` + 1 WHERE `id` = #{articleId}")
void like(Long articleId); void like(Long articleId);

View File

@@ -1,7 +1,7 @@
package com.imyeyu.server.modules.blog.mapper; package com.imyeyu.api.modules.blog.mapper;
import com.imyeyu.server.modules.blog.entity.Friend; import com.imyeyu.api.modules.blog.entity.Friend;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;

View File

@@ -1,10 +1,10 @@
package com.imyeyu.server.modules.blog.service; package com.imyeyu.api.modules.blog.service;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.server.modules.blog.entity.Article; import com.imyeyu.api.modules.blog.entity.Article;
import com.imyeyu.server.modules.blog.entity.ArticleRanking; import com.imyeyu.api.modules.blog.entity.ArticleRanking;
import com.imyeyu.server.modules.blog.vo.article.ArticleView; import com.imyeyu.api.modules.blog.vo.article.ArticleView;
import com.imyeyu.server.modules.blog.vo.article.KeywordPage; import com.imyeyu.api.modules.blog.vo.article.KeywordPage;
import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.bean.PageResult;
import com.imyeyu.spring.service.GettableService; import com.imyeyu.spring.service.GettableService;
import com.imyeyu.spring.service.PageableService; import com.imyeyu.spring.service.PageableService;

View File

@@ -1,7 +1,7 @@
package com.imyeyu.server.modules.blog.service; package com.imyeyu.api.modules.blog.service;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.server.modules.blog.entity.CommentRemindQueue; import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
import com.imyeyu.spring.service.CreatableService; import com.imyeyu.spring.service.CreatableService;
import java.util.List; import java.util.List;

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.blog.service; package com.imyeyu.api.modules.blog.service;
import com.imyeyu.server.modules.blog.entity.Friend; import com.imyeyu.api.modules.blog.entity.Friend;
import java.util.List; import java.util.List;

View File

@@ -1,20 +1,21 @@
package com.imyeyu.server.modules.blog.service.implement; package com.imyeyu.api.modules.blog.service.implement;
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.api.modules.blog.entity.Article;
import com.imyeyu.api.modules.blog.entity.ArticleRanking;
import com.imyeyu.api.modules.blog.mapper.ArticleMapper;
import com.imyeyu.api.modules.blog.service.ArticleService;
import com.imyeyu.api.modules.blog.vo.article.ArticleView;
import com.imyeyu.api.modules.blog.vo.article.KeywordPage;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.entity.Comment;
import com.imyeyu.api.modules.common.entity.Tag;
import com.imyeyu.api.modules.common.mapper.CommentMapper;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.common.service.TagService;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.server.config.dbsource.TimiServerDBConfig;
import com.imyeyu.server.modules.blog.entity.Article;
import com.imyeyu.server.modules.blog.entity.ArticleRanking;
import com.imyeyu.server.modules.blog.mapper.ArticleMapper;
import com.imyeyu.server.modules.blog.service.ArticleService;
import com.imyeyu.server.modules.blog.vo.article.ArticleView;
import com.imyeyu.server.modules.blog.vo.article.KeywordPage;
import com.imyeyu.server.modules.common.entity.Attachment;
import com.imyeyu.server.modules.common.entity.Comment;
import com.imyeyu.server.modules.common.entity.Tag;
import com.imyeyu.server.modules.common.mapper.CommentMapper;
import com.imyeyu.server.modules.common.service.AttachmentService;
import com.imyeyu.server.modules.common.service.TagService;
import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.TimiSpring;
import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.bean.PageResult;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.spring.service.AbstractEntityService; import com.imyeyu.spring.service.AbstractEntityService;
@@ -54,6 +55,14 @@ public class ArticleServiceImplement extends AbstractEntityService<Article, Long
return mapper; return mapper;
} }
@Override
public PageResult<Article> page(Page page) {
PageResult<Article> result = new PageResult<>();
result.setList(mapper.selectByPage(page));
result.setTotal(mapper.countByPage(page));
return result;
}
@Transactional(TimiServerDBConfig.ROLLBACKER) @Transactional(TimiServerDBConfig.ROLLBACKER)
@Override @Override
public ArticleView view(long id) { public ArticleView view(long id) {

View File

@@ -1,9 +1,9 @@
package com.imyeyu.server.modules.blog.service.implement; package com.imyeyu.api.modules.blog.service.implement;
import com.imyeyu.server.config.dbsource.TimiServerDBConfig; import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.server.modules.blog.entity.CommentRemindQueue; import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
import com.imyeyu.server.modules.blog.service.CommentRemindQueueService; import com.imyeyu.api.modules.blog.service.CommentRemindQueueService;
import com.imyeyu.server.modules.common.mapper.CommentRemindQueueMapper; import com.imyeyu.api.modules.common.mapper.CommentRemindQueueMapper;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.spring.service.AbstractEntityService; import com.imyeyu.spring.service.AbstractEntityService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -37,12 +37,16 @@ public class CommentRemindQueueServiceImplement extends AbstractEntityService<Co
@Transactional(TimiServerDBConfig.ROLLBACKER) @Transactional(TimiServerDBConfig.ROLLBACKER)
@Override @Override
public void destroyByUserId(Long userId) { public void destroyByUserId(Long userId) {
mapper.destroyByUserId(userId); CommentRemindQueue example = new CommentRemindQueue();
example.setUserId(userId);
mapper.deleteAllByExample(example);
} }
@Transactional(TimiServerDBConfig.ROLLBACKER) @Transactional(TimiServerDBConfig.ROLLBACKER)
@Override @Override
public void destroyByReplyId(Long replyId) { public void destroyByReplyId(Long replyId) {
mapper.destroyByReplyId(replyId); CommentRemindQueue example = new CommentRemindQueue();
example.setReplyId(replyId);
mapper.destroyAllByExample(example);
} }
} }

View File

@@ -1,9 +1,9 @@
package com.imyeyu.server.modules.blog.service.implement; package com.imyeyu.api.modules.blog.service.implement;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import com.imyeyu.server.modules.blog.entity.Friend; import com.imyeyu.api.modules.blog.entity.Friend;
import com.imyeyu.server.modules.blog.mapper.FriendMapper; import com.imyeyu.api.modules.blog.mapper.FriendMapper;
import com.imyeyu.server.modules.blog.service.FriendService; import com.imyeyu.api.modules.blog.service.FriendService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;

View File

@@ -1,13 +1,12 @@
package com.imyeyu.server.modules.blog.util; package com.imyeyu.api.modules.blog.util;
import com.imyeyu.java.TimiJava; import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.server.config.RedisConfig; import com.imyeyu.api.config.RedisConfig;
import com.imyeyu.server.modules.common.bean.SettingKey; import com.imyeyu.api.modules.common.bean.SettingKey;
import com.imyeyu.server.modules.common.entity.User; import com.imyeyu.api.modules.common.entity.User;
import com.imyeyu.server.modules.common.service.SettingService; import com.imyeyu.api.modules.common.service.SettingService;
import com.imyeyu.server.modules.common.service.UserService; import com.imyeyu.api.modules.common.service.UserService;
import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.TimiSpring;
import com.imyeyu.spring.util.Redis; import com.imyeyu.spring.util.Redis;
import com.imyeyu.spring.util.RedisSerializers; import com.imyeyu.spring.util.RedisSerializers;

View File

@@ -1,8 +1,8 @@
package com.imyeyu.server.modules.blog.vo.article; package com.imyeyu.api.modules.blog.vo.article;
import com.imyeyu.server.modules.blog.entity.Article; import com.imyeyu.api.modules.blog.entity.Article;
import com.imyeyu.server.modules.common.entity.Attachment; import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.server.modules.common.entity.Tag; import com.imyeyu.api.modules.common.entity.Tag;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.blog.vo.article; package com.imyeyu.api.modules.blog.vo.article;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.blog.vo.article; package com.imyeyu.api.modules.blog.vo.article;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.blog.vo.article; package com.imyeyu.api.modules.blog.vo.article;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.bean; package com.imyeyu.api.modules.common.bean;
/** /**
* 支持评论的实体 * 支持评论的实体

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.bean; package com.imyeyu.api.modules.common.bean;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.bean; package com.imyeyu.api.modules.common.bean;
/** /**
* *

View File

@@ -0,0 +1,19 @@
package com.imyeyu.api.modules.common.bean;
/**
* @author 夜雨
* @since 2025-10-20 15:04
*/
public class MediaAttach {
/**
* @author 夜雨
* @since 2025-09-28 02:01
*/
public enum Type {
SOURCE,
THUMB
}
}

View File

@@ -0,0 +1,42 @@
package com.imyeyu.api.modules.common.bean;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author 夜雨
* @since 2025-12-11 18:14
*/
public class Metadata {
/**
* 图片
*
* @author 夜雨
* @since 2025-12-11 18:15
*/
@Data
public static class Image {
private int width;
private int height;
}
/**
* 缩略图
*
* @author 夜雨
* @since 2026-01-04 18:10
*/
@Data
@EqualsAndHashCode(callSuper = true)
public static class ThumbImage extends Image {
private long sourceId;
private String sourceMongoId;
private String sourceMimeType;
}
}

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.bean; package com.imyeyu.api.modules.common.bean;
/** /**
* 系统设置 * 系统设置
@@ -32,6 +32,8 @@ public enum SettingKey {
/** 启用灰色滤镜 */ /** 启用灰色滤镜 */
ENABLE_GRAY_FILTER, ENABLE_GRAY_FILTER,
TEMP_FILE_PATH,
// ---------- ICP 备案号 ---------- // ---------- ICP 备案号 ----------
ICP_IMYEYU_COM, ICP_IMYEYU_COM,
@@ -111,6 +113,8 @@ public enum SettingKey {
GIT_API, GIT_API,
GIT_ABOUT_ARTICLE,
GIT_REPO_PATH, GIT_REPO_PATH,
// ---------- 远程音乐 ---------- // ---------- 远程音乐 ----------
@@ -127,6 +131,35 @@ public enum SettingKey {
MUSIC_CONTROLLER_URI, MUSIC_CONTROLLER_URI,
// ---------- 日记 ----------
JOURNAL_KEY,
JOURNAL_APP_ID,
JOURNAL_APP_SECRET,
JOURNAL_MEMO,
JOURNAL_OPEN_ID_WHITE_LIST,
// ---------- 临时文件 ----------
/** 临时文件最小缓存时间 */
TEMP_FILE_TTL_MIN,
/** 临时文件最长缓存时间 */
TEMP_FILE_TTL_MAX,
/** 临时文件默认缓存时间 */
TEMP_FILE_TTL_DEFAULT,
/** 已过期的临时文件保留时间 */
TEMP_FILE_RESIDUE_TIME,
/** 每个 IP 限制有效临时文件容量 */
TEMP_FILE_LIMIT,
// ---------- 系统 ---------- // ---------- 系统 ----------
SYSTEM_FILE_BASE, SYSTEM_FILE_BASE,

View File

@@ -1,20 +1,17 @@
package com.imyeyu.server.modules.common.controller; package com.imyeyu.api.modules.common.controller;
import com.imyeyu.server.annotation.CaptchaValid; import com.imyeyu.api.annotation.CaptchaValid;
import com.imyeyu.server.annotation.EnableSetting; import com.imyeyu.api.annotation.EnableSetting;
import com.imyeyu.server.bean.CaptchaFrom; import com.imyeyu.api.bean.CaptchaFrom;
import com.imyeyu.server.modules.common.bean.SettingKey; import com.imyeyu.api.modules.common.bean.SettingKey;
import com.imyeyu.server.modules.common.entity.Comment; import com.imyeyu.api.modules.common.entity.Comment;
import com.imyeyu.server.modules.common.entity.CommentReply; import com.imyeyu.api.modules.common.entity.CommentReply;
import com.imyeyu.server.modules.common.service.CommentReplyService; import com.imyeyu.api.modules.common.service.CommentReplyService;
import com.imyeyu.server.modules.common.service.CommentService; import com.imyeyu.api.modules.common.service.CommentService;
import com.imyeyu.server.modules.common.vo.comment.CommentReplyPage;
import com.imyeyu.server.modules.common.vo.comment.CommentReplyView;
import com.imyeyu.server.modules.common.vo.comment.CommentView;
import com.imyeyu.server.modules.git.vo.issue.CommentPage;
import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.bean.CaptchaData; import com.imyeyu.spring.bean.CaptchaData;
import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.bean.PageResult;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -51,8 +48,8 @@ public class CommentController {
@RequestRateLimit @RequestRateLimit
@PostMapping("/list") @PostMapping("/list")
public PageResult<CommentView> list(@Valid @RequestBody CommentPage commentPage) { public PageResult<Comment> list(@Valid @RequestBody Page<Comment> page) {
return service.pageByBizId(commentPage); return service.page(page);
} }
/** /**
@@ -77,9 +74,14 @@ public class CommentController {
*/ */
@RequestRateLimit @RequestRateLimit
@RequestMapping("/reply/list") @RequestMapping("/reply/list")
public PageResult<CommentReplyView> pageCommentReplies(@Valid @RequestBody CommentReplyPage page) { public PageResult<CommentReply> pageReplies(@Valid @RequestBody Page<CommentReply> page) {
// 通用接口只允许查询评论的回复 // 通用接口只允许查询评论的回复
page.setBizType(CommentReplyPage.BizType.COMMENT); CommentReply example = new CommentReply();
return replyService.pageByBizType(page); if (page.getEqualsExample() != null) {
example.setCommentId(page.getEqualsExample().getCommentId());
}
page.setEqualsExample(example);
page.setLikesExample(null);
return replyService.page(page);
} }
} }

View File

@@ -1,36 +1,40 @@
package com.imyeyu.server.modules.common.controller; package com.imyeyu.api.modules.common.controller;
import com.google.gson.Gson; import com.fasterxml.jackson.core.type.TypeReference;
import com.google.gson.reflect.TypeToken; import com.fasterxml.jackson.databind.ObjectMapper;
import com.imyeyu.api.bean.CaptchaFrom;
import com.imyeyu.api.modules.common.bean.ImageType;
import com.imyeyu.api.modules.common.bean.SettingKey;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.entity.Setting;
import com.imyeyu.api.modules.common.entity.Task;
import com.imyeyu.api.modules.common.entity.Template;
import com.imyeyu.api.modules.common.entity.Version;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.common.service.ClipboardService;
import com.imyeyu.api.modules.common.service.FeedbackService;
import com.imyeyu.api.modules.common.service.SettingService;
import com.imyeyu.api.modules.common.service.TaskService;
import com.imyeyu.api.modules.common.service.TempFileService;
import com.imyeyu.api.modules.common.service.TemplateService;
import com.imyeyu.api.modules.common.service.VersionService;
import com.imyeyu.api.modules.common.vo.ClipboardRequest;
import com.imyeyu.api.modules.common.vo.FeedbackRequest;
import com.imyeyu.api.modules.common.vo.TempFileResponse;
import com.imyeyu.api.modules.system.util.ResourceHandler;
import com.imyeyu.api.util.CaptchaManager;
import com.imyeyu.io.IO; import com.imyeyu.io.IO;
import com.imyeyu.java.TimiJava; import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode; import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.java.ref.Ref; import com.imyeyu.java.ref.Ref;
import com.imyeyu.network.Network; import com.imyeyu.network.Network;
import com.imyeyu.server.bean.CaptchaFrom;
import com.imyeyu.server.modules.common.bean.ImageType;
import com.imyeyu.server.modules.common.bean.SettingKey;
import com.imyeyu.server.modules.common.entity.Attachment;
import com.imyeyu.server.modules.common.entity.Setting;
import com.imyeyu.server.modules.common.entity.Task;
import com.imyeyu.server.modules.common.entity.Template;
import com.imyeyu.server.modules.common.entity.Version;
import com.imyeyu.server.modules.common.service.AttachmentService;
import com.imyeyu.server.modules.common.service.FeedbackService;
import com.imyeyu.server.modules.common.service.SettingService;
import com.imyeyu.server.modules.common.service.TaskService;
import com.imyeyu.server.modules.common.service.TemplateService;
import com.imyeyu.server.modules.common.service.VersionService;
import com.imyeyu.server.modules.common.vo.FeedbackRequest;
import com.imyeyu.server.modules.common.vo.attachment.AttachmentView;
import com.imyeyu.server.modules.system.util.ResourceHandler;
import com.imyeyu.server.util.CaptchaManager;
import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.TimiSpring;
import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.IgnoreGlobalReturn; import com.imyeyu.spring.annotation.IgnoreGlobalReturn;
import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.bean.CaptchaData; import com.imyeyu.spring.bean.CaptchaData;
import com.imyeyu.spring.bean.RequestRange;
import com.mongodb.client.gridfs.GridFSBucket; import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream; import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile; import com.mongodb.client.gridfs.model.GridFSFile;
@@ -39,9 +43,11 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.tika.Tika; import org.apache.tika.Tika;
import org.springframework.http.MediaType;
import org.springframework.data.mongodb.gridfs.GridFsResource; import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -51,6 +57,8 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -83,20 +91,77 @@ public class CommonController {
private final SettingService settingService; private final SettingService settingService;
private final FeedbackService feedbackService; private final FeedbackService feedbackService;
private final TemplateService templateService; private final TemplateService templateService;
private final TempFileService tempFileService;
private final AttachmentService attachmentService; private final AttachmentService attachmentService;
private final ClipboardService clipboardService;
private final Gson gson; private final ObjectMapper jackson;
private final Yaml yaml; private final Yaml yaml;
private final GridFSBucket gridFSBucket; private final GridFSBucket gridFSBucket;
private final CaptchaManager captchaManager; private final CaptchaManager captchaManager;
private final ResourceHandler resourceHandler; private final ResourceHandler resourceHandler;
private String writeJson(Object value) {
try {
return jackson.writeValueAsString(value);
} catch (IOException e) {
throw new TimiException(TimiCode.ERROR, "write json error", e);
}
}
private Map<String, Object> readJsonMap(String value) {
try {
return jackson.readValue(value, new TypeReference<>() {});
} catch (IOException e) {
throw new TimiException(TimiCode.ERROR, "read json error", e);
}
}
@AOPLog @AOPLog
@RequestMapping("") @RequestMapping("")
public String root() { public String root() {
return "IT WORKING! " + TimiSpring.getRequestIP(); return "IT WORKING! " + TimiSpring.getRequestIP();
} }
/**
* 获取共享剪切板内容
*
* @param id 会话 ID
* @return 剪切板内容
*/
@AOPLog
@RequestRateLimit
@GetMapping("/clipboard/{id}")
public String clipboardGet(@Valid @NotBlank @PathVariable("id") String id) {
return clipboardService.getContent(id);
}
/**
* 设置共享剪切板内容
*
* @param id 会话 ID
* @param request 请求内容
*/
@AOPLog
@RequestRateLimit
@PostMapping("/clipboard/{id}")
public void clipboardSet(@Valid @NotBlank @PathVariable("id") String id, @Valid @RequestBody ClipboardRequest request) {
clipboardService.setContent(id, request.getContent());
}
/**
* 订阅共享剪切板实时更新
*
* @param id 会话 ID
* @return SSE 发射器
*/
@AOPLog
@IgnoreGlobalReturn
@GetMapping(value = "/clipboard/stream/{id}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter clipboardStream(@Valid @NotBlank @PathVariable("id") String id) {
return clipboardService.subscribe(id);
}
/** /**
* 获取验证码 * 获取验证码
* *
@@ -171,7 +236,7 @@ public class CommonController {
@RequestRateLimit @RequestRateLimit
@PostMapping("/feedback") @PostMapping("/feedback")
public void createFeedback(@Valid @NotNull @RequestBody CaptchaData<FeedbackRequest> request) { public void createFeedback(@Valid @NotNull @RequestBody CaptchaData<FeedbackRequest> request) {
captchaManager.test(request.getCaptcha(), request.getFrom()); captchaManager.test(request.getCaptcha(), request.getCaptchaId());
feedbackService.create(request.getData()); feedbackService.create(request.getData());
} }
@@ -204,12 +269,12 @@ public class CommonController {
case JSON -> { case JSON -> {
if (setting.getType() == Setting.Type.YAML) { if (setting.getType() == Setting.Type.YAML) {
Map<String, Object> obj = yaml.load(setting.getValue()); Map<String, Object> obj = yaml.load(setting.getValue());
result = gson.toJson(obj); result = writeJson(obj);
} }
} }
case YAML -> { case YAML -> {
if (setting.getType() == Setting.Type.JSON) { if (setting.getType() == Setting.Type.JSON) {
Map<String, Object> obj = gson.fromJson(setting.getValue(), new TypeToken<Map<String, Object>>() {}.getType()); Map<String, Object> obj = readJsonMap(setting.getValue());
result = yaml.dump(obj); result = yaml.dump(obj);
} }
} }
@@ -235,12 +300,12 @@ public class CommonController {
case JSON -> { case JSON -> {
if (setting.getType() == Setting.Type.YAML) { if (setting.getType() == Setting.Type.YAML) {
Map<String, Object> obj = new Yaml().load(setting.getValue()); Map<String, Object> obj = new Yaml().load(setting.getValue());
setting.setValue(gson.toJson(obj)); setting.setValue(writeJson(obj));
} }
} }
case YAML -> { case YAML -> {
if (setting.getType() == Setting.Type.JSON) { if (setting.getType() == Setting.Type.JSON) {
Map<String, Object> obj = gson.fromJson(setting.getValue(), new TypeToken<Map<String, Object>>() {}.getType()); Map<String, Object> obj = readJsonMap(setting.getValue());
setting.setValue(new Yaml().dump(obj)); setting.setValue(new Yaml().dump(obj));
} }
} }
@@ -250,21 +315,14 @@ public class CommonController {
return result.stream().collect(Collectors.toMap(Setting::getKey, Setting::getValue)); return result.stream().collect(Collectors.toMap(Setting::getKey, Setting::getValue));
} }
@RequestRateLimit
@GetMapping("/setting/flushCache")
public void settingFlushCache() {
settingService.flushCache();
}
@AOPLog @AOPLog
@RequestRateLimit @RequestRateLimit
@GetMapping("/attachment/{mongoId}") @GetMapping("/attachment/{mongoId}")
public AttachmentView getAttachment(@PathVariable String mongoId) { public Attachment getAttachment(@PathVariable String mongoId) {
return attachmentService.viewByMongoId(mongoId); return attachmentService.getByMongoId(mongoId);
} }
@AOPLog @AOPLog
@RequestRateLimit
@IgnoreGlobalReturn @IgnoreGlobalReturn
@GetMapping("/attachment/read/{mongoId}") @GetMapping("/attachment/read/{mongoId}")
public void readAttachment( public void readAttachment(
@@ -372,4 +430,92 @@ public class CommonController {
resp.setCharacterEncoding(StandardCharsets.UTF_8.toString()); resp.setCharacterEncoding(StandardCharsets.UTF_8.toString());
} }
} }
/**
* 上传临时文件
*
* @param files 文件列表
* @param ttl 缓存时长毫秒最长 3 259200000ms
* @return 临时文件响应列表
*/
@AOPLog
@PostMapping("/temp/file/upload")
public List<TempFileResponse> tempFileUpload(@RequestParam("file") List<MultipartFile> files, @RequestParam(value = "ttl", required = false) Long ttl) {
return tempFileService.store(files, ttl);
}
/**
* 读取临时文件
*
* @param mongoId mongoId
* @param req 请求
* @param resp 返回
*/
@AOPLog
@RequestRateLimit
@IgnoreGlobalReturn
@GetMapping("/temp/file/read/{mongoId}")
public void tempFileRead(@PathVariable String mongoId, HttpServletRequest req, HttpServletResponse resp) {
try {
Attachment attach = attachmentService.getByMongoId(mongoId);
if (TimiJava.isEmpty(attach)) {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
GridFSFile file = attachmentService.readByMongoId(attach.getMongoId());
GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(file.getObjectId());
GridFsResource gridFsResource = new GridFsResource(file, downloadStream);
req.setAttribute(ResourceHandler.ATTR_TYPE, ResourceHandler.Type.MONGO);
req.setAttribute(ResourceHandler.ATTR_VALUE, gridFsResource);
resp.setContentType(attach.getMimeType());
resourceHandler.handleRequest(req, resp);
} catch (Exception e) {
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
/**
* 下载临时文件
*
* @param mongoId
* @param resp
*/
@AOPLog
@RequestRateLimit
@IgnoreGlobalReturn
@RequestMapping("/temp/file/download/{mongoId}")
public void tempFileDownload(@PathVariable String mongoId, HttpServletResponse resp) {
try {
Attachment attach = attachmentService.getByMongoId(mongoId);
if (TimiJava.isEmpty(attach)) {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
resp.setContentType(attach.getMimeType());
resp.setHeader("Content-Disposition", Network.getFileDownloadHeader(attach.getName()));
resp.setHeader("Accept-Ranges", "bytes");
GridFSFile file = attachmentService.readByMongoId(mongoId);
@Cleanup
GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(file.getObjectId());
RequestRange range = TimiSpring.getRequestRange(attach.getSize());
if (range == null) {
// 完整文件
resp.setContentLengthLong(attach.getSize());
resp.setStatus(HttpServletResponse.SC_OK);
IO.toOutputStream(downloadStream, resp.getOutputStream());
} else {
// 分片文件
resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
resp.setHeader("Content-Range", "bytes %s-%s/%s".formatted(range.getStart(), range.getEnd(), attach.getSize()));
resp.setContentLengthLong(range.getLength());
IO.toOutputStream(downloadStream, resp.getOutputStream(), range.getStart(), range.getEnd());
resp.flushBuffer();
}
} catch (Exception e) {
log.error("download error", e);
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
} }

View File

@@ -1,10 +1,10 @@
package com.imyeyu.server.modules.common.controller; package com.imyeyu.api.modules.common.controller;
import com.imyeyu.server.modules.common.entity.Icon; import com.imyeyu.api.modules.common.entity.Icon;
import com.imyeyu.server.modules.common.service.IconService; import com.imyeyu.api.modules.common.service.IconService;
import com.imyeyu.server.modules.common.vo.icon.AllResponse; import com.imyeyu.api.modules.common.vo.icon.AllResponse;
import com.imyeyu.server.modules.common.vo.icon.NamePage; import com.imyeyu.api.modules.common.vo.icon.NamePage;
import com.imyeyu.server.modules.common.vo.icon.UnicodePage; import com.imyeyu.api.modules.common.vo.icon.UnicodePage;
import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.bean.Page; import com.imyeyu.spring.bean.Page;

View File

@@ -1,37 +1,34 @@
package com.imyeyu.server.modules.common.controller; package com.imyeyu.api.modules.common.controller;
import com.imyeyu.api.annotation.CaptchaValid;
import com.imyeyu.api.annotation.EnableSetting;
import com.imyeyu.api.bean.CaptchaFrom;
import com.imyeyu.api.modules.common.bean.SettingKey;
import com.imyeyu.api.modules.common.entity.Comment;
import com.imyeyu.api.modules.common.entity.CommentReply;
import com.imyeyu.api.modules.common.entity.UserConfig;
import com.imyeyu.api.modules.common.entity.UserPrivacy;
import com.imyeyu.api.modules.common.service.CommentReplyService;
import com.imyeyu.api.modules.common.service.CommentService;
import com.imyeyu.api.modules.common.service.UserConfigService;
import com.imyeyu.api.modules.common.service.UserPrivacyService;
import com.imyeyu.api.modules.common.service.UserProfileService;
import com.imyeyu.api.modules.common.service.UserService;
import com.imyeyu.api.modules.common.vo.user.EmailVerifyCallbackRequest;
import com.imyeyu.api.modules.common.vo.user.LoginRequest;
import com.imyeyu.api.modules.common.vo.user.LoginResponse;
import com.imyeyu.api.modules.common.vo.user.RegisterRequest;
import com.imyeyu.api.modules.common.vo.user.UpdatePasswordByKeyRequest;
import com.imyeyu.api.modules.common.vo.user.UpdatePasswordRequest;
import com.imyeyu.api.modules.common.vo.user.UserRequest;
import com.imyeyu.api.modules.common.vo.user.UserView;
import com.imyeyu.java.TimiJava; import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.server.annotation.CaptchaValid;
import com.imyeyu.server.annotation.EnableSetting;
import com.imyeyu.server.bean.CaptchaFrom;
import com.imyeyu.server.modules.common.bean.SettingKey;
import com.imyeyu.server.modules.common.entity.CommentReply;
import com.imyeyu.server.modules.common.entity.UserConfig;
import com.imyeyu.server.modules.common.entity.UserPrivacy;
import com.imyeyu.server.modules.common.service.CommentReplyService;
import com.imyeyu.server.modules.common.service.CommentService;
import com.imyeyu.server.modules.common.service.UserConfigService;
import com.imyeyu.server.modules.common.service.UserPrivacyService;
import com.imyeyu.server.modules.common.service.UserProfileService;
import com.imyeyu.server.modules.common.service.UserService;
import com.imyeyu.server.modules.common.vo.comment.CommentReplyPage;
import com.imyeyu.server.modules.common.vo.comment.CommentReplyView;
import com.imyeyu.server.modules.common.vo.comment.CommentView;
import com.imyeyu.server.modules.common.vo.comment.UserCommentPage;
import com.imyeyu.server.modules.common.vo.user.EmailVerifyCallbackRequest;
import com.imyeyu.server.modules.common.vo.user.LoginRequest;
import com.imyeyu.server.modules.common.vo.user.LoginResponse;
import com.imyeyu.server.modules.common.vo.user.RegisterRequest;
import com.imyeyu.server.modules.common.vo.user.UpdatePasswordByKeyRequest;
import com.imyeyu.server.modules.common.vo.user.UpdatePasswordRequest;
import com.imyeyu.server.modules.common.vo.user.UserRequest;
import com.imyeyu.server.modules.common.vo.user.UserView;
import com.imyeyu.spring.annotation.AOPLog; import com.imyeyu.spring.annotation.AOPLog;
import com.imyeyu.spring.annotation.RequestRateLimit; import com.imyeyu.spring.annotation.RequestRateLimit;
import com.imyeyu.spring.annotation.RequestSingleParam;
import com.imyeyu.spring.annotation.RequiredToken; import com.imyeyu.spring.annotation.RequiredToken;
import com.imyeyu.spring.bean.CaptchaData; import com.imyeyu.spring.bean.CaptchaData;
import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.bean.PageResult;
import com.imyeyu.utils.Time; import com.imyeyu.utils.Time;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@@ -186,7 +183,7 @@ public class UserController implements TimiJava {
@RequiredToken @RequiredToken
@RequestRateLimit @RequestRateLimit
@PostMapping("/cancel") @PostMapping("/cancel")
public void cancel(@RequestSingleParam String password) { public void cancel(@RequestBody String password) {
service.cancel(password); service.cancel(password);
} }
@@ -266,16 +263,18 @@ public class UserController implements TimiJava {
@RequiredToken @RequiredToken
@RequestRateLimit @RequestRateLimit
@PostMapping("/comment/list") @PostMapping("/comment/list")
public PageResult<CommentView> listComment(@Valid @RequestBody UserCommentPage page) { public PageResult<Comment> listComment(@Valid @RequestBody Page<Comment> page) {
page.setUserId(service.getLoginUser().getId()); Comment example = new Comment();
return commentService.pageByUserId(page); example.setUserId(service.getLoginUser().getId());
page.setEqualsExample(example);
return commentService.page(page);
} }
@AOPLog @AOPLog
@RequiredToken @RequiredToken
@RequestRateLimit @RequestRateLimit
@PostMapping("/comment/delete") @PostMapping("/comment/delete")
public void deleteComment(@RequestSingleParam Long commentId) { public void deleteComment(@RequestBody Long commentId) {
commentService.get(commentId); commentService.get(commentId);
commentService.delete(commentId); commentService.delete(commentId);
} }
@@ -287,16 +286,18 @@ public class UserController implements TimiJava {
@RequiredToken @RequiredToken
@RequestRateLimit @RequestRateLimit
@PostMapping("/comment/reply/list") @PostMapping("/comment/reply/list")
public PageResult<CommentReplyView> listCommentReply(@Valid @RequestBody CommentReplyPage page) { public PageResult<CommentReply> listCommentReply(@Valid @RequestBody Page<CommentReply> page) {
page.setBizId(service.getLoginUser().getId()); CommentReply example = new CommentReply();
return commentReplyService.pageByBizType(page); example.setReceiverId(service.getLoginUser().getId());
page.setEqualsExample(example);
return commentReplyService.page(page);
} }
@AOPLog @AOPLog
@RequiredToken @RequiredToken
@RequestRateLimit @RequestRateLimit
@PostMapping("/comment/reply/delete") @PostMapping("/comment/reply/delete")
public void deleteCommentReply(@RequestSingleParam Long replyId) { public void deleteCommentReply(@RequestBody Long replyId) {
CommentReply reply = commentReplyService.get(replyId); CommentReply reply = commentReplyService.get(replyId);
TimiException.requiredTrue(reply.getSenderId().equals(service.getLoginUser().getId()), "user.comment.reply.delete.not_owner"); TimiException.requiredTrue(reply.getSenderId().equals(service.getLoginUser().getId()), "user.comment.reply.delete.not_owner");
commentReplyService.delete(replyId); commentReplyService.delete(replyId);
@@ -306,7 +307,7 @@ public class UserController implements TimiJava {
@RequiredToken @RequiredToken
@RequestRateLimit @RequestRateLimit
@PostMapping("/comment/reply/ignore") @PostMapping("/comment/reply/ignore")
public void ignoreCommentReply(@RequestSingleParam Long replyId) { public void ignoreCommentReply(@RequestBody Long replyId) {
CommentReply reply = commentReplyService.get(replyId); CommentReply reply = commentReplyService.get(replyId);
TimiException.requiredTrue(reply.getReceiverId().equals(service.getLoginUser().getId()), "user.comment.reply.ignore.not_owner"); TimiException.requiredTrue(reply.getReceiverId().equals(service.getLoginUser().getId()), "user.comment.reply.ignore.not_owner");
reply.setIgnoredAt(Time.now()); reply.setIgnoredAt(Time.now());

View File

@@ -0,0 +1,111 @@
package com.imyeyu.api.modules.common.entity;
import com.fasterxml.jackson.databind.JsonNode;
import com.imyeyu.api.TimiServerAPI;
import com.imyeyu.api.bean.MultilingualHandler;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.Entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.io.InputStream;
/**
* @author 夜雨
* @since 2023-08-15 10:17
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class Attachment extends Entity implements MultilingualHandler {
/**
* 附件类型
*
* @author 夜雨
* @since 2023-08-21 16:32
*/
@Getter
@AllArgsConstructor
public enum BizType {
/** 用户 */
USER,
/** 文章 */
ARTICLE,
/** Git */
GIT,
/** 歌词 */
LYRIC,
/** ForeverMC */
FMC,
/** 镜像 */
MIRROR,
/** 日记 */
JOURNAL,
/** 日记出行 */
JOURNAL_TRAVEL,
/** 日记瞬间 */
JOURNAL_MOMENT,
/** 系统 */
SYSTEM,
/** 临时文件 */
TEMP_FILE
}
protected BizType bizType;
protected Long bizId;
protected String attachType;
protected String mongoId;
@MultilingualField
protected String title;
protected String name;
protected String mimeType;
protected JsonNode metadata;
protected Long size;
protected String md5;
protected String uploaderIp;
protected Boolean isDestroyed;
protected Long destroyAt;
@Transient
protected InputStream inputStream;
public InputStream openInputStream() {
AttachmentService service = TimiServerAPI.applicationContext.getBean(AttachmentService.class);
return service.getInputStreamByMongoId(mongoId);
}
public void setAttachTypeValue(Enum<?> attachType) {
this.attachType = attachType.toString();
}
public <T extends Enum<T>> T getAttachTypeValue(Class<T> attachTypeClass) {
return Ref.toType(attachTypeClass, attachType);
}
}

View File

@@ -1,5 +1,14 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import com.imyeyu.api.modules.blog.entity.Article;
import com.imyeyu.api.modules.blog.service.implement.ArticleServiceImplement;
import com.imyeyu.api.modules.common.bean.CommentSupport;
import com.imyeyu.api.modules.common.vo.user.UserView;
import com.imyeyu.api.modules.git.bean.gitea.Repository;
import com.imyeyu.api.modules.git.service.implement.IssueServiceImplement;
import com.imyeyu.api.modules.git.service.implement.MergeServiceImplement;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.Entity;
import com.imyeyu.spring.service.GettableService; import com.imyeyu.spring.service.GettableService;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@@ -7,12 +16,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import com.imyeyu.server.modules.blog.service.implement.ArticleServiceImplement;
import com.imyeyu.server.modules.common.bean.CommentSupport; import java.util.List;
import com.imyeyu.server.modules.git.service.implement.IssueServiceImplement;
import com.imyeyu.server.modules.git.service.implement.MergeServiceImplement;
import com.imyeyu.spring.entity.Entity;
import com.imyeyu.spring.service.BaseService;
/** /**
* 评论 * 评论
@@ -65,4 +70,24 @@ public class Comment extends Entity {
/** 发送用户 IP */ /** 发送用户 IP */
private String ip; private String ip;
/** 回复数量 */
@Transient
private long repliesLength;
/** 发送用户 */
@Transient
private UserView user;
/** 关联文章 */
@Transient
private Article article;
/** 关联仓库 */
@Transient
private Repository repository;
/** 回复列表 */
@Transient
private List<CommentReply> replies;
} }

View File

@@ -1,9 +1,11 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import com.imyeyu.api.modules.common.vo.user.UserView;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.Entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import com.imyeyu.spring.entity.Entity;
/** /**
* 评论回复 * 评论回复
@@ -42,4 +44,16 @@ public class CommentReply extends Entity {
/** 被回复用户忽略该回复的时间 */ /** 被回复用户忽略该回复的时间 */
private Long ignoredAt; private Long ignoredAt;
/** 所属评论 */
@Transient
private Comment comment;
/** 发送用户 */
@Transient
private UserView sender;
/** 回复用户 */
@Transient
private UserView receiver;
} }

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import com.imyeyu.spring.annotation.table.AutoUUID; import com.imyeyu.spring.annotation.table.AutoUUID;
import com.imyeyu.spring.annotation.table.Id; import com.imyeyu.spring.annotation.table.Id;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -0,0 +1,28 @@
package com.imyeyu.api.modules.common.entity;
import com.imyeyu.java.bean.Language;
import com.imyeyu.spring.entity.Creatable;
import com.imyeyu.spring.entity.Deletable;
import com.imyeyu.spring.entity.IDEntity;
import com.imyeyu.spring.entity.Updatable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
/**
* @author 夜雨
* @since 2023-10-24 16:41
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class Multilingual extends Language implements Serializable, IDEntity<Long>, Creatable, Updatable, Deletable {
protected Long id;
protected Long createdAt;
protected Long updatedAt;
protected Long deletedAt;
}

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import com.imyeyu.server.modules.common.bean.SettingKey; import com.imyeyu.api.modules.common.bean.SettingKey;
import com.imyeyu.spring.annotation.table.Id; import com.imyeyu.spring.annotation.table.Id;
import com.imyeyu.spring.entity.Creatable; import com.imyeyu.spring.entity.Creatable;
import com.imyeyu.spring.entity.Updatable; import com.imyeyu.spring.entity.Updatable;

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import com.imyeyu.server.bean.MultilingualHandler; import com.imyeyu.api.bean.MultilingualHandler;
import com.imyeyu.spring.entity.Entity; import com.imyeyu.spring.entity.Entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import com.imyeyu.spring.annotation.table.Id; import com.imyeyu.spring.annotation.table.Id;
import com.imyeyu.spring.entity.Updatable; import com.imyeyu.spring.entity.Updatable;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import com.imyeyu.java.ref.Ref; import com.imyeyu.java.ref.Ref;
import com.imyeyu.spring.annotation.table.Id; import com.imyeyu.spring.annotation.table.Id;

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import com.imyeyu.server.modules.common.bean.ImageType; import com.imyeyu.api.modules.common.bean.ImageType;
import com.imyeyu.spring.annotation.table.Id; import com.imyeyu.spring.annotation.table.Id;
import com.imyeyu.spring.entity.Updatable; import com.imyeyu.spring.entity.Updatable;
import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Max;

View File

@@ -1,4 +1,4 @@
package com.imyeyu.server.modules.common.entity; package com.imyeyu.api.modules.common.entity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;

View File

@@ -1,7 +1,9 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.Attachment; import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.spring.bean.Page;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import com.imyeyu.spring.mapper.RawMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
import java.util.List; import java.util.List;
@@ -10,10 +12,10 @@ import java.util.List;
* @author 夜雨 * @author 夜雨
* @since 2023-08-15 10:22 * @since 2023-08-15 10:22
*/ */
public interface AttachmentMapper extends BaseMapper<Attachment, Long> { public interface AttachmentMapper extends BaseMapper<Attachment, Long>, RawMapper<Attachment, Long> {
/** 有效条件,非删除和销毁 */ /** 有效条件,非删除和销毁 */
String VALID = NOT_DELETE + " AND destroy_at IS NULL"; String VALID = NOT_DELETE + " AND (`destroy_at` IS NULL OR " + UNIX_TIME + " < `destroy_at`)";
@Select("SELECT * FROM attachment WHERE biz_type = #{bizType} AND biz_id = #{bizId} " + VALID + LIMIT_1) @Select("SELECT * FROM attachment WHERE biz_type = #{bizType} AND biz_id = #{bizId} " + VALID + LIMIT_1)
Attachment selectByBizId(Attachment.BizType bizType, long bizId); Attachment selectByBizId(Attachment.BizType bizType, long bizId);
@@ -24,8 +26,12 @@ public interface AttachmentMapper extends BaseMapper<Attachment, Long> {
@Select("SELECT * FROM attachment WHERE biz_type = #{bizType} AND biz_id = #{bizId} AND attach_type = #{attachType} " + VALID + LIMIT_1) @Select("SELECT * FROM attachment WHERE biz_type = #{bizType} AND biz_id = #{bizId} AND attach_type = #{attachType} " + VALID + LIMIT_1)
Attachment selectByAttachType(Attachment.BizType bizType, long bizId, Enum<?> attachType); Attachment selectByAttachType(Attachment.BizType bizType, long bizId, Enum<?> attachType);
@Select("SELECT * FROM attachment WHERE biz_type = #{bizType} AND biz_id = #{bizId} AND " + VALID + PAGE) List<Attachment> listByBizId(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypes, Page<Attachment> page);
List<Attachment> listByBizId(Attachment.BizType bizType, long bizId, long offset, int limit);
List<Attachment> listByAttachType(Attachment.BizType bizType, long bizId, Enum<?> ...attachTypes); long countByBizId(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypes);
List<Attachment> listByMd5s(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypes, List<String> md5s);
@Select("SELECT * FROM attachment WHERE `is_destroyed` = FALSE AND `destroy_at` < " + UNIX_TIME)
List<Attachment> selectNeedDestroy();
} }

View File

@@ -0,0 +1,19 @@
package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.api.modules.common.entity.Comment;
import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Update;
/**
* 评论
*
* @author 夜雨
* @since 2021-2-23 21:33
*/
public interface CommentMapper extends BaseMapper<Comment, Long> {
long countAll(Comment.BizType bizType, Long bizId);
@Update("UPDATE comment SET deleted_at = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000) WHERE user_id = #{userId} ")
void deleteByUserId(Long userId);
}

View File

@@ -1,8 +1,7 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.blog.entity.CommentRemindQueue; import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
import java.util.List; import java.util.List;
@@ -17,10 +16,4 @@ public interface CommentRemindQueueMapper extends BaseMapper<CommentRemindQueue,
@Select("SELECT * FROM comment_remind_queue WHERE user_id = #{userId}") @Select("SELECT * FROM comment_remind_queue WHERE user_id = #{userId}")
List<CommentRemindQueue> listByUserId(Long userId); List<CommentRemindQueue> listByUserId(Long userId);
@Delete("DELETE FROM comment_remind_queue WHERE user_id = #{userId}")
void destroyByUserId(Long userId);
@Delete("DELETE FROM comment_remind_queue WHERE reply_id = #{replyId}")
void destroyByReplyId(Long replyId);
} }

View File

@@ -0,0 +1,14 @@
package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.api.modules.common.entity.CommentReply;
import com.imyeyu.spring.mapper.BaseMapper;
/**
* 评论回复
*
* @author 夜雨
* @since 2021-08-24 10:36
*/
public interface CommentReplyMapper extends BaseMapper<CommentReply, Long> {
}

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.EmailQueueLog; import com.imyeyu.api.modules.common.entity.EmailQueueLog;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
/** /**

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.EmailQueue; import com.imyeyu.api.modules.common.entity.EmailQueue;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.Feedback; import com.imyeyu.api.modules.common.entity.Feedback;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
/** /**

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.Icon; import com.imyeyu.api.modules.common.entity.Icon;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
@@ -14,14 +14,6 @@ import java.util.List;
*/ */
public interface IconMapper extends BaseMapper<Icon, Long> { public interface IconMapper extends BaseMapper<Icon, Long> {
@Select("SELECT COUNT(1) FROM icon" + NOT_DELETE)
@Override
long count();
@Select("SELECT * FROM icon LIMIT #{offset}, #{limit}" + NOT_DELETE)
@Override
List<Icon> list(long offset, int limit);
@Select("SELECT * FROM icon WHERE 1 = 1" + NOT_DELETE) @Select("SELECT * FROM icon WHERE 1 = 1" + NOT_DELETE)
List<Icon> listAll(); List<Icon> listAll();
@@ -29,15 +21,15 @@ public interface IconMapper extends BaseMapper<Icon, Long> {
long countByName(String name); long countByName(String name);
@Select("SELECT * FROM icon WHERE name LIKE CONCAT('%', #{name}, '%')" + PAGE) @Select("SELECT * FROM icon WHERE name LIKE CONCAT('%', #{name}, '%')" + PAGE)
List<Icon> listByName(String name, long offset, int limit); List<Icon> listByName(String name, long offset, long limit);
long countByLabel(String lang, String label); long countByLabel(String lang, String label);
List<Icon> listByLabel(String lang, String label, long offset, int limit); List<Icon> listByLabel(String lang, String label, long offset, long limit);
@Select("SELECT COUNT(1) FROM icon WHERE unicode = #{unicode}" + NOT_DELETE) @Select("SELECT COUNT(1) FROM icon WHERE unicode = #{unicode}" + NOT_DELETE)
long countByUnicode(String unicode); long countByUnicode(String unicode);
@Select("SELECT * FROM icon WHERE unicode = #{unicode}" + PAGE) @Select("SELECT * FROM icon WHERE unicode = #{unicode}" + PAGE)
List<Icon> listByUnicode(String unicode, long offset, int limit); List<Icon> listByUnicode(String unicode, long offset, long limit);
} }

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.Multilingual; import com.imyeyu.api.modules.common.entity.Multilingual;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;

View File

@@ -1,7 +1,7 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.bean.SettingKey; import com.imyeyu.api.modules.common.bean.SettingKey;
import com.imyeyu.server.modules.common.entity.Setting; import com.imyeyu.api.modules.common.entity.Setting;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.Tag; import com.imyeyu.api.modules.common.entity.Tag;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
/** /**

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.Task; import com.imyeyu.api.modules.common.entity.Task;
import java.util.List; import java.util.List;

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.Template; import com.imyeyu.api.modules.common.entity.Template;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.UserConfig; import com.imyeyu.api.modules.common.entity.UserConfig;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
/** /**

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.User; import com.imyeyu.api.modules.common.entity.User;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.UserPrivacy; import com.imyeyu.api.modules.common.entity.UserPrivacy;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
/** /**

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.UserProfile; import com.imyeyu.api.modules.common.entity.UserProfile;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
/** /**

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.mapper; package com.imyeyu.api.modules.common.mapper;
import com.imyeyu.server.modules.common.entity.Version; import com.imyeyu.api.modules.common.entity.Version;
import com.imyeyu.spring.mapper.BaseMapper; import com.imyeyu.spring.mapper.BaseMapper;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;

View File

@@ -0,0 +1,64 @@
package com.imyeyu.api.modules.common.service;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.spring.service.BaseService;
import com.mongodb.client.gridfs.model.GridFSFile;
import java.io.InputStream;
import java.util.List;
/**
* 附件服务
*
* <p>删除和销毁都为数据库软删除,但删除不删除 MongoDB 文件,而销毁则删除 MongoDB 文件
*
* @author 夜雨
* @since 2023-08-15 10:21
*/
public interface AttachmentService extends BaseService<Attachment, Long> {
Attachment createMedia(Attachment attachment);
void deleteMedia(Long thumbId) throws TimiException;
Attachment getByBizId(Attachment.BizType bizType, long bizId);
Attachment getByAttachType(Attachment.BizType bizType, long bizId, Enum<?> attachType);
Attachment getByMongoId(String mongoId);
GridFSFile readByMongoId(String mongoId);
InputStream getInputStreamByMongoId(String mongoId);
byte[] readAllByMongoId(String mongoId);
/**
* 根据业务获取所有附件
*
* @param bizType 业务类型
* @param bizId 业务 ID可为 null
* @param attachTypes 附件类型,可为 null
* @return 所有附件
* @throws TimiException 服务异常
*/
List<Attachment> listByBizId(Attachment.BizType bizType, Long bizId, Enum<?> ...attachTypes);
/**
* 根据业务获取所有附件
*
* @param bizType 业务类型
* @param bizId 业务 ID可为 null
* @param attachTypes 附件类型,可为 null
* @return 附件数量
* @throws TimiException 服务异常
*/
long countByBizId(Attachment.BizType bizType, Long bizId, Enum<?> ...attachTypes);
List<Attachment> listByMd5s(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypeList, List<String> md5s);
void deleteByBizId(Attachment.BizType bizType, long bizId, Enum<?> ...attachTypes);
List<Attachment> listNeedDestroy();
}

View File

@@ -0,0 +1,36 @@
package com.imyeyu.api.modules.common.service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
/**
* 共享剪切板服务
*
* @author 夜雨
* @since 2026-01-15 16:40
*/
public interface ClipboardService {
/**
* 获取共享剪切板内容
*
* @param id 会话 ID
* @return 剪切板内容
*/
String getContent(String id);
/**
* 设置共享剪切板内容
*
* @param id 会话 ID
* @param content 剪切板内容
*/
void setContent(String id, String content);
/**
* 订阅共享剪切板实时更新
*
* @param id 会话 ID
* @return SSE 发射器
*/
SseEmitter subscribe(String id);
}

View File

@@ -0,0 +1,13 @@
package com.imyeyu.api.modules.common.service;
import com.imyeyu.api.modules.common.entity.CommentReply;
import com.imyeyu.spring.service.BaseService;
/**
* 评论回复服务
*
* @author 夜雨
* @since 2021-08-24 10:33
*/
public interface CommentReplyService extends BaseService<CommentReply, Long> {
}

View File

@@ -0,0 +1,13 @@
package com.imyeyu.api.modules.common.service;
import com.imyeyu.api.modules.common.entity.Comment;
import com.imyeyu.spring.service.BaseService;
/**
* 评论服务
*
* @author 夜雨
* @since 2021-02-23 21:32
*/
public interface CommentService extends BaseService<Comment, Long> {
}

View File

@@ -1,8 +1,8 @@
package com.imyeyu.server.modules.common.service; package com.imyeyu.api.modules.common.service;
import com.imyeyu.java.bean.timi.TimiException; import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.server.modules.common.entity.EmailQueue; import com.imyeyu.api.modules.common.entity.EmailQueue;
import com.imyeyu.server.modules.common.entity.EmailQueueLog; import com.imyeyu.api.modules.common.entity.EmailQueueLog;
import com.imyeyu.spring.service.CreatableService; import com.imyeyu.spring.service.CreatableService;
import com.imyeyu.spring.service.DestroyableService; import com.imyeyu.spring.service.DestroyableService;

View File

@@ -1,6 +1,6 @@
package com.imyeyu.server.modules.common.service; package com.imyeyu.api.modules.common.service;
import com.imyeyu.server.modules.common.vo.FeedbackRequest; import com.imyeyu.api.modules.common.vo.FeedbackRequest;
/** /**
* 反馈服务 * 反馈服务

View File

@@ -1,9 +1,9 @@
package com.imyeyu.server.modules.common.service; package com.imyeyu.api.modules.common.service;
import com.imyeyu.server.modules.common.entity.Icon; import com.imyeyu.api.modules.common.entity.Icon;
import com.imyeyu.server.modules.common.vo.icon.AllResponse; import com.imyeyu.api.modules.common.vo.icon.AllResponse;
import com.imyeyu.server.modules.common.vo.icon.NamePage; import com.imyeyu.api.modules.common.vo.icon.NamePage;
import com.imyeyu.server.modules.common.vo.icon.UnicodePage; import com.imyeyu.api.modules.common.vo.icon.UnicodePage;
import com.imyeyu.spring.bean.PageResult; import com.imyeyu.spring.bean.PageResult;
import com.imyeyu.spring.service.PageableService; import com.imyeyu.spring.service.PageableService;

View File

@@ -1,7 +1,7 @@
package com.imyeyu.server.modules.common.service; package com.imyeyu.api.modules.common.service;
import com.imyeyu.java.bean.Language; import com.imyeyu.java.bean.Language;
import com.imyeyu.server.modules.common.entity.Multilingual; import com.imyeyu.api.modules.common.entity.Multilingual;
import com.imyeyu.spring.service.UpdatableService; import com.imyeyu.spring.service.UpdatableService;
import java.util.List; import java.util.List;
@@ -16,9 +16,9 @@ public interface MultilingualService extends UpdatableService<Multilingual> {
Long createIfNotExist(String key, String zhCN); Long createIfNotExist(String key, String zhCN);
String get(Language language, Long id); String get(Language.Enum language, Long id);
String getByKey(Language language, String key); String getByKey(Language.Enum language, String key);
List<Multilingual> listByNotTranslate(); List<Multilingual> listByNotTranslate();
} }

Some files were not shown because too many files have changed in this diff Show More