Compare commits
51 Commits
723992f360
...
v1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 523edffc4e | |||
|
|
3cc371c53e | ||
| c7ddb1a8b0 | |||
|
|
b46e9079d5 | ||
|
|
dc20070bf8 | ||
| 45c9fc814a | |||
|
|
78163441dd | ||
| 407dc13ac4 | |||
|
|
9762be1244 | ||
| 0c06bf16c2 | |||
|
|
971cad7365 | ||
| 1db39e77d3 | |||
|
|
b5e9da0e9b | ||
|
|
34e1ac6264 | ||
|
|
ef192daa93 | ||
|
|
8947269351 | ||
|
|
b6a58b7376 | ||
|
|
cd7bc31e6b | ||
|
|
e619f3a1e2 | ||
|
|
230864fa43 | ||
|
|
fa11b388db | ||
|
|
b6eb7980e4 | ||
|
|
3410c540ab | ||
|
|
040a46934d | ||
|
|
7eb409f6d0 | ||
|
|
7b3d4e2a65 | ||
|
|
4e113e8fae | ||
|
|
7a34a481aa | ||
|
|
a402ea86ef | ||
|
|
3eaa560aec | ||
|
|
e61da7d8f9 | ||
|
|
355b192071 | ||
|
|
c732f7cead | ||
|
|
812e33693b | ||
|
|
a7b490240c | ||
|
|
959fc30758 | ||
|
|
fe86a84204 | ||
|
|
42bcfcf70e | ||
|
|
d82c5200e7 | ||
|
|
658765df6f | ||
|
|
d12c76fe03 | ||
|
|
2262eabe07 | ||
|
|
81c1a14228 | ||
|
|
fdfb0439d9 | ||
|
|
e2a3c193d8 | ||
|
|
40760ed517 | ||
|
|
6839dafdb9 | ||
|
|
bc2c920705 | ||
|
|
59b0153f3e | ||
|
|
ffaf7f84b4 | ||
|
|
289ba499d3 |
333
.gitea/workflows/ci.yml
Normal file
333
.gitea/workflows/ci.yml
Normal 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
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,8 +1,12 @@
|
|||||||
/config
|
/config
|
||||||
/data
|
/data
|
||||||
|
/docs
|
||||||
/logs
|
/logs
|
||||||
/target
|
/target
|
||||||
/temp
|
/temp
|
||||||
|
/.claude
|
||||||
|
/CLAUDE.md
|
||||||
|
/AGENTS.md
|
||||||
|
|
||||||
multilingualField/
|
multilingualField/
|
||||||
!.mvn/wrapper/maven-wrapper.jar
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|||||||
57
pom.xml
57
pom.xml
@@ -5,31 +5,24 @@
|
|||||||
<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>
|
|
||||||
<repository>
|
|
||||||
<id>apache-maven</id>
|
|
||||||
<url>https://repo.maven.apache.org/maven2/</url>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>dev-windows</id>
|
<id>dev-windows</id>
|
||||||
@@ -39,6 +32,20 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<native.classifier>windows-x86_64</native.classifier>
|
<native.classifier>windows-x86_64</native.classifier>
|
||||||
</properties>
|
</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>
|
||||||
<profile>
|
<profile>
|
||||||
<id>prod-linux</id>
|
<id>prod-linux</id>
|
||||||
@@ -98,22 +105,38 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<excludeDevtools>true</excludeDevtools>
|
<excludeDevtools>true</excludeDevtools>
|
||||||
<mainClass>com.imyeyu.api.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.2</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.2</version>
|
<version>0.0.8</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.imyeyu.lang</groupId>
|
<groupId>com.imyeyu.lang</groupId>
|
||||||
@@ -170,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>
|
||||||
@@ -206,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>
|
||||||
@@ -227,8 +250,6 @@
|
|||||||
<groupId>org.bytedeco</groupId>
|
<groupId>org.bytedeco</groupId>
|
||||||
<artifactId>ffmpeg</artifactId>
|
<artifactId>ffmpeg</artifactId>
|
||||||
<version>7.1.1-1.5.12</version>
|
<version>7.1.1-1.5.12</version>
|
||||||
<scope>provided</scope>
|
|
||||||
<classifier>windows-x86_64</classifier>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bytedeco</groupId>
|
<groupId>org.bytedeco</groupId>
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package com.imyeyu.api;
|
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;
|
||||||
@@ -31,8 +26,6 @@ import java.io.File;
|
|||||||
@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) {
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package com.imyeyu.api.bean;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2025-01-13 11:42
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class IOCBeans {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Yaml yaml() {
|
|
||||||
return new Yaml();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
29
src/main/java/com/imyeyu/api/bean/PreviewPage.java
Normal file
29
src/main/java/com/imyeyu/api/bean/PreviewPage.java
Normal 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;
|
||||||
|
}
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
package com.imyeyu.api.config;
|
package com.imyeyu.api.config;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
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.springframework.context.annotation.Configuration;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
@@ -10,7 +15,17 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class BeanConfig {
|
public class BeanConfig {
|
||||||
|
|
||||||
public Gson gson() {
|
@Bean
|
||||||
return new Gson();
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package com.imyeyu.api.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.api.modules.blog.entity.ArticleRanking;
|
||||||
import com.imyeyu.api.modules.common.entity.Multilingual;
|
import com.imyeyu.api.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package com.imyeyu.api.config;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
@@ -19,37 +17,17 @@ import java.util.concurrent.ThreadPoolExecutor;
|
|||||||
@Data
|
@Data
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
|
||||||
@ConfigurationProperties(prefix = "spring.async.thread-pool")
|
|
||||||
public class ThreadPoolConfig {
|
public class ThreadPoolConfig {
|
||||||
|
|
||||||
/** 核心数量 */
|
|
||||||
private int corePoolSize;
|
|
||||||
|
|
||||||
/** 最大数量 */
|
|
||||||
private int maxPoolSize;
|
|
||||||
|
|
||||||
/** 等待区容量 */
|
|
||||||
private int queueCapacity;
|
|
||||||
|
|
||||||
/** 最大保持活跃时间(秒) */
|
|
||||||
private int keepAliveSeconds;
|
|
||||||
|
|
||||||
/** 最大等待时间(秒) */
|
|
||||||
private int awaitTerminationSeconds;
|
|
||||||
|
|
||||||
/** 线程名称前缀 */
|
|
||||||
private String threadNamePrefix;
|
|
||||||
|
|
||||||
@Bean(name = "threadPoolTaskExecutor")
|
@Bean(name = "threadPoolTaskExecutor")
|
||||||
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
|
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
|
||||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
executor.setCorePoolSize(corePoolSize);
|
executor.setCorePoolSize(16);
|
||||||
executor.setMaxPoolSize(maxPoolSize);
|
executor.setMaxPoolSize(32);
|
||||||
executor.setQueueCapacity(queueCapacity);
|
executor.setQueueCapacity(16);
|
||||||
executor.setKeepAliveSeconds(keepAliveSeconds);
|
executor.setKeepAliveSeconds(60);
|
||||||
executor.setAwaitTerminationSeconds(awaitTerminationSeconds);
|
executor.setAwaitTerminationSeconds(60);
|
||||||
executor.setThreadNamePrefix(threadNamePrefix);
|
executor.setThreadNamePrefix("thread-pool-task-executor-");
|
||||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
executor.initialize();
|
executor.initialize();
|
||||||
return executor;
|
return executor;
|
||||||
|
|||||||
@@ -1,32 +1,28 @@
|
|||||||
package com.imyeyu.api.config;
|
package com.imyeyu.api.config;
|
||||||
|
|
||||||
import com.google.gson.GsonBuilder;
|
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.EnableSettingInterceptor;
|
||||||
import com.imyeyu.api.annotation.RequestRateLimitInterceptor;
|
import com.imyeyu.api.annotation.RequestRateLimitInterceptor;
|
||||||
import com.imyeyu.api.annotation.RequiredTokenInterceptor;
|
import com.imyeyu.api.annotation.RequiredTokenInterceptor;
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
|
||||||
import com.imyeyu.api.modules.common.vo.user.UserProfileView;
|
|
||||||
import com.imyeyu.api.modules.common.vo.user.UserView;
|
|
||||||
import com.imyeyu.api.modules.journal.util.JournalAPIInterceptor;
|
import com.imyeyu.api.modules.journal.util.JournalAPIInterceptor;
|
||||||
import com.imyeyu.api.modules.minecraft.annotation.RequiredFMCServerTokenInterceptor;
|
import com.imyeyu.api.modules.minecraft.annotation.RequiredFMCServerTokenInterceptor;
|
||||||
import com.imyeyu.api.modules.minecraft.entity.MinecraftPlayer;
|
|
||||||
import com.imyeyu.api.modules.mirror.vo.MirrorView;
|
|
||||||
import com.imyeyu.api.modules.system.util.SystemAPIInterceptor;
|
import com.imyeyu.api.modules.system.util.SystemAPIInterceptor;
|
||||||
import com.imyeyu.api.util.GsonSerializerAdapter;
|
import com.imyeyu.spring.annotation.RequestBodyValueArgumentResolver;
|
||||||
import com.imyeyu.spring.annotation.RequestSingleParamResolver;
|
import com.imyeyu.utils.Time;
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
import org.springframework.http.converter.json.GsonHttpMessageConverter;
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
import java.io.Writer;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,12 +36,11 @@ import java.util.List;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class WebConfig implements WebMvcConfigurer {
|
public class WebConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
private final ObjectMapper jackson;
|
||||||
private final SystemAPIInterceptor systemAPIInterceptor;
|
private final SystemAPIInterceptor systemAPIInterceptor;
|
||||||
private final GsonSerializerAdapter gsonSerializerAdapter;
|
|
||||||
private final JournalAPIInterceptor journalAPIInterceptor;
|
private final JournalAPIInterceptor journalAPIInterceptor;
|
||||||
private final RequiredTokenInterceptor requiredTokenInterceptor;
|
private final RequiredTokenInterceptor requiredTokenInterceptor;
|
||||||
private final EnableSettingInterceptor enableSettingInterceptor;
|
private final EnableSettingInterceptor enableSettingInterceptor;
|
||||||
private final RequestSingleParamResolver requestSingleParamResolver;
|
|
||||||
private final RequestRateLimitInterceptor requestRateLimitInterceptor;
|
private final RequestRateLimitInterceptor requestRateLimitInterceptor;
|
||||||
private final RequiredFMCServerTokenInterceptor requiredFMCServerTokenInterceptor;
|
private final RequiredFMCServerTokenInterceptor requiredFMCServerTokenInterceptor;
|
||||||
|
|
||||||
@@ -66,7 +61,7 @@ public class WebConfig implements WebMvcConfigurer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||||
argumentResolvers.add(requestSingleParamResolver);
|
argumentResolvers.add(new RequestBodyValueArgumentResolver(jackson));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,22 +71,17 @@ public class WebConfig implements WebMvcConfigurer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||||
GsonHttpMessageConverter converter = new GsonHttpMessageConverter() {
|
JsonMapper jsonMapper = JsonMapper.builder()
|
||||||
|
// 设置默认属性包含规则:忽略 null 值
|
||||||
@Override
|
.serializationInclusion(JsonInclude.Include.NON_NULL)
|
||||||
protected void writeInternal(@NotNull Object object, Type type, @NonNull Writer writer) {
|
// 日期格式化
|
||||||
// 忽略参数类型,因为接口返回对象会被全局返回处理器包装为 TimiResponse,否则会序列化转型错误
|
.defaultDateFormat(Time.dateTime)
|
||||||
getGson().toJson(object, writer);
|
// 忽略不存在字段
|
||||||
}
|
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
|
||||||
};
|
// 启用默认视图包含
|
||||||
|
.enable(MapperFeature.DEFAULT_VIEW_INCLUSION).build();
|
||||||
GsonBuilder builder = new GsonBuilder();
|
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(jsonMapper);
|
||||||
builder.registerTypeAdapter(Attachment.class, gsonSerializerAdapter);
|
converter.setDefaultCharset(StandardCharsets.UTF_8);
|
||||||
builder.registerTypeAdapter(UserView.class, gsonSerializerAdapter);
|
|
||||||
builder.registerTypeAdapter(MirrorView.class, gsonSerializerAdapter);
|
|
||||||
builder.registerTypeAdapter(UserProfileView.class, gsonSerializerAdapter);
|
|
||||||
builder.registerTypeAdapter(MinecraftPlayer.class, gsonSerializerAdapter);
|
|
||||||
converter.setGson(builder.create());
|
|
||||||
converters.add(converter);
|
converters.add(converter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.api.modules.gitea.entity",
|
"com.imyeyu.api.modules.gitea.entity",
|
||||||
};
|
};
|
||||||
String[] typeHandlers = {
|
String[] typeHandlers = {
|
||||||
"com.imyeyu.api.handler"
|
"com.imyeyu.spring.handler"
|
||||||
};
|
};
|
||||||
|
|
||||||
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
|
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
|
||||||
|
|||||||
@@ -81,9 +81,10 @@ 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 = {
|
||||||
@@ -98,7 +99,7 @@ public class TimiServerDBConfig {
|
|||||||
"com.imyeyu.api.modules.minecraft.entity"
|
"com.imyeyu.api.modules.minecraft.entity"
|
||||||
};
|
};
|
||||||
String[] typeHandlers = {
|
String[] typeHandlers = {
|
||||||
"com.imyeyu.api.handler"
|
"com.imyeyu.spring.handler"
|
||||||
};
|
};
|
||||||
|
|
||||||
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
|
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
package com.imyeyu.api.handler;
|
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import com.imyeyu.java.TimiJava;
|
|
||||||
import org.apache.ibatis.type.BaseTypeHandler;
|
|
||||||
import org.apache.ibatis.type.JdbcType;
|
|
||||||
|
|
||||||
import java.sql.CallableStatement;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MySQL JSON 数据类型处理器
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2021-07-04 09:36
|
|
||||||
*/
|
|
||||||
public class GsonHandler extends BaseTypeHandler<JsonElement> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setNonNullParameter(PreparedStatement ps, int i, JsonElement parameter, JdbcType jdbcType) throws SQLException {
|
|
||||||
ps.setString(i, String.valueOf(parameter.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonElement getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
|
||||||
return toElement(rs.getString(columnName));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonElement getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
|
||||||
return toElement(rs.getString(columnIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JsonElement getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
|
||||||
return toElement(cs.getNString(columnIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsonElement toElement(String json) {
|
|
||||||
if (TimiJava.isNotEmpty(json)) {
|
|
||||||
JsonElement el = JsonParser.parseString(json);
|
|
||||||
if (el.isJsonObject()) {
|
|
||||||
return el.getAsJsonObject();
|
|
||||||
}
|
|
||||||
if (el.isJsonArray()) {
|
|
||||||
return el.getAsJsonArray();
|
|
||||||
}
|
|
||||||
if (el.isJsonPrimitive()) {
|
|
||||||
return el.getAsJsonPrimitive();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.bill.controller;
|
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import com.imyeyu.api.modules.bill.entity.Bill;
|
|
||||||
import com.imyeyu.api.modules.bill.service.BillService;
|
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
|
||||||
import com.imyeyu.spring.TimiSpring;
|
|
||||||
import com.imyeyu.spring.annotation.AOPLog;
|
|
||||||
import com.imyeyu.spring.annotation.RequestRateLimit;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 收支帐单接口
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2023-02-04 01:02
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RestController
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RequestMapping("/bill")
|
|
||||||
public class BillController {
|
|
||||||
|
|
||||||
private final BillService service;
|
|
||||||
private final SettingService settingService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建收支帐单
|
|
||||||
*
|
|
||||||
* @param bill 账单
|
|
||||||
*/
|
|
||||||
@AOPLog
|
|
||||||
@RequestRateLimit
|
|
||||||
@PostMapping("/create")
|
|
||||||
public void createREBill(@Valid @RequestBody Bill bill) {
|
|
||||||
if (!settingService.getAsString(SettingKey.BILL_API_TOKEN).equals(TimiSpring.getToken())) {
|
|
||||||
throw new TimiException(TimiCode.REQUEST_BAD).msgKey("token.illegal");
|
|
||||||
}
|
|
||||||
service.create(bill);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.bill.entity;
|
|
||||||
|
|
||||||
import jakarta.validation.constraints.DecimalMin;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import lombok.Getter;
|
|
||||||
import com.imyeyu.spring.entity.Entity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 收支账单
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2022-03-29 11:28
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class Bill extends Entity {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 类型
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2022-03-29 11:28
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
public enum Type {
|
|
||||||
|
|
||||||
/** 收入 */
|
|
||||||
REVENUE,
|
|
||||||
|
|
||||||
/** 支出 */
|
|
||||||
EXPENDITURE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 收入类型
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2022-03-29 11:28
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
public enum RevenueType {
|
|
||||||
|
|
||||||
/** 工作 */
|
|
||||||
WORK,
|
|
||||||
|
|
||||||
/** 退款 */
|
|
||||||
REFUND,
|
|
||||||
|
|
||||||
/** 其他 */
|
|
||||||
OTHER
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支出类型
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2022-03-29 11:28
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
public enum ExpenditureType {
|
|
||||||
|
|
||||||
/** 饮食 */
|
|
||||||
FOOD,
|
|
||||||
|
|
||||||
/** 生活 */
|
|
||||||
LIFE,
|
|
||||||
|
|
||||||
/** 通信 */
|
|
||||||
COMMUNICATION,
|
|
||||||
|
|
||||||
/** 交通 */
|
|
||||||
TRAFFIC,
|
|
||||||
|
|
||||||
/** 娱乐 */
|
|
||||||
GAME,
|
|
||||||
|
|
||||||
/** 工作 */
|
|
||||||
WORK,
|
|
||||||
|
|
||||||
/** 服饰 */
|
|
||||||
CLOTHES,
|
|
||||||
|
|
||||||
/** 医疗 */
|
|
||||||
HEALTH,
|
|
||||||
|
|
||||||
/** 其他 */
|
|
||||||
OTHER
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 收入类型 */
|
|
||||||
private RevenueType revenueType;
|
|
||||||
|
|
||||||
/** 支出类型 */
|
|
||||||
private ExpenditureType expenditureType;
|
|
||||||
|
|
||||||
/** 描述 */
|
|
||||||
@NotBlank(message = "bill.description.empty")
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
/** 金额(未确保计算精度,放大了 100 倍) */
|
|
||||||
@NotNull(message = "bill.decimal.empty")
|
|
||||||
@DecimalMin(value = "0", message = "bill.decimal.limit")
|
|
||||||
private Long decimal;
|
|
||||||
|
|
||||||
/** 备注 */
|
|
||||||
private String remarks;
|
|
||||||
|
|
||||||
/** @return true 为收入账单 */
|
|
||||||
public boolean isRevenue() {
|
|
||||||
return revenueType != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return true 为支出账单 */
|
|
||||||
public boolean isExpenditure() {
|
|
||||||
return expenditureType != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.bill.mapper;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.bill.entity.Bill;
|
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 收支帐单表
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2022-04-01 16:26
|
|
||||||
*/
|
|
||||||
public interface BillMapper extends BaseMapper<Bill, Long> {
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.bill.service;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.bill.entity.Bill;
|
|
||||||
import com.imyeyu.spring.service.CreatableService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 收支帐单服务
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2022-04-01 16:24
|
|
||||||
*/
|
|
||||||
public interface BillService extends CreatableService<Bill> {
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.bill.service.implement;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.bill.entity.Bill;
|
|
||||||
import com.imyeyu.api.modules.bill.mapper.BillMapper;
|
|
||||||
import com.imyeyu.api.modules.bill.service.BillService;
|
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
|
||||||
import com.imyeyu.spring.service.AbstractEntityService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 收支账单服务
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2022-04-01 16:25
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class BillServiceImplement extends AbstractEntityService<Bill, Long> implements BillService {
|
|
||||||
|
|
||||||
private final BillMapper mapper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected BaseMapper<Bill, Long> mapper() {
|
|
||||||
return mapper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.imyeyu.api.modules.blog.entity;
|
package com.imyeyu.api.modules.blog.entity;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.imyeyu.api.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;
|
||||||
@@ -48,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;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.imyeyu.api.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 {
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.imyeyu.api.modules.blog.entity;
|
package com.imyeyu.api.modules.blog.entity;
|
||||||
|
|
||||||
import com.imyeyu.api.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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.blog.mapper;
|
package com.imyeyu.api.modules.blog.mapper;
|
||||||
|
|
||||||
import com.imyeyu.api.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;
|
||||||
|
|
||||||
@@ -15,14 +16,14 @@ import java.util.List;
|
|||||||
public interface ArticleMapper extends BaseMapper<Article, Long> {
|
public interface ArticleMapper extends BaseMapper<Article, Long> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
long count();
|
long countByPage(Page page);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
List<Article> list(long offset, int limit);
|
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);
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ public class ArticleServiceImplement extends AbstractEntityService<Article, Long
|
|||||||
@Override
|
@Override
|
||||||
public PageResult<Article> page(Page page) {
|
public PageResult<Article> page(Page page) {
|
||||||
PageResult<Article> result = new PageResult<>();
|
PageResult<Article> result = new PageResult<>();
|
||||||
result.setList(mapper.list(page.getOffset(), page.getLimit()));
|
result.setList(mapper.selectByPage(page));
|
||||||
result.setTotal(mapper.count());
|
result.setTotal(mapper.countByPage(page));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package com.imyeyu.api.modules.common.bean;
|
package com.imyeyu.api.modules.common.bean;
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2025-10-20 15:04
|
* @since 2025-10-20 15:04
|
||||||
@@ -18,22 +16,4 @@ public class MediaAttach {
|
|||||||
|
|
||||||
THUMB
|
THUMB
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2025-10-20 15:04
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class ExtData {
|
|
||||||
|
|
||||||
private Long sourceId;
|
|
||||||
|
|
||||||
private String sourceMongoId;
|
|
||||||
|
|
||||||
private boolean isImage;
|
|
||||||
|
|
||||||
private boolean isVideo;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -131,7 +131,7 @@ public enum SettingKey {
|
|||||||
|
|
||||||
MUSIC_CONTROLLER_URI,
|
MUSIC_CONTROLLER_URI,
|
||||||
|
|
||||||
// ---------- ----------
|
// ---------- 日记 ----------
|
||||||
|
|
||||||
JOURNAL_KEY,
|
JOURNAL_KEY,
|
||||||
|
|
||||||
@@ -139,7 +139,26 @@ public enum SettingKey {
|
|||||||
|
|
||||||
JOURNAL_APP_SECRET,
|
JOURNAL_APP_SECRET,
|
||||||
|
|
||||||
JOURNAL_TRAVEL,
|
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,
|
||||||
|
|
||||||
// ---------- 系统 ----------
|
// ---------- 系统 ----------
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.common.bean;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2025-09-27 01:47
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class TempFileMetaData {
|
|
||||||
|
|
||||||
private String id;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
private String originalName;
|
|
||||||
|
|
||||||
private Path path;
|
|
||||||
|
|
||||||
private Long lastAccessAt;
|
|
||||||
}
|
|
||||||
@@ -8,13 +8,10 @@ import com.imyeyu.api.modules.common.entity.Comment;
|
|||||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||||
import com.imyeyu.api.modules.common.service.CommentReplyService;
|
import com.imyeyu.api.modules.common.service.CommentReplyService;
|
||||||
import com.imyeyu.api.modules.common.service.CommentService;
|
import com.imyeyu.api.modules.common.service.CommentService;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
|
||||||
import com.imyeyu.api.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.imyeyu.api.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.bean.CaptchaFrom;
|
||||||
import com.imyeyu.api.modules.common.bean.ImageType;
|
import com.imyeyu.api.modules.common.bean.ImageType;
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||||
@@ -11,15 +11,16 @@ import com.imyeyu.api.modules.common.entity.Task;
|
|||||||
import com.imyeyu.api.modules.common.entity.Template;
|
import com.imyeyu.api.modules.common.entity.Template;
|
||||||
import com.imyeyu.api.modules.common.entity.Version;
|
import com.imyeyu.api.modules.common.entity.Version;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
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.FeedbackService;
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
import com.imyeyu.api.modules.common.service.TaskService;
|
import com.imyeyu.api.modules.common.service.TaskService;
|
||||||
import com.imyeyu.api.modules.common.service.TempFileService;
|
import com.imyeyu.api.modules.common.service.TempFileService;
|
||||||
import com.imyeyu.api.modules.common.service.TemplateService;
|
import com.imyeyu.api.modules.common.service.TemplateService;
|
||||||
import com.imyeyu.api.modules.common.service.VersionService;
|
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.FeedbackRequest;
|
||||||
import com.imyeyu.api.modules.common.vo.TempFileResponse;
|
import com.imyeyu.api.modules.common.vo.TempFileResponse;
|
||||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentView;
|
|
||||||
import com.imyeyu.api.modules.system.util.ResourceHandler;
|
import com.imyeyu.api.modules.system.util.ResourceHandler;
|
||||||
import com.imyeyu.api.util.CaptchaManager;
|
import com.imyeyu.api.util.CaptchaManager;
|
||||||
import com.imyeyu.io.IO;
|
import com.imyeyu.io.IO;
|
||||||
@@ -46,6 +47,7 @@ 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;
|
||||||
@@ -56,6 +58,7 @@ 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.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;
|
||||||
@@ -63,12 +66,8 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -94,19 +93,75 @@ public class CommonController {
|
|||||||
private final TemplateService templateService;
|
private final TemplateService templateService;
|
||||||
private final TempFileService tempFileService;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取验证码
|
* 获取验证码
|
||||||
*
|
*
|
||||||
@@ -214,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,8 +318,8 @@ public class CommonController {
|
|||||||
@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
|
||||||
@@ -376,67 +431,87 @@ public class CommonController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传临时文件
|
||||||
|
*
|
||||||
|
* @param files 文件列表
|
||||||
|
* @param ttl 缓存时长(毫秒),最长 3 天(259200000ms)
|
||||||
|
* @return 临时文件响应列表
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@PostMapping("/temp/file/upload")
|
@PostMapping("/temp/file/upload")
|
||||||
public List<TempFileResponse> uploadFile(@RequestParam("file") List<MultipartFile> files) {
|
public List<TempFileResponse> tempFileUpload(@RequestParam("file") List<MultipartFile> files, @RequestParam(value = "ttl", required = false) Long ttl) {
|
||||||
return tempFileService.store(files);
|
return tempFileService.store(files, ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取临时文件
|
||||||
|
*
|
||||||
|
* @param mongoId mongoId
|
||||||
|
* @param req 请求
|
||||||
|
* @param resp 返回
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@IgnoreGlobalReturn
|
@IgnoreGlobalReturn
|
||||||
@GetMapping("/temp/file/read/{fileId}")
|
@GetMapping("/temp/file/read/{mongoId}")
|
||||||
public void tempFileRead(@PathVariable String fileId, HttpServletRequest req, HttpServletResponse resp) {
|
public void tempFileRead(@PathVariable String mongoId, HttpServletRequest req, HttpServletResponse resp) {
|
||||||
try {
|
try {
|
||||||
File file = tempFileService.get(fileId);
|
Attachment attach = attachmentService.getByMongoId(mongoId);
|
||||||
if (TimiJava.isEmpty(file) && file.exists()) {
|
if (TimiJava.isEmpty(attach)) {
|
||||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Path filePath = file.toPath();
|
GridFSFile file = attachmentService.readByMongoId(attach.getMongoId());
|
||||||
resp.setContentLengthLong(Files.size(filePath));
|
GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(file.getObjectId());
|
||||||
String mimeType = new Tika().detect(filePath);
|
GridFsResource gridFsResource = new GridFsResource(file, downloadStream);
|
||||||
if (TimiJava.isNotEmpty(mimeType)) {
|
req.setAttribute(ResourceHandler.ATTR_TYPE, ResourceHandler.Type.MONGO);
|
||||||
resp.setContentType(mimeType);
|
req.setAttribute(ResourceHandler.ATTR_VALUE, gridFsResource);
|
||||||
}
|
|
||||||
req.setAttribute(ResourceHandler.ATTR_TYPE, ResourceHandler.Type.FILE);
|
resp.setContentType(attach.getMimeType());
|
||||||
req.setAttribute(ResourceHandler.ATTR_VALUE, filePath);
|
|
||||||
resourceHandler.handleRequest(req, resp);
|
resourceHandler.handleRequest(req, resp);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载临时文件
|
||||||
|
*
|
||||||
|
* @param mongoId
|
||||||
|
* @param resp
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@IgnoreGlobalReturn
|
@IgnoreGlobalReturn
|
||||||
@RequestMapping("/temp/file/download/{fileId}")
|
@RequestMapping("/temp/file/download/{mongoId}")
|
||||||
public void tempFileDownload(@PathVariable String fileId, HttpServletResponse resp) {
|
public void tempFileDownload(@PathVariable String mongoId, HttpServletResponse resp) {
|
||||||
try {
|
try {
|
||||||
File file = tempFileService.get(fileId);
|
Attachment attach = attachmentService.getByMongoId(mongoId);
|
||||||
if (TimiJava.isEmpty(file) && file.exists()) {
|
if (TimiJava.isEmpty(attach)) {
|
||||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String mimeType = new Tika().detect(file);
|
resp.setContentType(attach.getMimeType());
|
||||||
resp.setContentType(mimeType);
|
resp.setHeader("Content-Disposition", Network.getFileDownloadHeader(attach.getName()));
|
||||||
resp.setHeader("Content-Disposition", Network.getFileDownloadHeader(file.getName()));
|
|
||||||
resp.setHeader("Accept-Ranges", "bytes");
|
resp.setHeader("Accept-Ranges", "bytes");
|
||||||
|
|
||||||
RequestRange range = TimiSpring.requestRange(file.length());
|
GridFSFile file = attachmentService.readByMongoId(mongoId);
|
||||||
|
@Cleanup
|
||||||
|
GridFSDownloadStream downloadStream = gridFSBucket.openDownloadStream(file.getObjectId());
|
||||||
|
RequestRange range = TimiSpring.getRequestRange(attach.getSize());
|
||||||
if (range == null) {
|
if (range == null) {
|
||||||
// 完整文件
|
// 完整文件
|
||||||
resp.setContentLengthLong(file.length());
|
resp.setContentLengthLong(attach.getSize());
|
||||||
resp.setStatus(HttpServletResponse.SC_OK);
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
IO.toOutputStream(resp.getOutputStream(), file);
|
IO.toOutputStream(downloadStream, resp.getOutputStream());
|
||||||
} else {
|
} else {
|
||||||
// 分片文件
|
// 分片文件
|
||||||
resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||||
resp.setHeader("Content-Range", "bytes %s-%s/%s".formatted(range.getStart(), range.getEnd(), file.length()));
|
resp.setHeader("Content-Range", "bytes %s-%s/%s".formatted(range.getStart(), range.getEnd(), attach.getSize()));
|
||||||
resp.setContentLengthLong(range.getLength());
|
resp.setContentLengthLong(range.getLength());
|
||||||
|
IO.toOutputStream(downloadStream, resp.getOutputStream(), range.getStart(), range.getEnd());
|
||||||
@Cleanup RandomAccessFile raf = new RandomAccessFile(file, "r");
|
resp.flushBuffer();
|
||||||
raf.seek(range.getStart());
|
|
||||||
IO.toOutputStream(resp.getOutputStream(), raf, range.getStart(), range.getLength());
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("download error", e);
|
log.error("download error", e);
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package com.imyeyu.api.modules.common.controller;
|
package com.imyeyu.api.modules.common.controller;
|
||||||
|
|
||||||
import com.imyeyu.java.TimiJava;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import com.imyeyu.api.annotation.CaptchaValid;
|
import com.imyeyu.api.annotation.CaptchaValid;
|
||||||
import com.imyeyu.api.annotation.EnableSetting;
|
import com.imyeyu.api.annotation.EnableSetting;
|
||||||
import com.imyeyu.api.bean.CaptchaFrom;
|
import com.imyeyu.api.bean.CaptchaFrom;
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
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.CommentReply;
|
||||||
import com.imyeyu.api.modules.common.entity.UserConfig;
|
import com.imyeyu.api.modules.common.entity.UserConfig;
|
||||||
import com.imyeyu.api.modules.common.entity.UserPrivacy;
|
import com.imyeyu.api.modules.common.entity.UserPrivacy;
|
||||||
@@ -15,10 +14,6 @@ import com.imyeyu.api.modules.common.service.UserConfigService;
|
|||||||
import com.imyeyu.api.modules.common.service.UserPrivacyService;
|
import com.imyeyu.api.modules.common.service.UserPrivacyService;
|
||||||
import com.imyeyu.api.modules.common.service.UserProfileService;
|
import com.imyeyu.api.modules.common.service.UserProfileService;
|
||||||
import com.imyeyu.api.modules.common.service.UserService;
|
import com.imyeyu.api.modules.common.service.UserService;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
|
||||||
import com.imyeyu.api.modules.common.vo.comment.UserCommentPage;
|
|
||||||
import com.imyeyu.api.modules.common.vo.user.EmailVerifyCallbackRequest;
|
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.LoginRequest;
|
||||||
import com.imyeyu.api.modules.common.vo.user.LoginResponse;
|
import com.imyeyu.api.modules.common.vo.user.LoginResponse;
|
||||||
@@ -27,11 +22,13 @@ 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.UpdatePasswordRequest;
|
||||||
import com.imyeyu.api.modules.common.vo.user.UserRequest;
|
import com.imyeyu.api.modules.common.vo.user.UserRequest;
|
||||||
import com.imyeyu.api.modules.common.vo.user.UserView;
|
import com.imyeyu.api.modules.common.vo.user.UserView;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
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());
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
package com.imyeyu.api.modules.common.entity;
|
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.bean.MultilingualHandler;
|
||||||
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.java.ref.Ref;
|
import com.imyeyu.java.ref.Ref;
|
||||||
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
import com.imyeyu.spring.entity.Entity;
|
import com.imyeyu.spring.entity.Entity;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2023-08-15 10:17
|
* @since 2023-08-15 10:17
|
||||||
@@ -44,14 +50,20 @@ public class Attachment extends Entity implements MultilingualHandler {
|
|||||||
/** 镜像 */
|
/** 镜像 */
|
||||||
MIRROR,
|
MIRROR,
|
||||||
|
|
||||||
|
/** 日记 */
|
||||||
JOURNAL,
|
JOURNAL,
|
||||||
|
|
||||||
|
/** 日记出行 */
|
||||||
JOURNAL_TRAVEL,
|
JOURNAL_TRAVEL,
|
||||||
|
|
||||||
|
/** 日记瞬间 */
|
||||||
JOURNAL_MOMENT,
|
JOURNAL_MOMENT,
|
||||||
|
|
||||||
/** 系统 */
|
/** 系统 */
|
||||||
SYSTEM
|
SYSTEM,
|
||||||
|
|
||||||
|
/** 临时文件 */
|
||||||
|
TEMP_FILE
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BizType bizType;
|
protected BizType bizType;
|
||||||
@@ -67,14 +79,28 @@ public class Attachment extends Entity implements MultilingualHandler {
|
|||||||
|
|
||||||
protected String name;
|
protected String name;
|
||||||
|
|
||||||
|
protected String mimeType;
|
||||||
|
|
||||||
|
protected JsonNode metadata;
|
||||||
|
|
||||||
protected Long size;
|
protected Long size;
|
||||||
|
|
||||||
protected String md5;
|
protected String md5;
|
||||||
|
|
||||||
protected String ext;
|
protected String uploaderIp;
|
||||||
|
|
||||||
|
protected Boolean isDestroyed;
|
||||||
|
|
||||||
protected Long destroyAt;
|
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) {
|
public void setAttachTypeValue(Enum<?> attachType) {
|
||||||
this.attachType = attachType.toString();
|
this.attachType = attachType.toString();
|
||||||
}
|
}
|
||||||
@@ -82,5 +108,4 @@ public class Attachment extends Entity implements MultilingualHandler {
|
|||||||
public <T extends Enum<T>> T getAttachTypeValue(Class<T> attachTypeClass) {
|
public <T extends Enum<T>> T getAttachTypeValue(Class<T> attachTypeClass) {
|
||||||
return Ref.toType(attachTypeClass, attachType);
|
return Ref.toType(attachTypeClass, attachType);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
package com.imyeyu.api.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,11 +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.api.modules.blog.service.implement.ArticleServiceImplement;
|
|
||||||
import com.imyeyu.api.modules.common.bean.CommentSupport;
|
import java.util.List;
|
||||||
import com.imyeyu.api.modules.git.service.implement.IssueServiceImplement;
|
|
||||||
import com.imyeyu.api.modules.git.service.implement.MergeServiceImplement;
|
|
||||||
import com.imyeyu.spring.entity.Entity;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 评论
|
* 评论
|
||||||
@@ -64,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.imyeyu.api.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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package com.imyeyu.api.modules.common.entity;
|
package com.imyeyu.api.modules.common.entity;
|
||||||
|
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
import com.imyeyu.java.bean.Language;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.spring.entity.Creatable;
|
||||||
import com.imyeyu.java.ref.Ref;
|
import com.imyeyu.spring.entity.Deletable;
|
||||||
import com.imyeyu.api.TimiServerAPI;
|
import com.imyeyu.spring.entity.IDEntity;
|
||||||
import com.imyeyu.spring.entity.Entity;
|
import com.imyeyu.spring.entity.Updatable;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
@@ -16,52 +16,13 @@ import java.lang.reflect.Field;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class Multilingual extends Entity {
|
public class Multilingual extends Language implements Serializable, IDEntity<Long>, Creatable, Updatable, Deletable {
|
||||||
|
|
||||||
protected String key;
|
protected Long id;
|
||||||
|
|
||||||
protected String zhCN;
|
protected Long createdAt;
|
||||||
|
|
||||||
protected String zhTW;
|
protected Long updatedAt;
|
||||||
|
|
||||||
protected String enUS;
|
protected Long deletedAt;
|
||||||
|
|
||||||
protected String ruRU;
|
|
||||||
|
|
||||||
protected String koKR;
|
|
||||||
|
|
||||||
protected String jaJP;
|
|
||||||
|
|
||||||
protected String deDE;
|
|
||||||
|
|
||||||
/** @return 根据用户环境获取语言值 */
|
|
||||||
public String getValue() {
|
|
||||||
try {
|
|
||||||
Field field = Ref.getField(getClass(), TimiServerAPI.getUserLanguage().toString().replace("_", ""));
|
|
||||||
if (field == null) {
|
|
||||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("TODO not support language");
|
|
||||||
}
|
|
||||||
return Ref.getFieldValue(this, field, String.class);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取指定语言值
|
|
||||||
*
|
|
||||||
* @param language 指定语言
|
|
||||||
* @return 值
|
|
||||||
*/
|
|
||||||
public String getValue(com.imyeyu.java.bean.Language language) {
|
|
||||||
try {
|
|
||||||
Field field = Ref.getField(getClass(), language.toString().replace("_", ""));
|
|
||||||
if (field == null) {
|
|
||||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("TODO not support language");
|
|
||||||
}
|
|
||||||
return Ref.getFieldValue(this, field, String.class);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.imyeyu.api.modules.common.mapper;
|
|||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.spring.bean.Page;
|
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;
|
||||||
@@ -11,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);
|
||||||
@@ -25,9 +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);
|
||||||
|
|
||||||
List<Attachment> listByBizId(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypes, Page page);
|
List<Attachment> listByBizId(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypes, Page<Attachment> page);
|
||||||
|
|
||||||
long countByBizId(Attachment.BizType bizType, Long bizId, List<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);
|
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
package com.imyeyu.api.modules.common.mapper;
|
package com.imyeyu.api.modules.common.mapper;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Comment;
|
import com.imyeyu.api.modules.common.entity.Comment;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
import org.apache.ibatis.annotations.Update;
|
import org.apache.ibatis.annotations.Update;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 评论
|
* 评论
|
||||||
*
|
*
|
||||||
@@ -17,30 +12,8 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface CommentMapper extends BaseMapper<Comment, Long> {
|
public interface CommentMapper extends BaseMapper<Comment, Long> {
|
||||||
|
|
||||||
@Override
|
|
||||||
long count();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
List<Comment> list(long offset, int limit);
|
|
||||||
|
|
||||||
@Select("SELECT * FROM comment WHERE id = #{id}" + NOT_DELETE)
|
|
||||||
Comment select(Long id);
|
|
||||||
|
|
||||||
@Update("UPDATE comment SET deleted_at = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000) WHERE id = #{id}")
|
|
||||||
@Override
|
|
||||||
void delete(Long id);
|
|
||||||
|
|
||||||
@Select("SELECT COUNT(1) FROM comment WHERE biz_type = #{bizType} AND biz_id = #{bizId}" + NOT_DELETE)
|
|
||||||
long count(Comment.BizType bizType, Long bizId);
|
|
||||||
|
|
||||||
long countAll(Comment.BizType bizType, Long bizId);
|
long countAll(Comment.BizType bizType, Long bizId);
|
||||||
|
|
||||||
List<CommentView> list(Comment.BizType bizType, Long bizId, Long offset, int limit, LinkedHashMap<String, OrderType> orderMap);
|
|
||||||
|
|
||||||
long countByUserId(Long userId);
|
|
||||||
|
|
||||||
List<CommentView> listByUserId(Long userId, Long offset, int limit, LinkedHashMap<String, OrderType> orderMap);
|
|
||||||
|
|
||||||
@Update("UPDATE comment SET deleted_at = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000) WHERE user_id = #{userId} ")
|
@Update("UPDATE comment SET deleted_at = FLOOR(UNIX_TIMESTAMP(NOW(3)) * 1000) WHERE user_id = #{userId} ")
|
||||||
void deleteByUserId(Long userId);
|
void deleteByUserId(Long userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.imyeyu.api.modules.common.mapper;
|
|||||||
|
|
||||||
import com.imyeyu.api.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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,7 @@ package com.imyeyu.api.modules.common.mapper;
|
|||||||
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
import org.apache.ibatis.annotations.Update;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 评论回复
|
* 评论回复
|
||||||
@@ -17,19 +11,4 @@ import java.util.List;
|
|||||||
* @since 2021-08-24 10:36
|
* @since 2021-08-24 10:36
|
||||||
*/
|
*/
|
||||||
public interface CommentReplyMapper extends BaseMapper<CommentReply, Long> {
|
public interface CommentReplyMapper extends BaseMapper<CommentReply, Long> {
|
||||||
|
|
||||||
@Select("SELECT * FROM comment_reply WHERE sender_id = #{senderId}" + NOT_DELETE)
|
|
||||||
List<CommentReply> listAllBySenderId(Long senderId);
|
|
||||||
|
|
||||||
@Select("SELECT COUNT(1) FROM comment_reply WHERE ${bizType.column} = #{bizId}" + NOT_DELETE)
|
|
||||||
long countByBizType(CommentReplyPage.BizType bizType, Long bizId);
|
|
||||||
|
|
||||||
@Select("SELECT * FROM comment_reply WHERE ${bizType.column} = #{bizId} AND ignored_at IS NULL" + NOT_DELETE + PAGE)
|
|
||||||
List<CommentReplyView> listByBizType(CommentReplyPage.BizType bizType, Long bizId, Long offset, int limit);
|
|
||||||
|
|
||||||
@Update("UPDATE comment_reply SET deleted_at = " + UNIX_TIME + " WHERE sender_id = #{userId} OR receiver_id = #{userId}")
|
|
||||||
void deleteByUserId(Long userId);
|
|
||||||
|
|
||||||
@Update("UPDATE comment_reply SET deleted_at = " + UNIX_TIME + " WHERE comment_id = #{commentId}")
|
|
||||||
void deleteByCommentId(Long commentId);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.common.service;
|
package com.imyeyu.api.modules.common.service;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
|
|
||||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentView;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.spring.bean.Page;
|
import com.imyeyu.spring.service.BaseService;
|
||||||
import com.imyeyu.spring.bean.PageResult;
|
|
||||||
import com.imyeyu.spring.service.DeletableService;
|
|
||||||
import com.imyeyu.spring.service.DestroyableService;
|
|
||||||
import com.imyeyu.spring.service.GettableService;
|
|
||||||
import com.imyeyu.spring.service.PageableService;
|
|
||||||
import com.imyeyu.spring.service.UpdatableService;
|
|
||||||
import com.mongodb.client.gridfs.model.GridFSFile;
|
import com.mongodb.client.gridfs.model.GridFSFile;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -24,22 +16,9 @@ import java.util.List;
|
|||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2023-08-15 10:21
|
* @since 2023-08-15 10:21
|
||||||
*/
|
*/
|
||||||
public interface AttachmentService extends GettableService<Attachment, Long>, PageableService<Attachment>, UpdatableService<Attachment>, DeletableService<Long>, DestroyableService<Long> {
|
public interface AttachmentService extends BaseService<Attachment, Long> {
|
||||||
|
|
||||||
/**
|
Attachment createMedia(Attachment attachment);
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param request
|
|
||||||
*/
|
|
||||||
void create(AttachmentRequest request);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建媒体附件,同步创建缩略图
|
|
||||||
*
|
|
||||||
* @param request 附件请求
|
|
||||||
* @return 缩略图附件
|
|
||||||
*/
|
|
||||||
Attachment createMedia(AttachmentRequest request) throws TimiException;
|
|
||||||
|
|
||||||
void deleteMedia(Long thumbId) throws TimiException;
|
void deleteMedia(Long thumbId) throws TimiException;
|
||||||
|
|
||||||
@@ -49,8 +28,6 @@ public interface AttachmentService extends GettableService<Attachment, Long>, Pa
|
|||||||
|
|
||||||
Attachment getByMongoId(String mongoId);
|
Attachment getByMongoId(String mongoId);
|
||||||
|
|
||||||
AttachmentView viewByMongoId(String mongoId);
|
|
||||||
|
|
||||||
GridFSFile readByMongoId(String mongoId);
|
GridFSFile readByMongoId(String mongoId);
|
||||||
|
|
||||||
InputStream getInputStreamByMongoId(String mongoId);
|
InputStream getInputStreamByMongoId(String mongoId);
|
||||||
@@ -79,7 +56,9 @@ public interface AttachmentService extends GettableService<Attachment, Long>, Pa
|
|||||||
*/
|
*/
|
||||||
long countByBizId(Attachment.BizType bizType, Long bizId, Enum<?> ...attachTypes);
|
long countByBizId(Attachment.BizType bizType, Long bizId, Enum<?> ...attachTypes);
|
||||||
|
|
||||||
List<Attachment> listByMd5s(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypeList, List<String> md5s) throws TimiException;
|
List<Attachment> listByMd5s(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypeList, List<String> md5s);
|
||||||
|
|
||||||
PageResult<Attachment> pageByBizId(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypeList, Page page) throws TimiException;
|
void deleteByBizId(Attachment.BizType bizType, long bizId, Enum<?> ...attachTypes);
|
||||||
|
|
||||||
|
List<Attachment> listNeedDestroy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.common.service;
|
package com.imyeyu.api.modules.common.service;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
import com.imyeyu.spring.service.BaseService;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
|
||||||
import com.imyeyu.spring.bean.PageResult;
|
|
||||||
import com.imyeyu.spring.service.CreatableService;
|
|
||||||
import com.imyeyu.spring.service.DeletableService;
|
|
||||||
import com.imyeyu.spring.service.GettableService;
|
|
||||||
import com.imyeyu.spring.service.UpdatableService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 评论回复服务
|
* 评论回复服务
|
||||||
@@ -15,7 +9,5 @@ import com.imyeyu.spring.service.UpdatableService;
|
|||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2021-08-24 10:33
|
* @since 2021-08-24 10:33
|
||||||
*/
|
*/
|
||||||
public interface CommentReplyService extends CreatableService<CommentReply>, GettableService<CommentReply, Long>, UpdatableService<CommentReply>, DeletableService<Long> {
|
public interface CommentReplyService extends BaseService<CommentReply, Long> {
|
||||||
|
|
||||||
PageResult<CommentReplyView> pageByBizType(CommentReplyPage page);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.common.service;
|
package com.imyeyu.api.modules.common.service;
|
||||||
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import com.imyeyu.api.modules.common.entity.Comment;
|
import com.imyeyu.api.modules.common.entity.Comment;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
import com.imyeyu.spring.service.BaseService;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.UserCommentPage;
|
|
||||||
import com.imyeyu.api.modules.git.vo.issue.CommentPage;
|
|
||||||
import com.imyeyu.spring.bean.PageResult;
|
|
||||||
import com.imyeyu.spring.service.CreatableService;
|
|
||||||
import com.imyeyu.spring.service.DeletableService;
|
|
||||||
import com.imyeyu.spring.service.GettableService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 评论服务
|
* 评论服务
|
||||||
@@ -16,16 +9,5 @@ import com.imyeyu.spring.service.GettableService;
|
|||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2021-02-23 21:32
|
* @since 2021-02-23 21:32
|
||||||
*/
|
*/
|
||||||
public interface CommentService extends CreatableService<Comment>, GettableService<Comment, Long>, DeletableService<Long> {
|
public interface CommentService extends BaseService<Comment, Long> {
|
||||||
|
|
||||||
PageResult<CommentView> pageByBizId(CommentPage page);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取用户评论页面
|
|
||||||
*
|
|
||||||
* @param userCommentPage 页面参数
|
|
||||||
* @return 页面列表
|
|
||||||
* @throws TimiException 服务异常
|
|
||||||
*/
|
|
||||||
PageResult<CommentView> pageByUserId(UserCommentPage userCommentPage);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.imyeyu.api.modules.common.service;
|
package com.imyeyu.api.modules.common.service;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.google.gson.JsonElement;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.google.gson.JsonObject;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.spring.service.UpdatableService;
|
import com.imyeyu.spring.service.UpdatableService;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -60,15 +60,15 @@ public interface SettingService extends UpdatableService<Setting> {
|
|||||||
*/
|
*/
|
||||||
boolean not(SettingKey key);
|
boolean not(SettingKey key);
|
||||||
|
|
||||||
JsonElement getAsJsonElement(SettingKey key);
|
JsonNode getAsJsonNode(SettingKey key);
|
||||||
|
|
||||||
JsonObject getAsJsonObject(SettingKey key);
|
ObjectNode getAsJsonObject(SettingKey key);
|
||||||
|
|
||||||
JsonArray getAsJsonArray(SettingKey key);
|
ArrayNode getAsArrayNode(SettingKey key);
|
||||||
|
|
||||||
<T> T fromJson(SettingKey key, Class<T> clazz);
|
<T> T fromJson(SettingKey key, Class<T> clazz);
|
||||||
|
|
||||||
<T> T fromJson(SettingKey key, TypeToken<T> typeToken);
|
<T> T fromJson(SettingKey key, TypeReference<T> typeReference);
|
||||||
|
|
||||||
<T> T fromYaml(SettingKey key, Class<T> clazz);
|
<T> T fromYaml(SettingKey key, Class<T> clazz);
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.common.service;
|
package com.imyeyu.api.modules.common.service;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.bean.TempFileMetaData;
|
|
||||||
import com.imyeyu.api.modules.common.vo.TempFileResponse;
|
import com.imyeyu.api.modules.common.vo.TempFileResponse;
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,9 +11,5 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface TempFileService {
|
public interface TempFileService {
|
||||||
|
|
||||||
List<TempFileResponse> store(List<MultipartFile> files) throws TimiException;
|
List<TempFileResponse> store(List<MultipartFile> files, Long ttl);
|
||||||
|
|
||||||
File get(String id) throws TimiException;
|
|
||||||
|
|
||||||
TempFileMetaData metadata(String id) throws TimiException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
package com.imyeyu.api.modules.common.service.implement;
|
package com.imyeyu.api.modules.common.service.implement;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.common.bean.MediaAttach;
|
import com.imyeyu.api.modules.common.bean.MediaAttach;
|
||||||
|
import com.imyeyu.api.modules.common.bean.Metadata;
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.mapper.AttachmentMapper;
|
import com.imyeyu.api.modules.common.mapper.AttachmentMapper;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
|
|
||||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentView;
|
|
||||||
import com.imyeyu.api.util.JavaCV;
|
import com.imyeyu.api.util.JavaCV;
|
||||||
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.network.Network;
|
import com.imyeyu.network.Network;
|
||||||
import com.imyeyu.spring.bean.Page;
|
import com.imyeyu.spring.TimiSpring;
|
||||||
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;
|
||||||
import com.imyeyu.utils.Time;
|
import com.imyeyu.utils.Time;
|
||||||
@@ -26,13 +25,14 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.coobird.thumbnailator.Thumbnails;
|
import net.coobird.thumbnailator.Thumbnails;
|
||||||
import org.apache.tika.Tika;
|
import org.apache.tika.Tika;
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.data.mongodb.core.query.Criteria;
|
import org.springframework.data.mongodb.core.query.Criteria;
|
||||||
import org.springframework.data.mongodb.core.query.Query;
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
|
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -55,7 +55,7 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
|
|||||||
|
|
||||||
private final AttachmentMapper mapper;
|
private final AttachmentMapper mapper;
|
||||||
|
|
||||||
private final Gson gson;
|
private final ObjectMapper jackson;
|
||||||
private final GridFSBucket gridFSBucket;
|
private final GridFSBucket gridFSBucket;
|
||||||
private final GridFsTemplate gridFsTemplate;
|
private final GridFsTemplate gridFsTemplate;
|
||||||
|
|
||||||
@@ -66,44 +66,36 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
|
|||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void destroy(Long id) {
|
public void create(Attachment attachment) {
|
||||||
try {
|
TimiException.required(attachment.getBizType(), "not found attachment.bizType");
|
||||||
Attachment attachment = get(id);
|
TimiException.required(attachment.getName(), "not found attachment.name");
|
||||||
gridFsTemplate.delete(Query.query(Criteria.where("_id").is(attachment.getMongoId())));
|
|
||||||
|
|
||||||
attachment.setDeletedAt(Time.now());
|
|
||||||
attachment.setDestroyAt(Time.now());
|
|
||||||
mapper.update(attachment);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("delete mongo file error", e);
|
|
||||||
throw new TimiException(TimiCode.ERROR).msgKey("TODO delete mongo file error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
|
||||||
@Override
|
|
||||||
public void create(AttachmentRequest request) {
|
|
||||||
TimiException.required(request.getBizType(), "not found request.bizType");
|
|
||||||
TimiException.required(request.getBizId(), "not found request.bizId");
|
|
||||||
TimiException.required(request.getName(), "not found request.name");
|
|
||||||
String mongoId = null;
|
String mongoId = null;
|
||||||
try {
|
try {
|
||||||
InputStream is = request.getInputStream();
|
InputStream is = attachment.getInputStream();
|
||||||
TimiException.required(is, "not found request.inputStream");
|
TimiException.required(is, "not found attachment.inputStream");
|
||||||
TimiException.requiredTrue(is.available() != 0, "empty request.inputStream");
|
|
||||||
|
|
||||||
StringBuilder mongoName = new StringBuilder(request.getBizType().toString());
|
StringBuilder mongoName = new StringBuilder(attachment.getBizType().toString());
|
||||||
if (TimiJava.isNotEmpty(request.getAttachType())) {
|
if (TimiJava.isNotEmpty(attachment.getAttachType())) {
|
||||||
mongoName.append("_").append(request.getAttachType().toUpperCase()).append("_");
|
mongoName.append("_").append(attachment.getAttachType().toUpperCase()).append("_");
|
||||||
}
|
}
|
||||||
mongoName.append(request.getName());
|
mongoName.append(attachment.getName());
|
||||||
|
|
||||||
mongoId = gridFsTemplate.store(request.getInputStream(), mongoName.toString()).toString();
|
mongoId = gridFsTemplate.store(attachment.getInputStream(), mongoName.toString()).toString();
|
||||||
GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(mongoId)));
|
GridFSFile gridFSFile = gridFsTemplate.findOne(new Query(Criteria.where("_id").is(mongoId)));
|
||||||
request.setMongoId(mongoId);
|
attachment.setMongoId(mongoId);
|
||||||
request.setSize(gridFSFile.getLength());
|
attachment.setSize(gridFSFile.getLength());
|
||||||
request.setMd5(IO.md5(gridFSBucket.openDownloadStream(gridFSFile.getObjectId())));
|
attachment.setMd5(IO.md5(gridFSBucket.openDownloadStream(gridFSFile.getObjectId())));
|
||||||
mapper.insert(request);
|
attachment.setMimeType(new Tika().detect(gridFSBucket.openDownloadStream(gridFSFile.getObjectId())));
|
||||||
|
attachment.setUploaderIp(TimiSpring.getRequestIP());
|
||||||
|
attachment.setIsDestroyed(false);
|
||||||
|
if (attachment.getMimeType().startsWith("image")) {
|
||||||
|
BufferedImage image = ImageIO.read(gridFSBucket.openDownloadStream(gridFSFile.getObjectId()));
|
||||||
|
attachment.setMetadata(TimiJava.defaultIfNull(attachment.getMetadata(), jackson.createObjectNode()));
|
||||||
|
ObjectNode metadata = (ObjectNode) attachment.getMetadata();
|
||||||
|
metadata.put("width", image.getWidth());
|
||||||
|
metadata.put("height", image.getHeight());
|
||||||
|
}
|
||||||
|
mapper.insert(attachment);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (mongoId != null) {
|
if (mongoId != null) {
|
||||||
gridFsTemplate.delete(Query.query(Criteria.where("_id").is(mongoId)));
|
gridFsTemplate.delete(Query.query(Criteria.where("_id").is(mongoId)));
|
||||||
@@ -115,53 +107,66 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
|
|||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public Attachment createMedia(AttachmentRequest request) throws TimiException {
|
public void destroy(Long id) {
|
||||||
TimiException.required(request.getName(), "not found name");
|
try {
|
||||||
TimiException.required(request.getBizType(), "not found bizType");
|
Attachment attach = mapper.selectRaw(id);
|
||||||
TimiException.required(request.getInputStream(), "not found inputStream");
|
GridFSFile file = readByMongoId(attach.getMongoId());
|
||||||
|
if (file != null) {
|
||||||
|
gridFsTemplate.delete(Query.query(Criteria.where("_id").is(attach.getMongoId())));
|
||||||
|
}
|
||||||
|
if (!attach.isDeleted()) {
|
||||||
|
attach.setDeletedAt(Time.now());
|
||||||
|
}
|
||||||
|
attach.setIsDestroyed(true);
|
||||||
|
attach.setDestroyAt(Time.now());
|
||||||
|
mapper.update(attach);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("delete mongo file error", e);
|
||||||
|
throw new TimiException(TimiCode.ERROR).msgKey("TODO delete mongo file error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public Attachment createMedia(Attachment attachment) throws TimiException {
|
||||||
try {
|
try {
|
||||||
// 储存源文件
|
// 储存源文件
|
||||||
request.setAttachTypeValue(MediaAttach.Type.SOURCE);
|
attachment.setAttachTypeValue(MediaAttach.Type.SOURCE);
|
||||||
create(request);
|
create(attachment);
|
||||||
|
|
||||||
// 生成缩略图
|
// 生成缩略图
|
||||||
InputStream mimeStream = getInputStreamByMongoId(request.getMongoId());
|
InputStream mimeStream = attachment.openInputStream();
|
||||||
String mimeType = new Tika().detect(mimeStream);
|
String mimeType = new Tika().detect(mimeStream);
|
||||||
boolean isImage = false;
|
|
||||||
|
|
||||||
InputStream sourceStream = getInputStreamByMongoId(request.getMongoId());
|
InputStream sourceStream = attachment.openInputStream();
|
||||||
ByteArrayOutputStream thumbStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream thumbStream = new ByteArrayOutputStream();
|
||||||
switch (mimeType) {
|
switch (mimeType) {
|
||||||
case "image/png", "image/bmp", "image/jpeg" -> {
|
case "image/png", "image/bmp", "image/jpeg" -> Thumbnails.of(sourceStream).width(256).keepAspectRatio(true).toOutputStream(thumbStream);
|
||||||
isImage = true;
|
|
||||||
Thumbnails.of(sourceStream).width(256).keepAspectRatio(true).toOutputStream(thumbStream);
|
|
||||||
}
|
|
||||||
case "video/mp4", "video/quicktime" -> {
|
case "video/mp4", "video/quicktime" -> {
|
||||||
log.info("capturing thumbnail: {}", request.getName());
|
log.info("capturing thumbnail: {}", attachment.getName());
|
||||||
long start = Time.now();
|
long start = Time.now();
|
||||||
File tempFile = IO.file("temp/%s_%s".formatted(UUID.randomUUID().toString(), request.getName()));
|
File tempFile = IO.file("temp/%s_%s".formatted(UUID.randomUUID().toString(), attachment.getName()));
|
||||||
try {
|
try {
|
||||||
IO.toFile(tempFile, sourceStream);
|
IO.toFile(tempFile, sourceStream);
|
||||||
ByteArrayOutputStream baos = JavaCV.captureThumbnail(IO.getInputStream(tempFile), 2);
|
ByteArrayOutputStream baos = JavaCV.captureThumbnail(IO.getInputStream(tempFile), 2);
|
||||||
Thumbnails.of(IO.toInputStream(baos)).width(256).keepAspectRatio(true).toOutputStream(thumbStream);
|
Thumbnails.of(IO.toInputStream(baos)).width(256).keepAspectRatio(true).toOutputStream(thumbStream);
|
||||||
log.info("captured thumbnail: {} at {} ms", request.getName(), Time.now() - start);
|
log.info("captured thumbnail: {} at {} ms", attachment.getName(), Time.now() - start);
|
||||||
} finally {
|
} finally {
|
||||||
IO.destroy(tempFile);
|
IO.destroy(tempFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MediaAttach.ExtData extData = new MediaAttach.ExtData();
|
Metadata.ThumbImage thumbMetadata = new Metadata.ThumbImage();
|
||||||
extData.setImage(isImage);
|
thumbMetadata.setSourceId(attachment.getId());
|
||||||
extData.setVideo(!isImage);
|
thumbMetadata.setSourceMongoId(attachment.getMongoId());
|
||||||
extData.setSourceId(request.getId());
|
thumbMetadata.setSourceMimeType(mimeType);
|
||||||
extData.setSourceMongoId(request.getMongoId());
|
|
||||||
|
|
||||||
AttachmentRequest thumbAttach = new AttachmentRequest();
|
Attachment thumbAttach = new Attachment();
|
||||||
thumbAttach.setName(Network.simpleURIFileName(request.getName()) + ".png");
|
thumbAttach.setName(Network.simpleURIFileName(attachment.getName()) + ".png");
|
||||||
thumbAttach.setBizType(request.getBizType());
|
thumbAttach.setBizType(attachment.getBizType());
|
||||||
thumbAttach.setBizId(request.getBizId());
|
thumbAttach.setBizId(attachment.getBizId());
|
||||||
thumbAttach.setAttachTypeValue(MediaAttach.Type.THUMB);
|
thumbAttach.setAttachTypeValue(MediaAttach.Type.THUMB);
|
||||||
thumbAttach.setExt(gson.toJson(extData));
|
thumbAttach.setMetadata(jackson.valueToTree(thumbMetadata));
|
||||||
thumbAttach.setInputStream(new ByteArrayInputStream(thumbStream.toByteArray()));
|
thumbAttach.setInputStream(new ByteArrayInputStream(thumbStream.toByteArray()));
|
||||||
create(thumbAttach);
|
create(thumbAttach);
|
||||||
|
|
||||||
@@ -177,8 +182,8 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
|
|||||||
public void deleteMedia(Long thumbId) throws TimiException {
|
public void deleteMedia(Long thumbId) throws TimiException {
|
||||||
Attachment attachment = get(thumbId);
|
Attachment attachment = get(thumbId);
|
||||||
delete(attachment.getId());
|
delete(attachment.getId());
|
||||||
MediaAttach.ExtData data = gson.fromJson(attachment.getExt(), MediaAttach.ExtData.class);
|
Metadata.ThumbImage thumbMetadata = jackson.convertValue(attachment.getMetadata(), Metadata.ThumbImage.class);
|
||||||
delete(data.getSourceId());
|
delete(thumbMetadata.getSourceId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -196,22 +201,11 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
|
|||||||
return mapper.selectByMongoId(mongoId);
|
return mapper.selectByMongoId(mongoId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public AttachmentView viewByMongoId(String mongoId) {
|
|
||||||
Attachment attachment = getByMongoId(mongoId);
|
|
||||||
if (attachment == null) {
|
|
||||||
throw new TimiException(TimiCode.RESULT_NULL).msgKey("TODO not found attachment");
|
|
||||||
}
|
|
||||||
AttachmentView view = new AttachmentView();
|
|
||||||
BeanUtils.copyProperties(attachment, view);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GridFSFile readByMongoId(String mongoId) {
|
public GridFSFile readByMongoId(String mongoId) {
|
||||||
Attachment view = mapper.selectByMongoId(mongoId);
|
Attachment attach = mapper.selectByMongoId(mongoId);
|
||||||
TimiException.required(view, "not found attachment: %s".formatted(mongoId));
|
TimiException.required(attach, "not found attachment: %s".formatted(mongoId));
|
||||||
return gridFsTemplate.findOne(new Query(Criteria.where("_id").is(view.getMongoId())));
|
return gridFsTemplate.findOne(new Query(Criteria.where("_id").is(attach.getMongoId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -222,7 +216,7 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
|
|||||||
@Override
|
@Override
|
||||||
public byte[] readAllByMongoId(String mongoId) {
|
public byte[] readAllByMongoId(String mongoId) {
|
||||||
try {
|
try {
|
||||||
return IO.toBytes(getInputStreamByMongoId(mongoId));
|
return IO.toBytes(getByMongoId(mongoId).openInputStream());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new TimiException(TimiCode.ERROR, "TODO 读取失败");
|
throw new TimiException(TimiCode.ERROR, "TODO 读取失败");
|
||||||
}
|
}
|
||||||
@@ -244,10 +238,22 @@ public class AttachmentServiceImplement extends AbstractEntityService<Attachment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<Attachment> pageByBizId(Attachment.BizType bizType, Long bizId, List<Enum<?>> attachTypeList, Page page) throws TimiException {
|
public void deleteByBizId(Attachment.BizType bizType, long bizId, Enum<?>... attachTypes) {
|
||||||
PageResult<Attachment> result = new PageResult<>();
|
Attachment example = new Attachment();
|
||||||
result.setList(mapper.listByBizId(bizType, bizId, attachTypeList, page));
|
example.setBizType(bizType);
|
||||||
result.setTotal(mapper.countByBizId(bizType, bizId, attachTypeList));
|
example.setBizId(bizId);
|
||||||
return result;
|
if (TimiJava.isEmpty(attachTypes)) {
|
||||||
|
mapper.deleteAllByExample(example);
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < attachTypes.length; i++) {
|
||||||
|
example.setAttachTypeValue(attachTypes[i]);
|
||||||
|
mapper.deleteAllByExample(example);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Attachment> listNeedDestroy() {
|
||||||
|
return mapper.selectNeedDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package com.imyeyu.api.modules.common.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.common.service.ClipboardService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.util.Redis;
|
||||||
|
import com.imyeyu.utils.Time;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 共享剪切板服务实现
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-01-15 16:40
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ClipboardServiceImplement implements ClipboardService {
|
||||||
|
|
||||||
|
/** Redis key 前缀 */
|
||||||
|
private static final String KEY_PREFIX = "clipboard:";
|
||||||
|
|
||||||
|
/** 剪切板内容最大长度 */
|
||||||
|
private static final int MAX_CONTENT_SIZE = 10 * 1024 * 1024;
|
||||||
|
|
||||||
|
/** 剪切板缓存 */
|
||||||
|
private final Redis<String, String> redisClipboard;
|
||||||
|
|
||||||
|
/** SSE 订阅者 */
|
||||||
|
private final Map<String, List<SseEmitter>> emitterMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContent(String id) {
|
||||||
|
validateId(id);
|
||||||
|
return redisClipboard.get(getKey(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContent(String id, String content) {
|
||||||
|
validateId(id);
|
||||||
|
if (content == null) {
|
||||||
|
throw new TimiException(TimiCode.ARG_MISS).msgKey("剪切板内容不能为空");
|
||||||
|
}
|
||||||
|
byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
|
||||||
|
if (MAX_CONTENT_SIZE < contentBytes.length) {
|
||||||
|
throw new TimiException(TimiCode.ARG_BAD).msgKey("剪切板内容不能超过 10MB");
|
||||||
|
}
|
||||||
|
redisClipboard.set(getKey(id), content, Time.D * 7);
|
||||||
|
notifySubscribers(id, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SseEmitter subscribe(String id) {
|
||||||
|
validateId(id);
|
||||||
|
SseEmitter emitter = new SseEmitter(0L);
|
||||||
|
emitter.onCompletion(() -> removeEmitter(id, emitter));
|
||||||
|
emitter.onTimeout(() -> removeEmitter(id, emitter));
|
||||||
|
emitter.onError(e -> removeEmitter(id, emitter));
|
||||||
|
emitterMap.computeIfAbsent(id, key -> new CopyOnWriteArrayList<>()).add(emitter);
|
||||||
|
String content = getContent(id);
|
||||||
|
if (content != null) {
|
||||||
|
sendEvent(emitter, content);
|
||||||
|
}
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateId(String id) {
|
||||||
|
if (TimiJava.isEmpty(id)) {
|
||||||
|
throw new TimiException(TimiCode.ARG_MISS).msgKey("id 不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getKey(String id) {
|
||||||
|
return "%s%s".formatted(KEY_PREFIX, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifySubscribers(String id, String content) {
|
||||||
|
List<SseEmitter> emitterList = emitterMap.get(id);
|
||||||
|
if (emitterList == null || emitterList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (SseEmitter emitter : emitterList) {
|
||||||
|
boolean success;
|
||||||
|
try {
|
||||||
|
success = sendEvent(emitter, content);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("发送剪切板事件异常", e);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
|
completeEmitter(id, emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendEvent(SseEmitter emitter, String content) {
|
||||||
|
try {
|
||||||
|
emitter.send(SseEmitter.event().name("clipboard").data(content));
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.debug("剪切板连接已断开", e);
|
||||||
|
return false;
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
log.debug("剪切板 SSE 状态异常", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void completeEmitter(String id, SseEmitter emitter) {
|
||||||
|
removeEmitter(id, emitter);
|
||||||
|
try {
|
||||||
|
emitter.complete();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("关闭剪切板 SSE 连接失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeEmitter(String id, SseEmitter emitter) {
|
||||||
|
List<SseEmitter> emitterList = emitterMap.get(id);
|
||||||
|
if (emitterList == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emitterList.remove(emitter);
|
||||||
|
if (emitterList.isEmpty()) {
|
||||||
|
emitterMap.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.common.service.implement;
|
package com.imyeyu.api.modules.common.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.java.TimiJava;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import com.imyeyu.api.TimiServerAPI;
|
import com.imyeyu.api.TimiServerAPI;
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.blog.entity.Article;
|
|
||||||
import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
|
import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
|
||||||
import com.imyeyu.api.modules.blog.service.ArticleService;
|
|
||||||
import com.imyeyu.api.modules.blog.service.CommentRemindQueueService;
|
import com.imyeyu.api.modules.blog.service.CommentRemindQueueService;
|
||||||
import com.imyeyu.api.modules.common.bean.CommentSupport;
|
import com.imyeyu.api.modules.common.bean.CommentSupport;
|
||||||
import com.imyeyu.api.modules.common.entity.Comment;
|
import com.imyeyu.api.modules.common.entity.Comment;
|
||||||
@@ -21,23 +16,21 @@ import com.imyeyu.api.modules.common.service.CommentService;
|
|||||||
import com.imyeyu.api.modules.common.service.EmailQueueService;
|
import com.imyeyu.api.modules.common.service.EmailQueueService;
|
||||||
import com.imyeyu.api.modules.common.service.UserConfigService;
|
import com.imyeyu.api.modules.common.service.UserConfigService;
|
||||||
import com.imyeyu.api.modules.common.service.UserService;
|
import com.imyeyu.api.modules.common.service.UserService;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyPage;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.api.modules.git.service.RepositoryService;
|
|
||||||
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;
|
||||||
import com.imyeyu.spring.service.GettableService;
|
import com.imyeyu.spring.service.GettableService;
|
||||||
import com.imyeyu.utils.Time;
|
import com.imyeyu.utils.Time;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.beans.BeanUtils;
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,8 +45,6 @@ public class CommentReplyServiceImplement extends AbstractEntityService<CommentR
|
|||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final CommentService commentService;
|
private final CommentService commentService;
|
||||||
private final ArticleService articleService;
|
|
||||||
private final RepositoryService repositoryService;
|
|
||||||
private final UserConfigService userConfigService;
|
private final UserConfigService userConfigService;
|
||||||
private final EmailQueueService emailQueueService;
|
private final EmailQueueService emailQueueService;
|
||||||
private final CommentRemindQueueService commentRemindQueueService;
|
private final CommentRemindQueueService commentRemindQueueService;
|
||||||
@@ -65,11 +56,23 @@ public class CommentReplyServiceImplement extends AbstractEntityService<CommentR
|
|||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(Long crId) {
|
public PageResult<CommentReply> page(Page<CommentReply> page) {
|
||||||
super.delete(crId);
|
PageResult<CommentReply> result = super.page(page);
|
||||||
commentRemindQueueService.destroyByReplyId(crId);
|
for (CommentReply reply : result.getList()) {
|
||||||
|
Comment comment = commentService.get(reply.getCommentId());
|
||||||
|
if (TimiJava.isNotEmpty(comment.getUserId())) {
|
||||||
|
comment.setUser(userService.view(comment.getUserId()));
|
||||||
|
}
|
||||||
|
reply.setComment(comment);
|
||||||
|
if (TimiJava.isNotEmpty(reply.getSenderId())) {
|
||||||
|
reply.setSender(userService.view(reply.getSenderId()));
|
||||||
|
}
|
||||||
|
if (TimiJava.isNotEmpty(reply.getReceiverId())) {
|
||||||
|
reply.setReceiver(userService.view(reply.getReceiverId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@@ -160,37 +163,10 @@ public class CommentReplyServiceImplement extends AbstractEntityService<CommentR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public PageResult<CommentReplyView> pageByBizType(CommentReplyPage page) {
|
public void delete(Long crId) {
|
||||||
PageResult<CommentReplyView> result = new PageResult<>();
|
super.delete(crId);
|
||||||
List<CommentReplyView> list = mapper.listByBizType(page.getBizType(), page.getBizId(), page.getOffset(), page.getLimit());
|
commentRemindQueueService.destroyByReplyId(crId);
|
||||||
for (int i = 0; i < list.size(); i++) {
|
|
||||||
CommentReplyView reply = list.get(i);
|
|
||||||
CommentView comment = new CommentView();
|
|
||||||
BeanUtils.copyProperties(commentService.get(reply.getCommentId()), comment);
|
|
||||||
if (TimiJava.isNotEmpty(comment.getUserId())) {
|
|
||||||
comment.setUser(userService.view(comment.getUserId()));
|
|
||||||
}
|
|
||||||
switch (comment.getBizType()) {
|
|
||||||
case ARTICLE -> {
|
|
||||||
Article article = articleService.get(comment.getBizId());
|
|
||||||
article.setData(null);
|
|
||||||
article.setExtendData(null);
|
|
||||||
comment.setArticle(article);
|
|
||||||
}
|
|
||||||
// case GIT_ISSUE, GIT_MERGE -> comment.setRepository(repositoryService.get(comment.getBizId()));
|
|
||||||
}
|
|
||||||
reply.setComment(comment);
|
|
||||||
|
|
||||||
if (TimiJava.isNotEmpty(reply.getSenderId())) {
|
|
||||||
reply.setSender(userService.view(reply.getSenderId()));
|
|
||||||
}
|
|
||||||
if (TimiJava.isNotEmpty(reply.getReceiverId())) {
|
|
||||||
reply.setReceiver(userService.view(reply.getReceiverId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.setList(list);
|
|
||||||
result.setTotal(mapper.countByBizType(page.getBizType(), page.getBizId()));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.common.service.implement;
|
package com.imyeyu.api.modules.common.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.java.TimiJava;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import com.imyeyu.api.TimiServerAPI;
|
import com.imyeyu.api.TimiServerAPI;
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.blog.entity.Article;
|
import com.imyeyu.api.modules.blog.entity.CommentRemindQueue;
|
||||||
import com.imyeyu.api.modules.blog.service.ArticleService;
|
|
||||||
import com.imyeyu.api.modules.common.bean.CommentSupport;
|
import com.imyeyu.api.modules.common.bean.CommentSupport;
|
||||||
import com.imyeyu.api.modules.common.entity.Comment;
|
import com.imyeyu.api.modules.common.entity.Comment;
|
||||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||||
@@ -16,12 +12,11 @@ import com.imyeyu.api.modules.common.mapper.CommentRemindQueueMapper;
|
|||||||
import com.imyeyu.api.modules.common.mapper.CommentReplyMapper;
|
import com.imyeyu.api.modules.common.mapper.CommentReplyMapper;
|
||||||
import com.imyeyu.api.modules.common.service.CommentService;
|
import com.imyeyu.api.modules.common.service.CommentService;
|
||||||
import com.imyeyu.api.modules.common.service.UserService;
|
import com.imyeyu.api.modules.common.service.UserService;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentView;
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.UserCommentPage;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.api.modules.git.service.RepositoryService;
|
|
||||||
import com.imyeyu.api.modules.git.vo.issue.CommentPage;
|
|
||||||
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;
|
||||||
@@ -31,7 +26,6 @@ import org.springframework.context.annotation.Lazy;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,8 +39,6 @@ import java.util.List;
|
|||||||
public class CommentServiceImplement extends AbstractEntityService<Comment, Long> implements CommentService {
|
public class CommentServiceImplement extends AbstractEntityService<Comment, Long> implements CommentService {
|
||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final ArticleService articleService;
|
|
||||||
private final RepositoryService repositoryService;
|
|
||||||
|
|
||||||
private final CommentMapper mapper;
|
private final CommentMapper mapper;
|
||||||
private final CommentReplyMapper replyMapper;
|
private final CommentReplyMapper replyMapper;
|
||||||
@@ -57,6 +49,36 @@ public class CommentServiceImplement extends AbstractEntityService<Comment, Long
|
|||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<Comment> page(Page<Comment> page) {
|
||||||
|
PageResult<Comment> result = super.page(page);
|
||||||
|
for (Comment comment : result.getList()) {
|
||||||
|
if (TimiJava.isNotEmpty(comment.getUserId())) {
|
||||||
|
comment.setUser(userService.view(comment.getUserId()));
|
||||||
|
}
|
||||||
|
Page<CommentReply> replyPage = new Page<>();
|
||||||
|
replyPage.setIndex(0);
|
||||||
|
replyPage.setSize(6);
|
||||||
|
{
|
||||||
|
CommentReply exampleReply = new CommentReply();
|
||||||
|
exampleReply.setCommentId(comment.getId());
|
||||||
|
replyPage.setEqualsExample(exampleReply);
|
||||||
|
}
|
||||||
|
PageResult<CommentReply> replyResult = replyMapper.selectPageResult(replyPage);
|
||||||
|
comment.setReplies(replyResult.getList());
|
||||||
|
comment.setRepliesLength(replyResult.getTotal());
|
||||||
|
for (CommentReply reply : replyResult.getList()) {
|
||||||
|
if (TimiJava.isNotEmpty(reply.getSenderId())) {
|
||||||
|
reply.setSender(userService.view(reply.getSenderId()));
|
||||||
|
}
|
||||||
|
if (TimiJava.isNotEmpty(reply.getReceiverId())) {
|
||||||
|
reply.setReceiver(userService.view(reply.getReceiverId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(Comment comment) {
|
public void create(Comment comment) {
|
||||||
@@ -83,77 +105,31 @@ public class CommentServiceImplement extends AbstractEntityService<Comment, Long
|
|||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void delete(Long cId) {
|
public void delete(Long commentId) {
|
||||||
User user = userService.getLoginUser();
|
User user = userService.getLoginUser();
|
||||||
|
|
||||||
Comment comment = get(cId);
|
Comment comment = get(commentId);
|
||||||
if (!comment.getUserId().equals(user.getId())) {
|
if (!comment.getUserId().equals(user.getId())) {
|
||||||
throw new TimiException(TimiCode.PERMISSION_ERROR).msgKey("token.illegal");
|
throw new TimiException(TimiCode.PERMISSION_ERROR).msgKey("token.illegal");
|
||||||
}
|
}
|
||||||
List<CommentReply> replies = replyMapper.listAllBySenderId(user.getId());
|
{
|
||||||
for (int i = 0; i < replies.size(); i++) {
|
// 移除被回复者的回复提醒队列
|
||||||
// 移出被回复者的回复提醒队列
|
CommentReply example = new CommentReply();
|
||||||
remindQueueMapper.destroyByReplyId(replies.get(i).getId());
|
example.setSenderId(user.getId());
|
||||||
}
|
List<CommentReply> replies = replyMapper.selectAllByExample(example);
|
||||||
replyMapper.deleteByCommentId(cId);
|
for (CommentReply reply : replies) {
|
||||||
super.delete(cId);
|
CommentRemindQueue destroyRemindExample = new CommentRemindQueue();
|
||||||
}
|
destroyRemindExample.setReplyId(reply.getReplyId());
|
||||||
|
remindQueueMapper.destroyAllByExample(destroyRemindExample);
|
||||||
@Override
|
|
||||||
public PageResult<CommentView> pageByBizId(CommentPage page) {
|
|
||||||
if (page.getOrderMap() == null) {
|
|
||||||
page.setOrderMap(new LinkedHashMap<>());
|
|
||||||
}
|
|
||||||
if (page.getOrderMap().isEmpty()) {
|
|
||||||
page.getOrderMap().put("createdAt", BaseMapper.OrderType.DESC);
|
|
||||||
}
|
|
||||||
List<CommentView> list = mapper.list(page.getBizType(), page.getBizId(), page.getOffset(), page.getLimit(), page.getOrderMap());
|
|
||||||
for (int i = 0; i < list.size(); i++) {
|
|
||||||
CommentView comment = list.get(i);
|
|
||||||
if (TimiJava.isNotEmpty(comment.getUserId())) {
|
|
||||||
comment.setUser(userService.view(comment.getUserId()));
|
|
||||||
}
|
|
||||||
List<CommentReplyView> replies = comment.getReplies();
|
|
||||||
for (int j = 0; j < replies.size(); j++) {
|
|
||||||
CommentReplyView reply = replies.get(j);
|
|
||||||
if (TimiJava.isNotEmpty(reply.getSenderId())) {
|
|
||||||
reply.setSender(userService.view(reply.getSenderId()));
|
|
||||||
}
|
|
||||||
if (TimiJava.isNotEmpty(reply.getReceiverId())) {
|
|
||||||
reply.setReceiver(userService.view(reply.getReceiverId()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// 删除回复
|
||||||
|
CommentReply example = new CommentReply();
|
||||||
|
example.setCommentId(commentId);
|
||||||
|
replyMapper.deleteAllByExample(example);
|
||||||
}
|
}
|
||||||
PageResult<CommentView> result = new PageResult<>();
|
// 删除评论
|
||||||
result.setList(list);
|
super.delete(commentId);
|
||||||
result.setTotal(mapper.count(page.getBizType(), page.getBizId()));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PageResult<CommentView> pageByUserId(UserCommentPage page) {
|
|
||||||
if (page.getOrderMap() == null) {
|
|
||||||
page.setOrderMap(new LinkedHashMap<>());
|
|
||||||
}
|
|
||||||
if (page.getOrderMap().isEmpty()) {
|
|
||||||
page.getOrderMap().put("createdAt", BaseMapper.OrderType.DESC);
|
|
||||||
}
|
|
||||||
PageResult<CommentView> result = new PageResult<>();
|
|
||||||
result.setList(mapper.listByUserId(page.getUserId(), page.getOffset(), page.getLimit(), page.getOrderMap()));
|
|
||||||
result.setTotal(mapper.countByUserId(page.getUserId()));
|
|
||||||
|
|
||||||
for (int i = 0; i < result.getList().size(); i++) {
|
|
||||||
CommentView view = result.getList().get(i);
|
|
||||||
switch (view.getBizType()) {
|
|
||||||
case ARTICLE -> {
|
|
||||||
Article article = articleService.get(view.getBizId());
|
|
||||||
article.setData(null);
|
|
||||||
article.setExtendData(null);
|
|
||||||
view.setArticle(article);
|
|
||||||
}
|
|
||||||
// case GIT_ISSUE, GIT_MERGE -> view.setRepository(repositoryService.get(view.getBizId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class MultilingualServiceImplement extends AbstractEntityService<Multilin
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String get(Language language, Long id) {
|
public String get(Language.Enum language, Long id) {
|
||||||
Multilingual result = redisLanguage.get(id);
|
Multilingual result = redisLanguage.get(id);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = mapper.select(id);
|
result = mapper.select(id);
|
||||||
@@ -72,7 +72,7 @@ public class MultilingualServiceImplement extends AbstractEntityService<Multilin
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getByKey(Language language, String key) {
|
public String getByKey(Language.Enum language, String key) {
|
||||||
Long languageId = redisLanguageMap.get(key);
|
Long languageId = redisLanguageMap.get(key);
|
||||||
if (languageId == null) {
|
if (languageId == null) {
|
||||||
Multilingual result = mapper.selectByKey(key);
|
Multilingual result = mapper.selectByKey(key);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.imyeyu.api.modules.common.service.implement;
|
package com.imyeyu.api.modules.common.service.implement;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.google.gson.JsonArray;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.google.gson.JsonElement;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.google.gson.JsonObject;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.gson.JsonParser;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
import com.imyeyu.api.modules.common.mapper.SettingMapper;
|
import com.imyeyu.api.modules.common.mapper.SettingMapper;
|
||||||
@@ -39,13 +39,19 @@ public class SettingServiceImplement extends AbstractEntityService<Setting, Stri
|
|||||||
private final SettingMapper mapper;
|
private final SettingMapper mapper;
|
||||||
private final Redis<String, String> redisSetting;
|
private final Redis<String, String> redisSetting;
|
||||||
|
|
||||||
private final Gson gson;
|
private final ObjectMapper jackson;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected BaseMapper<Setting, String> mapper() {
|
protected BaseMapper<Setting, String> mapper() {
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Setting setting) {
|
||||||
|
super.update(setting);
|
||||||
|
clearCache(setting.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
public Setting getByKey(SettingKey key) {
|
public Setting getByKey(SettingKey key) {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new TimiException(TimiCode.ARG_MISS).msgKey("key can not be null");
|
throw new TimiException(TimiCode.ARG_MISS).msgKey("key can not be null");
|
||||||
@@ -53,7 +59,11 @@ public class SettingServiceImplement extends AbstractEntityService<Setting, Stri
|
|||||||
|
|
||||||
String cacheValue = redisSetting.get(key.toString());
|
String cacheValue = redisSetting.get(key.toString());
|
||||||
if (TimiJava.isNotEmpty(cacheValue)) {
|
if (TimiJava.isNotEmpty(cacheValue)) {
|
||||||
return gson.fromJson(cacheValue, Setting.class);
|
try {
|
||||||
|
return jackson.readValue(cacheValue, Setting.class);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new TimiException(TimiCode.ERROR, "read setting cache error", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Setting setting = mapper.selectByKey(key);
|
Setting setting = mapper.selectByKey(key);
|
||||||
if (TimiJava.isEmpty(setting.getValue())) {
|
if (TimiJava.isEmpty(setting.getValue())) {
|
||||||
@@ -66,7 +76,11 @@ public class SettingServiceImplement extends AbstractEntityService<Setting, Stri
|
|||||||
settingTTL = Integer.parseInt(getByKey(SettingKey.TTL_SETTING).getValue());
|
settingTTL = Integer.parseInt(getByKey(SettingKey.TTL_SETTING).getValue());
|
||||||
}
|
}
|
||||||
if (0 < settingTTL) {
|
if (0 < settingTTL) {
|
||||||
redisSetting.set(key.toString(), gson.toJson(setting), Time.M * settingTTL);
|
try {
|
||||||
|
redisSetting.set(key.toString(), jackson.writeValueAsString(setting), Time.M * settingTTL);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new TimiException(TimiCode.ERROR, "write setting cache error", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return setting;
|
return setting;
|
||||||
}
|
}
|
||||||
@@ -111,28 +125,36 @@ public class SettingServiceImplement extends AbstractEntityService<Setting, Stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonElement getAsJsonElement(SettingKey key) {
|
public JsonNode getAsJsonNode(SettingKey key) {
|
||||||
return JsonParser.parseString(getAsString(key));
|
try {
|
||||||
|
return jackson.readTree(getAsString(key));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new TimiException(TimiCode.ERROR, "read setting json error", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonObject getAsJsonObject(SettingKey key) {
|
public ObjectNode getAsJsonObject(SettingKey key) {
|
||||||
return getAsJsonElement(key).getAsJsonObject();
|
return (ObjectNode) getAsJsonNode(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonArray getAsJsonArray(SettingKey key) {
|
public ArrayNode getAsArrayNode(SettingKey key) {
|
||||||
return getAsJsonElement(key).getAsJsonArray();
|
return (ArrayNode) getAsJsonNode(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T fromJson(SettingKey key, Class<T> clazz) {
|
public <T> T fromJson(SettingKey key, Class<T> clazz) {
|
||||||
return gson.fromJson(getAsJsonElement(key), clazz);
|
try {
|
||||||
|
return jackson.treeToValue(getAsJsonNode(key), clazz);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new TimiException(TimiCode.ERROR, "read setting json error", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T fromJson(SettingKey key, TypeToken<T> typeToken) {
|
public <T> T fromJson(SettingKey key, TypeReference<T> typeReference) {
|
||||||
return gson.fromJson(getAsJsonElement(key), typeToken);
|
return jackson.convertValue(getAsJsonNode(key), typeReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T fromYaml(SettingKey key, Class<T> clazz) {
|
public <T> T fromYaml(SettingKey key, Class<T> clazz) {
|
||||||
|
|||||||
@@ -1,36 +1,24 @@
|
|||||||
package com.imyeyu.api.modules.common.service.implement;
|
package com.imyeyu.api.modules.common.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||||
import com.imyeyu.api.modules.common.bean.TempFileMetaData;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
import com.imyeyu.api.modules.common.service.TempFileService;
|
import com.imyeyu.api.modules.common.service.TempFileService;
|
||||||
import com.imyeyu.api.modules.common.vo.TempFileResponse;
|
import com.imyeyu.api.modules.common.vo.TempFileResponse;
|
||||||
import com.imyeyu.io.IO;
|
|
||||||
import com.imyeyu.io.IOSize;
|
import com.imyeyu.io.IOSize;
|
||||||
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.network.Network;
|
|
||||||
import com.imyeyu.spring.TimiSpring;
|
import com.imyeyu.spring.TimiSpring;
|
||||||
|
import com.imyeyu.spring.bean.Page;
|
||||||
import com.imyeyu.utils.Time;
|
import com.imyeyu.utils.Time;
|
||||||
import jakarta.annotation.PostConstruct;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
@@ -41,56 +29,56 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class TempFileServiceImplement implements TempFileService {
|
public class TempFileServiceImplement implements TempFileService {
|
||||||
|
|
||||||
private static final Long TTL = Time.H * 6;
|
|
||||||
private static final Long LIMIT = IOSize.GB * 10;
|
|
||||||
|
|
||||||
private final SettingService settingService;
|
private final SettingService settingService;
|
||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
|
|
||||||
private Path storagePath;
|
public List<TempFileResponse> store(List<MultipartFile> files, Long ttl) throws TimiException {
|
||||||
|
String ttlMinStr = settingService.getAsString(SettingKey.TEMP_FILE_TTL_MIN);
|
||||||
|
String ttlMaxStr = settingService.getAsString(SettingKey.TEMP_FILE_TTL_MAX);
|
||||||
|
String limitStr = settingService.getAsString(SettingKey.TEMP_FILE_LIMIT);
|
||||||
|
|
||||||
private final Map<String, TempFileMetaData> metadataMap = new ConcurrentHashMap<>();
|
long minTTL = Time.parseToMS(ttlMinStr);
|
||||||
private final Map<String, Long> usageMap = new ConcurrentHashMap<>();
|
long maxTTL = Time.parseToMS(ttlMaxStr);
|
||||||
|
long defaultTTL = Time.parseToMS(settingService.getAsString(SettingKey.TEMP_FILE_TTL_DEFAULT));
|
||||||
|
long limit = IOSize.parse(limitStr);
|
||||||
|
long residueTime = Time.parseToMS(settingService.getAsString(SettingKey.TEMP_FILE_RESIDUE_TIME));
|
||||||
|
|
||||||
@PostConstruct
|
ttl = TimiJava.defaultIfNull(ttl, defaultTTL);
|
||||||
public void init() throws IOException {
|
TimiException.requiredTrue(minTTL < ttl && ttl <= maxTTL, String.format("ttl must be between %s and %s", ttlMinStr, ttlMaxStr));
|
||||||
storagePath = Paths.get(settingService.getAsString(SettingKey.TEMP_FILE_PATH));
|
|
||||||
IO.destroy(storagePath.toFile());
|
|
||||||
Files.createDirectories(storagePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<TempFileResponse> store(List<MultipartFile> files) throws TimiException {
|
|
||||||
long newFileSize = files.stream().mapToLong(MultipartFile::getSize).sum();
|
long newFileSize = files.stream().mapToLong(MultipartFile::getSize).sum();
|
||||||
long currentUsage = usageMap.getOrDefault(TimiSpring.getRequestIP(), 0L);
|
{
|
||||||
TimiException.requiredTrue(currentUsage + newFileSize < LIMIT, "out of storage limit(10 GB)");
|
// 缓存池大小限制
|
||||||
|
Page<Attachment> page = new Page<>();
|
||||||
|
page.setIndex(0);
|
||||||
|
page.setSize(Long.MAX_VALUE);
|
||||||
|
{
|
||||||
|
Attachment example = new Attachment();
|
||||||
|
example.setUploaderIp(TimiSpring.getRequestIP());
|
||||||
|
page.setEqualsExample(example);
|
||||||
|
}
|
||||||
|
long currentUsage = attachmentService.page(page).getList().stream().mapToLong(Attachment::getSize).sum();
|
||||||
|
TimiException.requiredTrue(currentUsage + newFileSize < limit, "out of storage limit(%s)".formatted(limitStr));
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
List<TempFileResponse> result = new ArrayList<>();
|
List<TempFileResponse> result = new ArrayList<>();
|
||||||
|
|
||||||
|
long deletedAt = Time.now() + ttl;
|
||||||
|
long destroyAt = deletedAt + residueTime;
|
||||||
for (int i = 0; i < files.size(); i++) {
|
for (int i = 0; i < files.size(); i++) {
|
||||||
MultipartFile file = files.get(i);
|
MultipartFile file = files.get(i);
|
||||||
|
|
||||||
String fileId = UUID.randomUUID().toString();
|
Attachment attach = new Attachment();
|
||||||
String fileName = fileId;
|
attach.setBizType(Attachment.BizType.TEMP_FILE);
|
||||||
if (TimiJava.isNotEmpty(file.getOriginalFilename())) {
|
attach.setName(file.getOriginalFilename());
|
||||||
fileName = fileId + "." + Network.uriFileExtension(file.getOriginalFilename());
|
attach.setDeletedAt(deletedAt);
|
||||||
}
|
attach.setDestroyAt(destroyAt);
|
||||||
Path filePath = storagePath.resolve(fileName);
|
attach.setInputStream(file.getInputStream());
|
||||||
Files.copy(file.getInputStream(), filePath);
|
attachmentService.create(attach);
|
||||||
|
|
||||||
// 创建元数据
|
|
||||||
TempFileMetaData metadata = new TempFileMetaData();
|
|
||||||
metadata.setId(fileId);
|
|
||||||
metadata.setPath(filePath);
|
|
||||||
metadata.setName(fileName);
|
|
||||||
metadata.setOriginalName(file.getOriginalFilename());
|
|
||||||
metadata.setLastAccessAt(Time.now());
|
|
||||||
metadataMap.put(metadata.getId(), metadata);
|
|
||||||
|
|
||||||
TempFileResponse resp = new TempFileResponse();
|
TempFileResponse resp = new TempFileResponse();
|
||||||
resp.setId(metadata.getId());
|
resp.setId(attach.getMongoId());
|
||||||
resp.setExpireAt(metadata.getLastAccessAt() + TTL);
|
resp.setExpireAt(deletedAt);
|
||||||
|
|
||||||
usageMap.put(TimiSpring.getRequestIP(), currentUsage + newFileSize);
|
|
||||||
result.add(resp);
|
result.add(resp);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -99,34 +87,4 @@ public class TempFileServiceImplement implements TempFileService {
|
|||||||
throw new TimiException(TimiCode.ERROR, "store temp file error", e);
|
throw new TimiException(TimiCode.ERROR, "store temp file error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public File get(String id) throws TimiException {
|
|
||||||
TempFileMetaData metaData = metadataMap.get(id);
|
|
||||||
TimiException.required(metaData, "not found temp file");
|
|
||||||
metaData.setLastAccessAt(Time.now());
|
|
||||||
return metaData.getPath().toFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TempFileMetaData metadata(String id) throws TimiException {
|
|
||||||
return metadataMap.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Scheduled(fixedRate = 3600000)
|
|
||||||
public void cleanup() {
|
|
||||||
List<String> expiredIds = metadataMap.values()
|
|
||||||
.stream()
|
|
||||||
.filter(metadata -> metadata.getLastAccessAt() + TTL < Time.now())
|
|
||||||
.map(TempFileMetaData::getId)
|
|
||||||
.toList();
|
|
||||||
for (int i = 0; i < expiredIds.size(); i++) {
|
|
||||||
TempFileMetaData removed = metadataMap.remove(expiredIds.get(i));
|
|
||||||
if (TimiJava.isNotEmpty(removed)) {
|
|
||||||
File file = removed.getPath().toFile();
|
|
||||||
IO.destroy(file);
|
|
||||||
usageMap.computeIfPresent(TimiSpring.getRequestIP(), (ip, usage) -> usage - file.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package com.imyeyu.api.modules.common.service.implement;
|
package com.imyeyu.api.modules.common.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.io.IOSize;
|
|
||||||
import com.imyeyu.java.TimiJava;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.entity.User;
|
import com.imyeyu.api.modules.common.entity.User;
|
||||||
@@ -12,8 +8,11 @@ import com.imyeyu.api.modules.common.mapper.UserProfileMapper;
|
|||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.common.service.UserProfileService;
|
import com.imyeyu.api.modules.common.service.UserProfileService;
|
||||||
import com.imyeyu.api.modules.common.service.UserService;
|
import com.imyeyu.api.modules.common.service.UserService;
|
||||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
|
|
||||||
import com.imyeyu.api.modules.common.vo.user.UserRequest;
|
import com.imyeyu.api.modules.common.vo.user.UserRequest;
|
||||||
|
import com.imyeyu.io.IOSize;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
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;
|
||||||
@@ -73,7 +72,7 @@ public class UserProfileServiceImplement extends AbstractEntityService<UserProfi
|
|||||||
if (IOSize.MB < bytes.length) {
|
if (IOSize.MB < bytes.length) {
|
||||||
throw new TimiException(TimiCode.ARG_BAD).msgKey("限制上传文件大小 1 MB");
|
throw new TimiException(TimiCode.ARG_BAD).msgKey("限制上传文件大小 1 MB");
|
||||||
}
|
}
|
||||||
AttachmentRequest wrapperAttach = new AttachmentRequest();
|
Attachment wrapperAttach = new Attachment();
|
||||||
wrapperAttach.setName(request.getId() + ".png");
|
wrapperAttach.setName(request.getId() + ".png");
|
||||||
wrapperAttach.setBizType(Attachment.BizType.USER);
|
wrapperAttach.setBizType(Attachment.BizType.USER);
|
||||||
wrapperAttach.setBizId(request.getId());
|
wrapperAttach.setBizId(request.getId());
|
||||||
@@ -88,7 +87,7 @@ public class UserProfileServiceImplement extends AbstractEntityService<UserProfi
|
|||||||
attachmentService.delete(dbAvatar.getId());
|
attachmentService.delete(dbAvatar.getId());
|
||||||
}
|
}
|
||||||
MultipartFile avatar = request.getProfile().getAvatar();
|
MultipartFile avatar = request.getProfile().getAvatar();
|
||||||
AttachmentRequest avatarAttach = new AttachmentRequest();
|
Attachment avatarAttach = new Attachment();
|
||||||
avatarAttach.setName(request.getId() + ".png");
|
avatarAttach.setName(request.getId() + ".png");
|
||||||
avatarAttach.setBizType(Attachment.BizType.USER);
|
avatarAttach.setBizType(Attachment.BizType.USER);
|
||||||
avatarAttach.setBizId(request.getId());
|
avatarAttach.setBizId(request.getId());
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package com.imyeyu.api.modules.common.service.implement;
|
package com.imyeyu.api.modules.common.service.implement;
|
||||||
|
|
||||||
import com.imyeyu.java.TimiJava;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiCode;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.blog.util.UserToken;
|
import com.imyeyu.api.modules.blog.util.UserToken;
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
|
import com.imyeyu.api.modules.common.entity.CommentReply;
|
||||||
import com.imyeyu.api.modules.common.entity.EmailQueue;
|
import com.imyeyu.api.modules.common.entity.EmailQueue;
|
||||||
import com.imyeyu.api.modules.common.entity.User;
|
import com.imyeyu.api.modules.common.entity.User;
|
||||||
import com.imyeyu.api.modules.common.entity.UserConfig;
|
import com.imyeyu.api.modules.common.entity.UserConfig;
|
||||||
@@ -29,7 +27,11 @@ import com.imyeyu.api.modules.common.vo.user.UserView;
|
|||||||
import com.imyeyu.api.modules.git.entity.Developer;
|
import com.imyeyu.api.modules.git.entity.Developer;
|
||||||
import com.imyeyu.api.modules.git.service.DeveloperService;
|
import com.imyeyu.api.modules.git.service.DeveloperService;
|
||||||
import com.imyeyu.api.modules.minecraft.service.PlayerService;
|
import com.imyeyu.api.modules.minecraft.service.PlayerService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiCode;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.spring.TimiSpring;
|
import com.imyeyu.spring.TimiSpring;
|
||||||
|
import com.imyeyu.spring.bean.Logic;
|
||||||
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 com.imyeyu.spring.util.Redis;
|
import com.imyeyu.spring.util.Redis;
|
||||||
@@ -360,8 +362,13 @@ public class UserServiceImplement extends AbstractEntityService<User, Long> impl
|
|||||||
}
|
}
|
||||||
// 删除评论
|
// 删除评论
|
||||||
commentMapper.deleteByUserId(user.getId());
|
commentMapper.deleteByUserId(user.getId());
|
||||||
|
{
|
||||||
// 删除回复
|
// 删除回复
|
||||||
commentReplyMapper.deleteByUserId(user.getId());
|
CommentReply example = new CommentReply();
|
||||||
|
example.setSenderId(user.getId());
|
||||||
|
example.setReceiverId(user.getId());
|
||||||
|
commentReplyMapper.deleteAllByExample(example, Logic.OR);
|
||||||
|
}
|
||||||
// 删除账号
|
// 删除账号
|
||||||
delete(user.getId());
|
delete(user.getId());
|
||||||
// 清除登录会话
|
// 清除登录会话
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.imyeyu.api.modules.common.task;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件清除定时任务。每天凌晨 1 点执行,清除已过期但未销毁的附件
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-01-05 12:37
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Configuration
|
||||||
|
@EnableScheduling
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AttachmentClearTask {
|
||||||
|
|
||||||
|
private final AttachmentService attachmentService;
|
||||||
|
|
||||||
|
@Scheduled(cron = "0 0 1 * * ?")
|
||||||
|
public void run() {
|
||||||
|
log.info("start clear expired attachments");
|
||||||
|
try {
|
||||||
|
List<Attachment> needDestroyList = attachmentService.listNeedDestroy();
|
||||||
|
if (needDestroyList.isEmpty()) {
|
||||||
|
log.info("nothing attachment need clear");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Attachment attach : needDestroyList) {
|
||||||
|
try {
|
||||||
|
attachmentService.destroy(attach.getId());
|
||||||
|
log.info("clear attachment success: id[{}]", attach.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("clear attachment error: id[{}]", attach.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("end clear expired attachments");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("attachment clear task error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,6 @@ import com.imyeyu.api.modules.common.entity.User;
|
|||||||
import com.imyeyu.api.modules.common.service.CommentReplyService;
|
import com.imyeyu.api.modules.common.service.CommentReplyService;
|
||||||
import com.imyeyu.api.modules.common.service.EmailQueueService;
|
import com.imyeyu.api.modules.common.service.EmailQueueService;
|
||||||
import com.imyeyu.api.modules.common.service.UserService;
|
import com.imyeyu.api.modules.common.service.UserService;
|
||||||
import com.imyeyu.api.modules.common.vo.comment.CommentReplyView;
|
|
||||||
import com.imyeyu.spring.util.Redis;
|
import com.imyeyu.spring.util.Redis;
|
||||||
import com.imyeyu.utils.Text;
|
import com.imyeyu.utils.Text;
|
||||||
import com.imyeyu.utils.Time;
|
import com.imyeyu.utils.Time;
|
||||||
@@ -175,9 +174,7 @@ public class EmailTask implements TimiJava {
|
|||||||
for (CommentRemindQueue remind : reminds) {
|
for (CommentRemindQueue remind : reminds) {
|
||||||
// 回复
|
// 回复
|
||||||
CommentReply reply = commentReplyService.get(remind.getReplyId());
|
CommentReply reply = commentReplyService.get(remind.getReplyId());
|
||||||
CommentReplyView replyView = new CommentReplyView();
|
remind.setReply(reply);
|
||||||
BeanUtils.copyProperties(reply, replyView);
|
|
||||||
remind.setReply(replyView);
|
|
||||||
if (TimiJava.isNotEmpty(remind.getReply().getSenderId())) {
|
if (TimiJava.isNotEmpty(remind.getReply().getSenderId())) {
|
||||||
// 发送者
|
// 发送者
|
||||||
remind.getReply().setSender(userService.view(remind.getReply().getSenderId()));
|
remind.getReply().setSender(userService.view(remind.getReply().getSenderId()));
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.common.task;
|
package com.imyeyu.api.modules.common.task;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.google.gson.JsonObject;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||||
import com.imyeyu.api.modules.common.entity.Multilingual;
|
import com.imyeyu.api.modules.common.entity.Multilingual;
|
||||||
@@ -50,16 +49,16 @@ public class MultilingualTranslateTask {
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public enum BaiduLanguage {
|
public enum BaiduLanguage {
|
||||||
|
|
||||||
ZH(Language.zh_CN),
|
ZH(Language.Enum.zh_CN),
|
||||||
EN(Language.en_US),
|
EN(Language.Enum.en_US),
|
||||||
JP(Language.ja_JP),
|
JP(Language.Enum.ja_JP),
|
||||||
KOR(Language.ko_KR),
|
KOR(Language.Enum.ko_KR),
|
||||||
RU(Language.ru_RU),
|
RU(Language.Enum.ru_RU),
|
||||||
DE(Language.de_DE),
|
DE(Language.Enum.de_DE),
|
||||||
CHT(Language.zh_TW);
|
CHT(Language.Enum.zh_TW);
|
||||||
|
|
||||||
/** 标准映射 */
|
/** 标准映射 */
|
||||||
final Language language;
|
final Language.Enum language;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取排除语言列表
|
* 获取排除语言列表
|
||||||
@@ -81,6 +80,7 @@ public class MultilingualTranslateTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final ObjectMapper jackson;
|
||||||
private final SettingService settingService;
|
private final SettingService settingService;
|
||||||
private final MultilingualService service;
|
private final MultilingualService service;
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ public class MultilingualTranslateTask {
|
|||||||
Map<String, String> result = doTranslate(sb.toString(), languageList.get(j));
|
Map<String, String> result = doTranslate(sb.toString(), languageList.get(j));
|
||||||
for (Map.Entry<String, String> item : result.entrySet()) {
|
for (Map.Entry<String, String> item : result.entrySet()) {
|
||||||
Multilingual multilingual = cnMap.get(item.getKey());
|
Multilingual multilingual = cnMap.get(item.getKey());
|
||||||
Language lang = languageList.get(j).language;
|
Language.Enum lang = languageList.get(j).language;
|
||||||
String value = multilingual.getValue(lang);
|
String value = multilingual.getValue(lang);
|
||||||
if (TimiJava.isEmpty(value)) {
|
if (TimiJava.isEmpty(value)) {
|
||||||
Ref.setFieldValue(multilingual, lang.toString().replace("_", ""), item.getValue());
|
Ref.setFieldValue(multilingual, lang.toString().replace("_", ""), item.getValue());
|
||||||
@@ -147,18 +147,17 @@ public class MultilingualTranslateTask {
|
|||||||
.execute()
|
.execute()
|
||||||
.returnContent()
|
.returnContent()
|
||||||
.asString();
|
.asString();
|
||||||
JsonObject jo = JsonParser.parseString(response).getAsJsonObject();
|
JsonNode jo = jackson.readTree(response);
|
||||||
if (jo.has("error_code")) {
|
if (jo.has("error_code")) {
|
||||||
System.err.println(jo);
|
System.err.println(jo);
|
||||||
throw new TimiException(TimiCode.ERROR, jo.get("error_msg").getAsString());
|
throw new TimiException(TimiCode.ERROR, jo.get("error_msg").asText());
|
||||||
}
|
}
|
||||||
JsonArray ja = jo.get("trans_result").getAsJsonArray();
|
JsonNode ja = jo.get("trans_result");
|
||||||
|
|
||||||
JsonObject resultJO;
|
|
||||||
Map<String, String> result = new HashMap<>();
|
Map<String, String> result = new HashMap<>();
|
||||||
for (int i = 0; i < ja.size(); i++) {
|
for (int i = 0; i < ja.size(); i++) {
|
||||||
resultJO = ja.get(i).getAsJsonObject();
|
JsonNode resultJO = ja.get(i);
|
||||||
result.put(resultJO.get("src").getAsString(), resultJO.get("dst").getAsString());
|
result.put(resultJO.get("src").asText(), resultJO.get("dst").asText());
|
||||||
}
|
}
|
||||||
wait(200);
|
wait(200);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.imyeyu.api.modules.common.vo;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 共享剪切板请求
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-01-15 16:40
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ClipboardRequest {
|
||||||
|
|
||||||
|
/** 剪切板内容 */
|
||||||
|
@NotNull
|
||||||
|
private String content;
|
||||||
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.common.vo.attachment;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
|
||||||
import com.imyeyu.spring.annotation.table.Transient;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2024-02-21 10:58
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@Transient
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class AttachmentRequest extends Attachment {
|
|
||||||
|
|
||||||
private InputStream inputStream;
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.common.vo.attachment;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import com.imyeyu.api.bean.MultilingualHandler;
|
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2024-02-21 10:55
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class AttachmentView extends Attachment implements MultilingualHandler {
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.common.vo.comment;
|
|
||||||
|
|
||||||
import com.imyeyu.spring.bean.Page;
|
|
||||||
import jakarta.validation.constraints.Min;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2023-07-15 09:03
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class CommentReplyPage extends Page {
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2025-04-17 23:29
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
|
||||||
public enum BizType {
|
|
||||||
|
|
||||||
COMMENT("comment_id"),
|
|
||||||
|
|
||||||
SENDER("sender_id"),
|
|
||||||
|
|
||||||
RECEIVER("receiver_id");
|
|
||||||
|
|
||||||
final String column;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BizType bizType;
|
|
||||||
|
|
||||||
@Min(1)
|
|
||||||
private Long bizId;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.common.vo.comment;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.CommentReply;
|
|
||||||
import com.imyeyu.api.modules.common.vo.user.UserView;
|
|
||||||
import com.imyeyu.spring.annotation.table.Transient;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2024-03-05 17:48
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class CommentReplyView extends CommentReply {
|
|
||||||
|
|
||||||
/** 所属评论 */
|
|
||||||
@Transient
|
|
||||||
private CommentView comment;
|
|
||||||
|
|
||||||
/** 发送用户 */
|
|
||||||
@Transient
|
|
||||||
private UserView sender;
|
|
||||||
|
|
||||||
/** 回复用户 */
|
|
||||||
@Transient
|
|
||||||
private UserView receiver;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.common.vo.comment;
|
|
||||||
|
|
||||||
import com.imyeyu.api.modules.blog.entity.Article;
|
|
||||||
import com.imyeyu.api.modules.common.entity.Comment;
|
|
||||||
import com.imyeyu.api.modules.common.vo.user.UserView;
|
|
||||||
import com.imyeyu.api.modules.git.bean.gitea.Repository;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2024-02-29 16:37
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class CommentView extends Comment {
|
|
||||||
|
|
||||||
/** 回复数量 */
|
|
||||||
private int repliesLength;
|
|
||||||
|
|
||||||
/** 发送用户 */
|
|
||||||
private UserView user;
|
|
||||||
|
|
||||||
/** 关联文章 */
|
|
||||||
private Article article;
|
|
||||||
|
|
||||||
/** 关联仓库 */
|
|
||||||
private Repository repository;
|
|
||||||
|
|
||||||
/** 回复列表 */
|
|
||||||
private List<CommentReplyView> replies;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.common.vo.comment;
|
|
||||||
|
|
||||||
import jakarta.validation.constraints.Min;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import com.imyeyu.spring.bean.Page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2023-07-15 14:13
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class UserCommentPage extends Page {
|
|
||||||
|
|
||||||
@Min(1)
|
|
||||||
private Long userId;
|
|
||||||
}
|
|
||||||
@@ -56,7 +56,7 @@ public class ServerServiceImplement implements ServerService {
|
|||||||
public void report(ReportRequest request) {
|
public void report(ReportRequest request) {
|
||||||
ServerStatus status = serverMap.get(request.getId());
|
ServerStatus status = serverMap.get(request.getId());
|
||||||
if (status != null) {
|
if (status != null) {
|
||||||
request.setBaseInfo(TimiJava.firstNotNull(request.getBaseInfo(), status.getBaseInfo()));
|
request.setBaseInfo(TimiJava.defaultIfNull(request.getBaseInfo(), status.getBaseInfo()));
|
||||||
if (request.getBaseInfo() == null || TimiJava.isEmpty(request.getBaseInfo().getCore())) {
|
if (request.getBaseInfo() == null || TimiJava.isEmpty(request.getBaseInfo().getCore())) {
|
||||||
// 需要报告基本信息,与调用方约定返回代码为 IGNORE
|
// 需要报告基本信息,与调用方约定返回代码为 IGNORE
|
||||||
throw new TimiException(TimiCode.IGNORE);
|
throw new TimiException(TimiCode.IGNORE);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.imyeyu.api.modules.git.bean.gitea;
|
package com.imyeyu.api.modules.git.bean.gitea;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,6 +12,6 @@ public class Branch {
|
|||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@SerializedName("protected")
|
@JsonProperty("protected")
|
||||||
private boolean isProtected;
|
private boolean isProtected;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.git.bean.gitea;
|
package com.imyeyu.api.modules.git.bean.gitea;
|
||||||
|
|
||||||
import com.google.gson.annotations.JsonAdapter;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.imyeyu.api.modules.git.util.GiteaTimestampAdapter;
|
import com.imyeyu.api.modules.git.util.GiteaTimestampAdapter;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ public class File {
|
|||||||
|
|
||||||
private String lastCommitSha;
|
private String lastCommitSha;
|
||||||
|
|
||||||
@JsonAdapter(GiteaTimestampAdapter.class)
|
@JsonSerialize(using = GiteaTimestampAdapter.Serializer.class)
|
||||||
|
@JsonDeserialize(using = GiteaTimestampAdapter.Deserializer.class)
|
||||||
private Long lastCommitterDate;
|
private Long lastCommitterDate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.imyeyu.api.modules.git.bean.gitea;
|
package com.imyeyu.api.modules.git.bean.gitea;
|
||||||
|
|
||||||
import com.google.gson.annotations.JsonAdapter;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.imyeyu.api.modules.git.util.GiteaTimestampAdapter;
|
import com.imyeyu.api.modules.git.util.GiteaTimestampAdapter;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@@ -35,13 +36,16 @@ public class Repository {
|
|||||||
|
|
||||||
private Boolean archived;
|
private Boolean archived;
|
||||||
|
|
||||||
@JsonAdapter(GiteaTimestampAdapter.class)
|
@JsonSerialize(using = GiteaTimestampAdapter.Serializer.class)
|
||||||
|
@JsonDeserialize(using = GiteaTimestampAdapter.Deserializer.class)
|
||||||
private Long createdAt;
|
private Long createdAt;
|
||||||
|
|
||||||
@JsonAdapter(GiteaTimestampAdapter.class)
|
@JsonSerialize(using = GiteaTimestampAdapter.Serializer.class)
|
||||||
|
@JsonDeserialize(using = GiteaTimestampAdapter.Deserializer.class)
|
||||||
private Long updatedAt;
|
private Long updatedAt;
|
||||||
|
|
||||||
@JsonAdapter(GiteaTimestampAdapter.class)
|
@JsonSerialize(using = GiteaTimestampAdapter.Serializer.class)
|
||||||
|
@JsonDeserialize(using = GiteaTimestampAdapter.Deserializer.class)
|
||||||
private Long archivedAt;
|
private Long archivedAt;
|
||||||
|
|
||||||
private List<String> licenses;
|
private List<String> licenses;
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ public interface IssueMapper extends BaseMapper<Issue, Long> {
|
|||||||
* @param limit 数据量
|
* @param limit 数据量
|
||||||
* @return 反馈列表
|
* @return 反馈列表
|
||||||
*/
|
*/
|
||||||
List<Issue> listByIssuePage(@Param("issuePage") IssuePage issuePage, long offset, int limit);
|
List<Issue> listByIssuePage(@Param("issuePage") IssuePage issuePage, long offset, long limit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,5 +19,5 @@ public interface MergeMapper extends BaseMapper<Merge, Long> {
|
|||||||
|
|
||||||
long countByMergePage(@Param("mergePage") MergePage mergePage);
|
long countByMergePage(@Param("mergePage") MergePage mergePage);
|
||||||
|
|
||||||
List<Merge> listByMergePage(@Param("mergePage") MergePage mergePage, long offset, int limit);
|
List<Merge> listByMergePage(@Param("mergePage") MergePage mergePage, long offset, long limit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public interface ReleaseMapper extends BaseMapper<Release, Long> {
|
|||||||
@Select("SELECT COUNT(1) FROM git_release WHERE repository_id = #{repositoryId}" + NOT_DELETE)
|
@Select("SELECT COUNT(1) FROM git_release WHERE repository_id = #{repositoryId}" + NOT_DELETE)
|
||||||
long countByRepositoryId(long repositoryId);
|
long countByRepositoryId(long repositoryId);
|
||||||
|
|
||||||
List<ReleaseView> listByRepositoryId(long repositoryId, long offset, int limit);
|
List<ReleaseView> listByRepositoryId(long repositoryId, long offset, long limit);
|
||||||
|
|
||||||
@Select("SELECT * FROM git_release WHERE repository_id = #{repositoryId}" + NOT_DELETE + "ORDER BY created_at DESC" + LIMIT_1)
|
@Select("SELECT * FROM git_release WHERE repository_id = #{repositoryId}" + NOT_DELETE + "ORDER BY created_at DESC" + LIMIT_1)
|
||||||
Release queryLatestByRepositoryId(long repositoryId);
|
Release queryLatestByRepositoryId(long repositoryId);
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.git.service.implement;
|
package com.imyeyu.api.modules.git.service.implement;
|
||||||
|
|
||||||
import com.google.gson.FieldNamingPolicy;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.google.gson.Gson;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
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;
|
||||||
@@ -56,17 +55,18 @@ import java.util.List;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class RepositoryServiceImplement implements RepositoryService {
|
public class RepositoryServiceImplement implements RepositoryService {
|
||||||
|
|
||||||
private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
|
||||||
|
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final GiteaService giteaService;
|
private final GiteaService giteaService;
|
||||||
private final SettingService settingService;
|
private final SettingService settingService;
|
||||||
|
private final ObjectMapper jackson;
|
||||||
|
|
||||||
private User owner;
|
private User owner;
|
||||||
|
private ObjectMapper giteaJackson;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void postConstruct() {
|
private void postConstruct() {
|
||||||
owner = giteaService.getOwner();
|
owner = giteaService.getOwner();
|
||||||
|
giteaJackson = jackson.copy().setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,8 +80,7 @@ public class RepositoryServiceImplement implements RepositoryService {
|
|||||||
put("limit", page.getLimit());
|
put("limit", page.getLimit());
|
||||||
}})).execute().returnResponse();
|
}})).execute().returnResponse();
|
||||||
String respText = EntityUtils.toString(resp.getEntity());
|
String respText = EntityUtils.toString(resp.getEntity());
|
||||||
GiteaResponse<List<Repository>> respObj = gson.fromJson(respText, new TypeToken<GiteaResponse<List<Repository>>>() {
|
GiteaResponse<List<Repository>> respObj = giteaJackson.readValue(respText, new TypeReference<>() {});
|
||||||
}.getType());
|
|
||||||
|
|
||||||
PageResult<Repository> result = new PageResult<>();
|
PageResult<Repository> result = new PageResult<>();
|
||||||
result.setTotal(Long.parseLong(resp.getHeader("X-Total-Count").getValue()));
|
result.setTotal(Long.parseLong(resp.getHeader("X-Total-Count").getValue()));
|
||||||
@@ -100,7 +99,7 @@ public class RepositoryServiceImplement implements RepositoryService {
|
|||||||
put("owner", owner.getName());
|
put("owner", owner.getName());
|
||||||
put("repoName", repoName);
|
put("repoName", repoName);
|
||||||
}})).execute().returnContent().asString();
|
}})).execute().returnContent().asString();
|
||||||
return gson.fromJson(respText, Repository.class);
|
return giteaJackson.readValue(respText, Repository.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("get repository error", e);
|
log.error("get repository error", e);
|
||||||
throw new TimiException(TimiCode.ERROR, "get repository error", e);
|
throw new TimiException(TimiCode.ERROR, "get repository error", e);
|
||||||
@@ -114,7 +113,7 @@ public class RepositoryServiceImplement implements RepositoryService {
|
|||||||
put("owner", owner.getName());
|
put("owner", owner.getName());
|
||||||
put("repoName", repoName);
|
put("repoName", repoName);
|
||||||
}})).execute().returnContent().asString();
|
}})).execute().returnContent().asString();
|
||||||
return gson.fromJson(respText, new TypeToken<List<Branch>>() {}.getType());
|
return giteaJackson.readValue(respText, new TypeReference<>() {});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("list repository branches error", e);
|
log.error("list repository branches error", e);
|
||||||
throw new TimiException(TimiCode.ERROR, "list repository branches error", e);
|
throw new TimiException(TimiCode.ERROR, "list repository branches error", e);
|
||||||
@@ -131,7 +130,7 @@ public class RepositoryServiceImplement implements RepositoryService {
|
|||||||
}}, new HashMap<>() {{
|
}}, new HashMap<>() {{
|
||||||
put("ref", branch);
|
put("ref", branch);
|
||||||
}})).execute().returnContent().asString();
|
}})).execute().returnContent().asString();
|
||||||
List<File> list = gson.fromJson(respText, new TypeToken<List<File>>() {}.getType());
|
List<File> list = giteaJackson.readValue(respText, new TypeReference<>() {});
|
||||||
// 排序
|
// 排序
|
||||||
list.sort((f1, f2) -> {
|
list.sort((f1, f2) -> {
|
||||||
if (f1.getType() == File.Type.dir && f2.getType() == File.Type.file) {
|
if (f1.getType() == File.Type.dir && f2.getType() == File.Type.file) {
|
||||||
|
|||||||
@@ -1,28 +1,54 @@
|
|||||||
package com.imyeyu.api.modules.git.util;
|
package com.imyeyu.api.modules.git.util;
|
||||||
|
|
||||||
import com.google.gson.JsonDeserializationContext;
|
import com.fasterxml.jackson.core.JacksonException;
|
||||||
import com.google.gson.JsonDeserializer;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
import com.google.gson.JsonElement;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
import com.google.gson.JsonPrimitive;
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
import com.google.gson.JsonSerializationContext;
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
import com.google.gson.JsonSerializer;
|
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.io.IOException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2025-06-26 11:40
|
* @since 2025-06-26 11:40
|
||||||
*/
|
*/
|
||||||
public class GiteaTimestampAdapter implements JsonSerializer<Long>, JsonDeserializer<Long> {
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public final class GiteaTimestampAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-04-07 22:16
|
||||||
|
*/
|
||||||
|
public static class Serializer extends JsonSerializer<Long> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long deserialize(JsonElement json, Type type, JsonDeserializationContext context) {
|
public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
return Instant.parse(json.getAsString()).toEpochMilli();
|
if (value == null) {
|
||||||
|
gen.writeNull();
|
||||||
|
} else {
|
||||||
|
gen.writeNumber(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-04-07 22:19
|
||||||
|
*/
|
||||||
|
public static class Deserializer extends JsonDeserializer<Long> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonElement serialize(Long src, Type type, JsonSerializationContext context) {
|
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
|
||||||
return new JsonPrimitive(src);
|
return Instant.parse(p.getText()).toEpochMilli();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.git.vo.issue;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import com.imyeyu.api.modules.common.entity.Comment;
|
|
||||||
import com.imyeyu.spring.bean.Page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2024-02-28 14:27
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class CommentPage extends Page {
|
|
||||||
|
|
||||||
private Comment.BizType bizType;
|
|
||||||
|
|
||||||
private long bizId;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.gitea.bean;
|
package com.imyeyu.api.modules.gitea.bean;
|
||||||
|
|
||||||
import com.google.gson.annotations.JsonAdapter;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.imyeyu.api.modules.gitea.util.GiteaUTCTimestampAdapter;
|
import com.imyeyu.api.modules.gitea.util.GiteaUTCTimestampAdapter;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -50,14 +51,15 @@ public class ActionLogDTO {
|
|||||||
@Data
|
@Data
|
||||||
public static class Commit {
|
public static class Commit {
|
||||||
|
|
||||||
@SerializedName("Sha1")
|
@JsonProperty("Sha1")
|
||||||
private String sha;
|
private String sha;
|
||||||
|
|
||||||
@SerializedName("Message")
|
@JsonProperty("Message")
|
||||||
private String message;
|
private String message;
|
||||||
|
|
||||||
@JsonAdapter(GiteaUTCTimestampAdapter.class)
|
@JsonProperty("Timestamp")
|
||||||
@SerializedName("Timestamp")
|
@JsonSerialize(using = GiteaUTCTimestampAdapter.Serializer.class)
|
||||||
|
@JsonDeserialize(using = GiteaUTCTimestampAdapter.Deserializer.class)
|
||||||
private Long timestamp;
|
private Long timestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ public interface ActionMapper {
|
|||||||
|
|
||||||
long count(Long repoId, String branch, ActionLogDTO.Operation operation);
|
long count(Long repoId, String branch, ActionLogDTO.Operation operation);
|
||||||
|
|
||||||
List<ActionLogDTO> list(Long repoId, String branch, ActionLogDTO.Operation operation, long offset, int limit);
|
List<ActionLogDTO> list(Long repoId, String branch, ActionLogDTO.Operation operation, long offset, long limit);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package com.imyeyu.api.modules.gitea.util;
|
package com.imyeyu.api.modules.gitea.util;
|
||||||
|
|
||||||
import com.google.gson.JsonDeserializationContext;
|
import com.fasterxml.jackson.core.JacksonException;
|
||||||
import com.google.gson.JsonDeserializer;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
import com.google.gson.JsonElement;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
import com.google.gson.JsonPrimitive;
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
import com.google.gson.JsonSerializationContext;
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
import com.google.gson.JsonSerializer;
|
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.io.IOException;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
@@ -18,15 +19,21 @@ import java.time.ZoneOffset;
|
|||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2025-06-27 16:18
|
* @since 2025-06-27 16:18
|
||||||
*/
|
*/
|
||||||
public class GiteaUTCTimestampAdapter implements JsonDeserializer<Long>, JsonSerializer<Long> {
|
public class GiteaUTCTimestampAdapter {
|
||||||
|
|
||||||
|
public static class Deserializer extends JsonDeserializer<Long> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long deserialize(JsonElement json, Type type, JsonDeserializationContext context) {
|
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
|
||||||
return OffsetDateTime.parse(json.getAsString()).toInstant().toEpochMilli();
|
return OffsetDateTime.parse(p.getText()).toInstant().toEpochMilli();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Serializer extends JsonSerializer<Long> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonElement serialize(Long timestamp, Type type, JsonSerializationContext context) {
|
public void serialize(Long timestamp, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
return new JsonPrimitive(OffsetDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).toString());
|
gen.writeString(OffsetDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC).toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package com.imyeyu.api.modules.gitea.vo;
|
package com.imyeyu.api.modules.gitea.vo;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.google.gson.JsonObject;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.google.gson.JsonParser;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import com.imyeyu.java.TimiJava;
|
import com.imyeyu.java.TimiJava;
|
||||||
import com.imyeyu.api.TimiServerAPI;
|
import com.imyeyu.api.TimiServerAPI;
|
||||||
import com.imyeyu.api.modules.common.service.UserService;
|
import com.imyeyu.api.modules.common.service.UserService;
|
||||||
@@ -34,7 +33,7 @@ public class ActionLogView {
|
|||||||
private UserView operator;
|
private UserView operator;
|
||||||
|
|
||||||
public static ActionLogView fromDTO(ActionLogDTO dto) {
|
public static ActionLogView fromDTO(ActionLogDTO dto) {
|
||||||
Gson gson = TimiServerAPI.applicationContext.getBean(Gson.class);
|
ObjectMapper jackson = TimiServerAPI.applicationContext.getBean(ObjectMapper.class);
|
||||||
UserService userService = TimiServerAPI.applicationContext.getBean(UserService.class);
|
UserService userService = TimiServerAPI.applicationContext.getBean(UserService.class);
|
||||||
|
|
||||||
ActionLogView view = new ActionLogView();
|
ActionLogView view = new ActionLogView();
|
||||||
@@ -45,8 +44,9 @@ public class ActionLogView {
|
|||||||
view.setOperator(userService.view((long) dto.getOperatorId()).doFilter());
|
view.setOperator(userService.view((long) dto.getOperatorId()).doFilter());
|
||||||
{
|
{
|
||||||
if (TimiJava.isNotEmpty(dto.getContent())) {
|
if (TimiJava.isNotEmpty(dto.getContent())) {
|
||||||
JsonObject content = JsonParser.parseString(dto.getContent()).getAsJsonObject();
|
try {
|
||||||
List<ActionLogDTO.Commit> commitList = gson.fromJson(content.get("Commits"), new TypeToken<List<ActionLogDTO.Commit>>() {}.getType());
|
JsonNode content = jackson.readTree(dto.getContent());
|
||||||
|
List<ActionLogDTO.Commit> commitList = jackson.convertValue(content.get("Commits"), new TypeReference<>() {});
|
||||||
view.setCommitList(new ArrayList<>());
|
view.setCommitList(new ArrayList<>());
|
||||||
for (ActionLogDTO.Commit dtoCommit : commitList) {
|
for (ActionLogDTO.Commit dtoCommit : commitList) {
|
||||||
Commit commit = new Commit();
|
Commit commit = new Commit();
|
||||||
@@ -55,6 +55,9 @@ public class ActionLogView {
|
|||||||
commit.setCommittedAt(dtoCommit.getTimestamp());
|
commit.setCommittedAt(dtoCommit.getTimestamp());
|
||||||
view.getCommitList().add(commit);
|
view.getCommitList().add(commit);
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("read gitea action log content error", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return view;
|
return view;
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.bean;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.journal.entity.Journal;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 经纬度位置
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-05 15:26
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Location {
|
||||||
|
|
||||||
|
/** 经度 */
|
||||||
|
private Double lng;
|
||||||
|
|
||||||
|
/** 维度 */
|
||||||
|
private Double lat;
|
||||||
|
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public Location(Journal journal) {
|
||||||
|
this.lat = journal.getLat();
|
||||||
|
this.lng = journal.getLng();
|
||||||
|
this.text = journal.getLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-01-29 10:59
|
||||||
|
*/
|
||||||
|
public @interface RequiredUploadPermission {
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package com.imyeyu.api.modules.journal.bean;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2025-09-30 11:51
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class Travel {
|
|
||||||
|
|
||||||
private Luggage luggage;
|
|
||||||
|
|
||||||
private List<Guide> guides;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2025-09-30 11:52
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class Luggage {
|
|
||||||
|
|
||||||
private List<Item> gao;
|
|
||||||
|
|
||||||
private List<Item> yu;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2025-09-30 11:52
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class Item {
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
private boolean isTaken;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2025-09-30 11:54
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public static class Guide {
|
|
||||||
|
|
||||||
private String title;
|
|
||||||
|
|
||||||
private List<String> images;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +1,45 @@
|
|||||||
package com.imyeyu.api.modules.journal.controller;
|
package com.imyeyu.api.modules.journal.controller;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.imyeyu.api.bean.PreviewPage;
|
||||||
import com.imyeyu.api.bean.wechat.InitCodeResponse;
|
import com.imyeyu.api.bean.wechat.InitCodeResponse;
|
||||||
import com.imyeyu.api.modules.common.bean.MediaAttach;
|
import com.imyeyu.api.modules.common.bean.MediaAttach;
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
import com.imyeyu.api.modules.journal.bean.Travel;
|
import com.imyeyu.api.modules.journal.bean.RequiredUploadPermission;
|
||||||
import com.imyeyu.api.modules.journal.entity.Journal;
|
import com.imyeyu.api.modules.journal.entity.Journal;
|
||||||
import com.imyeyu.api.modules.journal.service.JournalService;
|
import com.imyeyu.api.modules.journal.service.JournalService;
|
||||||
import com.imyeyu.api.modules.journal.vo.AppendRequest;
|
import com.imyeyu.api.modules.journal.util.JournalAPIInterceptor;
|
||||||
import com.imyeyu.api.modules.journal.vo.ArchiveRequest;
|
import com.imyeyu.api.modules.journal.vo.journal.ArchiveRequest;
|
||||||
import com.imyeyu.api.modules.journal.vo.JournalPage;
|
import com.imyeyu.api.modules.journal.vo.journal.JournalRequest;
|
||||||
import com.imyeyu.api.modules.journal.vo.JournalRequest;
|
import com.imyeyu.api.modules.journal.vo.journal.JournalResponse;
|
||||||
import com.imyeyu.api.modules.journal.vo.JournalResponse;
|
import com.imyeyu.api.modules.journal.vo.journal.UpdateRequest;
|
||||||
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.network.ArgMap;
|
import com.imyeyu.network.ArgMap;
|
||||||
import com.imyeyu.network.GsonRequest;
|
|
||||||
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.bean.Page;
|
||||||
import com.imyeyu.spring.bean.PageResult;
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
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.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 微信小程序糕雨日记接口
|
||||||
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2025-09-26 15:55
|
* @since 2025-09-26 15:55
|
||||||
*/
|
*/
|
||||||
@@ -44,22 +49,34 @@ import java.util.List;
|
|||||||
@RequestMapping("/journal")
|
@RequestMapping("/journal")
|
||||||
public class JournalController {
|
public class JournalController {
|
||||||
|
|
||||||
private final Gson gson;
|
|
||||||
private final JournalService service;
|
private final JournalService service;
|
||||||
private final SettingService settingService;
|
private final SettingService settingService;
|
||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
|
private final ObjectMapper jackson;
|
||||||
|
|
||||||
|
private final JournalAPIInterceptor apiInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化用户 OpenId
|
||||||
|
*
|
||||||
|
* @param code 微信授权码
|
||||||
|
* @return OpenId
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@PostMapping("/openid")
|
@PostMapping("/openid")
|
||||||
public String initOpenId(@RequestSingleParam String code) {
|
public String initOpenId(@RequestBody String code) {
|
||||||
try {
|
try {
|
||||||
ArgMap<String, String> args = new ArgMap<>();
|
ArgMap<String, String> args = new ArgMap<>();
|
||||||
args.put("appid", settingService.getAsString(SettingKey.JOURNAL_APP_ID));
|
args.put("appid", settingService.getAsString(SettingKey.JOURNAL_APP_ID));
|
||||||
args.put("secret", settingService.getAsString(SettingKey.JOURNAL_APP_SECRET));
|
args.put("secret", settingService.getAsString(SettingKey.JOURNAL_APP_SECRET));
|
||||||
args.put("js_code", code);
|
args.put("js_code", code);
|
||||||
args.put("grant_type", "authorization_code");
|
args.put("grant_type", "authorization_code");
|
||||||
InitCodeResponse resp = GsonRequest.get(args.toURL("https://api.weixin.qq.com/sns/jscode2session")).resultAs(InitCodeResponse.class);
|
String response = org.apache.hc.client5.http.fluent.Request.get(args.toURL("https://api.weixin.qq.com/sns/jscode2session"))
|
||||||
|
.execute()
|
||||||
|
.returnContent()
|
||||||
|
.asString();
|
||||||
|
InitCodeResponse resp = jackson.readValue(response, InitCodeResponse.class);
|
||||||
return resp.getOpenid();
|
return resp.getOpenid();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("init WeChat openId error", e);
|
log.error("init WeChat openId error", e);
|
||||||
@@ -67,35 +84,98 @@ public class JournalController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/can-upload")
|
||||||
|
public boolean canUpload() {
|
||||||
|
return apiInterceptor.canUploadKey();
|
||||||
|
}
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
|
@RequestMapping("/{id}")
|
||||||
|
public JournalResponse detail(@PathVariable Long id) {
|
||||||
|
Journal journal = service.get(id);
|
||||||
|
JournalResponse resp = new JournalResponse();
|
||||||
|
Page<Attachment> attachPage = new Page<>();
|
||||||
|
{
|
||||||
|
Attachment example = new Attachment();
|
||||||
|
example.setBizType(Attachment.BizType.JOURNAL);
|
||||||
|
example.setBizId(journal.getId());
|
||||||
|
attachPage.setEqualsExample(example);
|
||||||
|
}
|
||||||
|
attachPage.setIndex(0);
|
||||||
|
attachPage.setSize(Long.MAX_VALUE);
|
||||||
|
resp.setItems(attachmentService.page(attachPage).getList());
|
||||||
|
BeanUtils.copyProperties(journal, resp);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建记录
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
public void create(@RequestBody JournalRequest request) {
|
public void create(@RequestBody JournalRequest request) {
|
||||||
service.create(request);
|
service.create(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/append")
|
/**
|
||||||
public void append(@RequestBody AppendRequest request) {
|
* 更新记录(支持附件差分保存)
|
||||||
service.appendItems(request);
|
*
|
||||||
}
|
* @param request 更新请求,包含 id、基本信息、保留的附件 ID 列表和新上传的临时文件 ID 列表
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@PostMapping("/delete")
|
@RequestRateLimit
|
||||||
public void delete(@RequestBody Long thumbId) {
|
@RequiredUploadPermission
|
||||||
attachmentService.deleteMedia(thumbId);
|
@PostMapping("/update")
|
||||||
|
public void update(@RequestBody @Valid UpdateRequest request) {
|
||||||
|
service.update(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除记录
|
||||||
|
*
|
||||||
|
* @param id 记录 ID
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequiredUploadPermission
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public void delete(@RequestBody Long id) {
|
||||||
|
service.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录列表
|
||||||
|
*
|
||||||
|
* @param page 查询页面
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@RequestMapping("/list")
|
@RequestMapping("/list")
|
||||||
public PageResult<JournalResponse> list(@RequestBody JournalPage page) {
|
public PageResult<JournalResponse> list(@RequestBody PreviewPage<Journal> page) {
|
||||||
PageResult<Journal> pageResult = service.page(page);
|
PageResult<Journal> pageResult = service.page(page);
|
||||||
|
|
||||||
PageResult<JournalResponse> result = new PageResult<>();
|
PageResult<JournalResponse> result = new PageResult<>();
|
||||||
result.setTotal(pageResult.getTotal());
|
result.setTotal(pageResult.getTotal());
|
||||||
result.setList(pageResult.getList().stream().map(item -> {
|
result.setList(pageResult.getList().stream().map(item -> {
|
||||||
JournalResponse resp = new JournalResponse();
|
JournalResponse resp = new JournalResponse();
|
||||||
resp.setItems(attachmentService.listByBizId(Attachment.BizType.JOURNAL, item.getId()));
|
Page<Attachment> attachPage = new Page<>();
|
||||||
|
{
|
||||||
|
Attachment example = new Attachment();
|
||||||
|
example.setBizType(Attachment.BizType.JOURNAL);
|
||||||
|
example.setBizId(item.getId());
|
||||||
|
attachPage.setEqualsExample(example);
|
||||||
|
}
|
||||||
|
attachPage.setIndex(0);
|
||||||
|
attachPage.setSize(switch (page.getType()) {
|
||||||
|
case NORMAL -> Long.MAX_VALUE;
|
||||||
|
case PREVIEW -> 2; // 原图缩略图并存
|
||||||
|
});
|
||||||
|
resp.setItems(attachmentService.page(attachPage).getList());
|
||||||
BeanUtils.copyProperties(item, resp);
|
BeanUtils.copyProperties(item, resp);
|
||||||
return resp;
|
return resp;
|
||||||
}).toList());
|
}).toList());
|
||||||
@@ -104,11 +184,35 @@ public class JournalController {
|
|||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@GetMapping("/list/date")
|
@PostMapping("/list/ids")
|
||||||
public Long[] listDate() {
|
public List<JournalResponse> listByIds(@RequestBody Long[] ids) {
|
||||||
return service.listDate();
|
List<JournalResponse> result = new ArrayList<>();
|
||||||
|
|
||||||
|
List<Journal> journals = service.listByIds(ids);
|
||||||
|
for (int i = 0; i < journals.size(); i++) {
|
||||||
|
Journal journal = journals.get(i);
|
||||||
|
JournalResponse resp = new JournalResponse();
|
||||||
|
Page<Attachment> attachPage = new Page<>();
|
||||||
|
{
|
||||||
|
Attachment example = new Attachment();
|
||||||
|
example.setBizType(Attachment.BizType.JOURNAL);
|
||||||
|
example.setBizId(journal.getId());
|
||||||
|
attachPage.setEqualsExample(example);
|
||||||
|
}
|
||||||
|
attachPage.setIndex(0);
|
||||||
|
attachPage.setSize(Long.MAX_VALUE);
|
||||||
|
resp.setItems(attachmentService.page(attachPage).getList());
|
||||||
|
BeanUtils.copyProperties(journal, resp);
|
||||||
|
result.add(resp);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已记录照片、视频数量
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@GetMapping("/total")
|
@GetMapping("/total")
|
||||||
public long total() {
|
public long total() {
|
||||||
@@ -117,33 +221,48 @@ public class JournalController {
|
|||||||
return journal + journalTravel;
|
return journal + journalTravel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestRateLimit
|
/**
|
||||||
@GetMapping("/travel")
|
* 创建瞬间(上传的临时文件持久化储存,微信限制单次上传数量)
|
||||||
public Travel getTravel() {
|
*
|
||||||
return service.getTravel();
|
* @param tempFileIds
|
||||||
}
|
* @return
|
||||||
|
*/
|
||||||
@AOPLog
|
|
||||||
@RequestRateLimit
|
|
||||||
@PostMapping("/travel/luggage/update")
|
|
||||||
public void updateTravel(@RequestBody Travel.Luggage luggage) {
|
|
||||||
service.updateTravelLuggage(luggage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
@PostMapping("/moment/create")
|
@PostMapping("/moment/create")
|
||||||
public List<Attachment> createMoment(@RequestBody String[] tempFileIds) {
|
public List<Attachment> createMoment(@RequestBody String[] tempFileIds) {
|
||||||
return service.createMoment(tempFileIds);
|
return service.createMoment(tempFileIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 瞬间列表
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@PostMapping("/moment/list")
|
@PostMapping("/moment/list")
|
||||||
public List<Attachment> listMoment() {
|
public List<Attachment> listMoment() {
|
||||||
return service.listMoment();
|
Page<Attachment> page = new Page<>();
|
||||||
|
{
|
||||||
|
Attachment example = new Attachment();
|
||||||
|
example.setBizType(Attachment.BizType.JOURNAL_MOMENT);
|
||||||
|
example.setBizId(0L);
|
||||||
|
example.setAttachTypeValue(MediaAttach.Type.THUMB);
|
||||||
|
page.setEqualsExample(example);
|
||||||
|
}
|
||||||
|
page.setIndex(0);
|
||||||
|
page.setSize(Long.MAX_VALUE);
|
||||||
|
return attachmentService.page(page).getList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查待上传文件是否已存在
|
||||||
|
*
|
||||||
|
* @param md5s
|
||||||
|
* @return 不存在的文件 md5 列表
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
@PostMapping("/moment/filter")
|
@PostMapping("/moment/filter")
|
||||||
@@ -151,15 +270,27 @@ public class JournalController {
|
|||||||
return service.filterExistMoment(md5s);
|
return service.filterExistMoment(md5s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除瞬间
|
||||||
|
*
|
||||||
|
* @param thumbIds 缩略图附件 ID 列表
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
@PostMapping("/moment/delete")
|
@PostMapping("/moment/delete")
|
||||||
public void deleteMoment(@RequestBody Long[] thumbIds) {
|
public void deleteMoment(@RequestBody Long[] thumbIds) {
|
||||||
service.deleteMoment(thumbIds);
|
service.deleteMoment(thumbIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 归档瞬间,归档记录 ID 不为空时表示归档到指定记录
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
@AOPLog
|
@AOPLog
|
||||||
@RequestRateLimit
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
@PostMapping("/moment/archive")
|
@PostMapping("/moment/archive")
|
||||||
public void archiveMoment(@RequestBody ArchiveRequest request) {
|
public void archiveMoment(@RequestBody ArchiveRequest request) {
|
||||||
service.archiveMoment(request);
|
service.archiveMoment(request);
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Setting;
|
||||||
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
|
import com.imyeyu.api.modules.journal.bean.RequiredUploadPermission;
|
||||||
|
import com.imyeyu.spring.annotation.RequestBodyValue;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2026-01-28 11:36
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/journal/tool")
|
||||||
|
public class ToolController {
|
||||||
|
|
||||||
|
private final SettingService settingService;
|
||||||
|
|
||||||
|
@GetMapping("/memo")
|
||||||
|
public String getMemo() {
|
||||||
|
return settingService.getAsString(SettingKey.JOURNAL_MEMO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredUploadPermission
|
||||||
|
@PostMapping("/memo/update")
|
||||||
|
public void updateMemo(@RequestBodyValue String data) {
|
||||||
|
Setting setting = settingService.getByKey(SettingKey.JOURNAL_MEMO);
|
||||||
|
setting.setValue(data);
|
||||||
|
settingService.update(setting);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.journal.bean.RequiredUploadPermission;
|
||||||
|
import com.imyeyu.api.modules.journal.entity.Travel;
|
||||||
|
import com.imyeyu.api.modules.journal.service.TravelService;
|
||||||
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import com.imyeyu.spring.bean.Page;
|
||||||
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行计划接口
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:50
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/journal/travel")
|
||||||
|
public class TravelController {
|
||||||
|
|
||||||
|
private final TravelService service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建旅行计划
|
||||||
|
*
|
||||||
|
* @param travel 旅行计划
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
|
@PostMapping("/create")
|
||||||
|
public void create(@RequestBody @Valid Travel travel) {
|
||||||
|
service.create(travel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新旅行计划
|
||||||
|
*
|
||||||
|
* @param travel 旅行计划
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
|
@PostMapping("/update")
|
||||||
|
public void update(@RequestBody @Valid Travel travel) {
|
||||||
|
service.update(travel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除旅行计划(级联删除关联地点和附件)
|
||||||
|
*
|
||||||
|
* @param id 旅行 ID
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public void delete(@RequestBody Long id) {
|
||||||
|
service.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询旅行计划详情
|
||||||
|
*
|
||||||
|
* @param id 旅行 ID
|
||||||
|
* @return 旅行计划
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Travel detail(@PathVariable Long id) {
|
||||||
|
return service.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行计划列表
|
||||||
|
*
|
||||||
|
* @param page 分页参数
|
||||||
|
* @return 旅行列表
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@PostMapping("/list")
|
||||||
|
public PageResult<Travel> list(@RequestBody Page<Travel> page) {
|
||||||
|
return service.page(page);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.controller;
|
||||||
|
|
||||||
|
import com.imyeyu.api.bean.PreviewPage;
|
||||||
|
import com.imyeyu.api.modules.common.bean.MediaAttach;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
|
import com.imyeyu.api.modules.journal.bean.RequiredUploadPermission;
|
||||||
|
import com.imyeyu.api.modules.journal.entity.TravelLocation;
|
||||||
|
import com.imyeyu.api.modules.journal.service.TravelLocationService;
|
||||||
|
import com.imyeyu.spring.annotation.AOPLog;
|
||||||
|
import com.imyeyu.spring.annotation.RequestRateLimit;
|
||||||
|
import com.imyeyu.spring.bean.Page;
|
||||||
|
import com.imyeyu.spring.bean.PageResult;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行地点接口
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:50
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RequestMapping("/journal/travel/location")
|
||||||
|
public class TravelLocationController {
|
||||||
|
|
||||||
|
private final AttachmentService attachmentService;
|
||||||
|
private final TravelLocationService service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建旅行地点
|
||||||
|
*
|
||||||
|
* @param location 旅行地点
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
|
@PostMapping("/create")
|
||||||
|
public void create(@RequestBody @Valid TravelLocation location) {
|
||||||
|
service.create(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新旅行地点
|
||||||
|
*
|
||||||
|
* @param location 旅行地点
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
|
@PostMapping("/update")
|
||||||
|
public void update(@RequestBody @Valid TravelLocation location) {
|
||||||
|
service.update(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除旅行地点(级联删除附件)
|
||||||
|
*
|
||||||
|
* @param id 地点 ID
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@RequiredUploadPermission
|
||||||
|
@PostMapping("/delete")
|
||||||
|
public void delete(@RequestBody Long id) {
|
||||||
|
service.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询旅行地点详情
|
||||||
|
*
|
||||||
|
* @param id 地点 ID
|
||||||
|
* @return 旅行地点
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public TravelLocation detail(@PathVariable Long id) {
|
||||||
|
TravelLocation location = service.get(id);
|
||||||
|
location.setItems(attachmentService.listByBizId(Attachment.BizType.JOURNAL_TRAVEL, location.getId()));
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行地点列表
|
||||||
|
*
|
||||||
|
* @param page 分页参数
|
||||||
|
* @return 地点列表
|
||||||
|
*/
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@PostMapping("/list")
|
||||||
|
public PageResult<TravelLocation> list(@RequestBody PreviewPage<TravelLocation> page) {
|
||||||
|
PageResult<TravelLocation> result = service.page(page);
|
||||||
|
for (TravelLocation location : result.getList()) {
|
||||||
|
Page<Attachment> attachPage = new Page<>();
|
||||||
|
{
|
||||||
|
Attachment example = new Attachment();
|
||||||
|
example.setBizType(Attachment.BizType.JOURNAL_TRAVEL);
|
||||||
|
example.setBizId(location.getId());
|
||||||
|
example.setAttachTypeValue(MediaAttach.Type.THUMB);
|
||||||
|
attachPage.setEqualsExample(example);
|
||||||
|
}
|
||||||
|
attachPage.setIndex(0);
|
||||||
|
attachPage.setSize(1); // 列表接口只允许预览
|
||||||
|
location.setItems(attachmentService.page(attachPage).getList());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AOPLog
|
||||||
|
@RequestRateLimit
|
||||||
|
@PostMapping("/list/ids")
|
||||||
|
public List<TravelLocation> listByIds(@RequestBody Long[] ids) {
|
||||||
|
List<TravelLocation> locationList = service.listByIds(ids);
|
||||||
|
for (TravelLocation location : locationList) {
|
||||||
|
Page<Attachment> attachPage = new Page<>();
|
||||||
|
{
|
||||||
|
Attachment example = new Attachment();
|
||||||
|
example.setBizType(Attachment.BizType.JOURNAL_TRAVEL);
|
||||||
|
example.setBizId(location.getId());
|
||||||
|
attachPage.setEqualsExample(example);
|
||||||
|
}
|
||||||
|
attachPage.setIndex(0);
|
||||||
|
attachPage.setSize(Long.MAX_VALUE);
|
||||||
|
location.setItems(attachmentService.page(attachPage).getList());
|
||||||
|
}
|
||||||
|
return locationList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.spring.annotation.table.PageIgnore;
|
||||||
|
import com.imyeyu.spring.entity.Entity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行计划
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class Travel extends Entity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交通类型
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:30
|
||||||
|
*/
|
||||||
|
public enum TransportationType {
|
||||||
|
|
||||||
|
/** 飞机 */
|
||||||
|
PLANE,
|
||||||
|
|
||||||
|
/** 火车 */
|
||||||
|
TRAIN,
|
||||||
|
|
||||||
|
/** 汽车 */
|
||||||
|
CAR,
|
||||||
|
|
||||||
|
/** 轮船 */
|
||||||
|
SHIP,
|
||||||
|
|
||||||
|
/** 自驾 */
|
||||||
|
SELF_DRIVING,
|
||||||
|
|
||||||
|
/** 其他 */
|
||||||
|
OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行状态
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:30
|
||||||
|
*/
|
||||||
|
public enum Status {
|
||||||
|
|
||||||
|
/** 计划中 */
|
||||||
|
PLANNING,
|
||||||
|
|
||||||
|
/** 进行中 */
|
||||||
|
ONGOING,
|
||||||
|
|
||||||
|
/** 已完成 */
|
||||||
|
COMPLETED,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 交通类型 */
|
||||||
|
private TransportationType transportationType;
|
||||||
|
|
||||||
|
/** 标题 */
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/** 内容 */
|
||||||
|
@PageIgnore
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/** 出行时间 */
|
||||||
|
private Long travelAt;
|
||||||
|
|
||||||
|
/** 天数 */
|
||||||
|
private Integer days;
|
||||||
|
|
||||||
|
/** 状态 */
|
||||||
|
private Status status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.entity;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
|
import com.imyeyu.spring.annotation.table.Transient;
|
||||||
|
import com.imyeyu.spring.entity.Entity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行地点
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class TravelLocation extends Entity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地点类型
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:30
|
||||||
|
*/
|
||||||
|
public enum Type {
|
||||||
|
|
||||||
|
/** 美食 */
|
||||||
|
FOOD,
|
||||||
|
|
||||||
|
/** 酒店 */
|
||||||
|
HOTEL,
|
||||||
|
|
||||||
|
/** 交通站点 */
|
||||||
|
TRANSPORT,
|
||||||
|
|
||||||
|
/** 景点 */
|
||||||
|
ATTRACTION,
|
||||||
|
|
||||||
|
/** 商场 */
|
||||||
|
MALL,
|
||||||
|
|
||||||
|
/** 购物 */
|
||||||
|
SHOPPING,
|
||||||
|
|
||||||
|
/** 玩乐 */
|
||||||
|
PLAY,
|
||||||
|
|
||||||
|
/** 生活 */
|
||||||
|
LIFE,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 旅行计划 ID */
|
||||||
|
private Long travelId;
|
||||||
|
|
||||||
|
/** 类型 */
|
||||||
|
private Type type;
|
||||||
|
|
||||||
|
/** 标题 */
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
/** 说明 */
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/** 纬度 */
|
||||||
|
private Double lat;
|
||||||
|
|
||||||
|
/** 经度 */
|
||||||
|
private Double lng;
|
||||||
|
|
||||||
|
/** 位置 */
|
||||||
|
private String location;
|
||||||
|
|
||||||
|
/** 费用 */
|
||||||
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
/** true 为需要身份证 */
|
||||||
|
private Boolean requireIdCard;
|
||||||
|
|
||||||
|
/** true 为需要预约 */
|
||||||
|
private Boolean requireAppointment;
|
||||||
|
|
||||||
|
/** 评分 */
|
||||||
|
private Integer score;
|
||||||
|
|
||||||
|
/** 重要程度 */
|
||||||
|
private Integer importance;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private Long[] attachmentIds;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private String[] tempFileIds;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private List<Attachment> items;
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package com.imyeyu.api.modules.journal.mapper;
|
|||||||
|
|
||||||
import com.imyeyu.api.modules.journal.entity.Journal;
|
import com.imyeyu.api.modules.journal.entity.Journal;
|
||||||
import com.imyeyu.spring.mapper.BaseMapper;
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -12,12 +11,5 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface JournalMapper extends BaseMapper<Journal, Long> {
|
public interface JournalMapper extends BaseMapper<Journal, Long> {
|
||||||
|
|
||||||
@Select("SELECT COUNT(1) FROM `journal` WHERE `type` = #{type}")
|
List<Journal> listByIds(Long[] ids);
|
||||||
long countByType(Journal.Type type);
|
|
||||||
|
|
||||||
@Select("SELECT * FROM `journal` WHERE `type` = #{type} AND `deleted_at` IS NULL ORDER BY `created_at` DESC LIMIT #{offset}, #{limit}")
|
|
||||||
List<Journal> listByType(Journal.Type type, long offset, int limit);
|
|
||||||
|
|
||||||
@Select("SELECT created_at FROM `journal` WHERE `deleted_at` IS NULL")
|
|
||||||
Long[] listDate();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.journal.entity.TravelLocation;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行地点 Mapper
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:35
|
||||||
|
*/
|
||||||
|
public interface TravelLocationMapper extends BaseMapper<TravelLocation, Long> {
|
||||||
|
|
||||||
|
List<TravelLocation> listByIds(Long[] ids);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.mapper;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.journal.entity.Travel;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行计划 Mapper
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:35
|
||||||
|
*/
|
||||||
|
public interface TravelMapper extends BaseMapper<Travel, Long> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 ID 列表查询
|
||||||
|
*
|
||||||
|
* @param ids ID 列表
|
||||||
|
* @return 旅行列表
|
||||||
|
*/
|
||||||
|
List<Travel> listByIds(@Param("ids") Long[] ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态查询
|
||||||
|
*
|
||||||
|
* @param status 状态
|
||||||
|
* @return 旅行列表
|
||||||
|
*/
|
||||||
|
List<Travel> listByStatus(@Param("status") Travel.Status status);
|
||||||
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
package com.imyeyu.api.modules.journal.service;
|
package com.imyeyu.api.modules.journal.service;
|
||||||
|
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.journal.bean.Travel;
|
|
||||||
import com.imyeyu.api.modules.journal.entity.Journal;
|
import com.imyeyu.api.modules.journal.entity.Journal;
|
||||||
import com.imyeyu.api.modules.journal.vo.AppendRequest;
|
import com.imyeyu.api.modules.journal.vo.journal.ArchiveRequest;
|
||||||
import com.imyeyu.api.modules.journal.vo.ArchiveRequest;
|
import com.imyeyu.api.modules.journal.vo.journal.JournalRequest;
|
||||||
import com.imyeyu.api.modules.journal.vo.JournalPage;
|
import com.imyeyu.api.modules.journal.vo.journal.UpdateRequest;
|
||||||
import com.imyeyu.api.modules.journal.vo.JournalRequest;
|
|
||||||
import com.imyeyu.java.bean.timi.TimiException;
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
import com.imyeyu.spring.bean.PageResult;
|
|
||||||
import com.imyeyu.spring.service.DeletableService;
|
import com.imyeyu.spring.service.DeletableService;
|
||||||
|
import com.imyeyu.spring.service.GettableService;
|
||||||
|
import com.imyeyu.spring.service.PageableService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -17,27 +16,26 @@ import java.util.List;
|
|||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @since 2025-09-26 15:53
|
* @since 2025-09-26 15:53
|
||||||
*/
|
*/
|
||||||
public interface JournalService extends DeletableService<Long> {
|
public interface JournalService extends PageableService<Journal>, GettableService<Journal, Long>, DeletableService<Long> {
|
||||||
|
|
||||||
PageResult<Journal> page(JournalPage page) throws TimiException;
|
|
||||||
|
|
||||||
Long[] listDate() throws TimiException;
|
|
||||||
|
|
||||||
void create(JournalRequest request) throws TimiException;
|
void create(JournalRequest request) throws TimiException;
|
||||||
|
|
||||||
void appendItems(AppendRequest request) throws TimiException;
|
void update(UpdateRequest request) throws TimiException;
|
||||||
|
|
||||||
|
List<Journal> listByIds(Long... ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤已存在瞬间
|
||||||
|
*
|
||||||
|
* @param md5s 原图文件 MD5 列表
|
||||||
|
* @return 未持久化储存的 MD5 列表
|
||||||
|
* @throws TimiException
|
||||||
|
*/
|
||||||
String[] filterExistMoment(String[] md5s) throws TimiException;
|
String[] filterExistMoment(String[] md5s) throws TimiException;
|
||||||
|
|
||||||
List<Attachment> createMoment(String[] tempFileIds) throws TimiException;
|
List<Attachment> createMoment(String[] tempFileIds) throws TimiException;
|
||||||
|
|
||||||
List<Attachment> listMoment() throws TimiException;
|
|
||||||
|
|
||||||
void deleteMoment(Long[] thumbIds) throws TimiException;
|
void deleteMoment(Long[] thumbIds) throws TimiException;
|
||||||
|
|
||||||
void archiveMoment(ArchiveRequest request) throws TimiException;
|
void archiveMoment(ArchiveRequest request) throws TimiException;
|
||||||
|
|
||||||
Travel getTravel() throws TimiException;
|
|
||||||
|
|
||||||
void updateTravelLuggage(Travel.Luggage luggage) throws TimiException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.journal.entity.TravelLocation;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行地点服务
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:40
|
||||||
|
*/
|
||||||
|
public interface TravelLocationService extends BaseService<TravelLocation, Long> {
|
||||||
|
|
||||||
|
List<TravelLocation> listByIds(Long... ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据旅行 ID 删除所有地点
|
||||||
|
*
|
||||||
|
* @param travelId 旅行 ID
|
||||||
|
* @throws TimiException 服务异常
|
||||||
|
*/
|
||||||
|
void deleteByTravelId(Long travelId) throws TimiException;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.service;
|
||||||
|
|
||||||
|
import com.imyeyu.api.modules.journal.entity.Travel;
|
||||||
|
import com.imyeyu.spring.service.BaseService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行计划服务
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:40
|
||||||
|
*/
|
||||||
|
public interface TravelService extends BaseService<Travel, Long> {
|
||||||
|
}
|
||||||
@@ -1,30 +1,22 @@
|
|||||||
package com.imyeyu.api.modules.journal.service.implement;
|
package com.imyeyu.api.modules.journal.service.implement;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
import com.imyeyu.api.modules.common.bean.MediaAttach;
|
import com.imyeyu.api.modules.common.bean.MediaAttach;
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
import com.imyeyu.api.modules.common.bean.Metadata;
|
||||||
import com.imyeyu.api.modules.common.bean.TempFileMetaData;
|
|
||||||
import com.imyeyu.api.modules.common.entity.Attachment;
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
import com.imyeyu.api.modules.common.entity.Setting;
|
|
||||||
import com.imyeyu.api.modules.common.service.AttachmentService;
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
import com.imyeyu.api.modules.common.service.TempFileService;
|
import com.imyeyu.api.modules.common.service.TempFileService;
|
||||||
import com.imyeyu.api.modules.common.vo.attachment.AttachmentRequest;
|
|
||||||
import com.imyeyu.api.modules.journal.bean.Travel;
|
|
||||||
import com.imyeyu.api.modules.journal.entity.Journal;
|
import com.imyeyu.api.modules.journal.entity.Journal;
|
||||||
import com.imyeyu.api.modules.journal.mapper.JournalMapper;
|
import com.imyeyu.api.modules.journal.mapper.JournalMapper;
|
||||||
import com.imyeyu.api.modules.journal.service.JournalService;
|
import com.imyeyu.api.modules.journal.service.JournalService;
|
||||||
import com.imyeyu.api.modules.journal.vo.AppendRequest;
|
import com.imyeyu.api.modules.journal.vo.journal.ArchiveRequest;
|
||||||
import com.imyeyu.api.modules.journal.vo.ArchiveRequest;
|
import com.imyeyu.api.modules.journal.vo.journal.JournalRequest;
|
||||||
import com.imyeyu.api.modules.journal.vo.JournalPage;
|
import com.imyeyu.api.modules.journal.vo.journal.UpdateRequest;
|
||||||
import com.imyeyu.api.modules.journal.vo.JournalRequest;
|
|
||||||
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.spring.bean.Page;
|
|
||||||
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;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -33,10 +25,8 @@ import org.springframework.beans.BeanUtils;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -50,7 +40,7 @@ import java.util.stream.Collectors;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class JournalServiceImplement extends AbstractEntityService<Journal, Long> implements JournalService {
|
public class JournalServiceImplement extends AbstractEntityService<Journal, Long> implements JournalService {
|
||||||
|
|
||||||
private final Gson gson;
|
private final ObjectMapper jackson;
|
||||||
|
|
||||||
private final SettingService settingService;
|
private final SettingService settingService;
|
||||||
private final TempFileService tempFileService;
|
private final TempFileService tempFileService;
|
||||||
@@ -63,19 +53,6 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
|
|||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PageResult<Journal> page(JournalPage page) {
|
|
||||||
PageResult<Journal> result = new PageResult<>();
|
|
||||||
result.setTotal(mapper.countByType(page.getType()));
|
|
||||||
result.setList(mapper.listByType(page.getType(), page.getOffset(), page.getLimit()));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long[] listDate() throws TimiException {
|
|
||||||
return mapper.listDate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public void create(JournalRequest request) throws TimiException {
|
public void create(JournalRequest request) throws TimiException {
|
||||||
@@ -89,14 +66,13 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < tempFileIds.length; i++) {
|
for (int i = 0; i < tempFileIds.length; i++) {
|
||||||
TempFileMetaData metadata = tempFileService.metadata(tempFileIds[i]);
|
Attachment tempFileAttach = attachmentService.getByMongoId(tempFileIds[i]);
|
||||||
File file = tempFileService.get(tempFileIds[i]);
|
|
||||||
|
|
||||||
AttachmentRequest sourceAttach = new AttachmentRequest();
|
Attachment sourceAttach = new Attachment();
|
||||||
sourceAttach.setName(metadata.getOriginalName());
|
sourceAttach.setName(tempFileAttach.getName());
|
||||||
sourceAttach.setBizType(Attachment.BizType.JOURNAL);
|
sourceAttach.setBizType(Attachment.BizType.JOURNAL);
|
||||||
sourceAttach.setBizId(journal.getId());
|
sourceAttach.setBizId(journal.getId());
|
||||||
sourceAttach.setInputStream(IO.getInputStream(file));
|
sourceAttach.setInputStream(tempFileAttach.openInputStream());
|
||||||
attachmentService.createMedia(sourceAttach);
|
attachmentService.createMedia(sourceAttach);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -105,28 +81,47 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void update(UpdateRequest request) throws TimiException {
|
||||||
|
TimiException.required(request.getId(), "not found request.id");
|
||||||
|
Journal journal = get(request.getId());
|
||||||
|
BeanUtils.copyProperties(request, journal);
|
||||||
|
super.update(journal);
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
Set<Long> dbAttachSet = attachmentService.listByBizId(Attachment.BizType.JOURNAL, journal.getId(), MediaAttach.Type.THUMB)
|
||||||
|
.stream()
|
||||||
|
.map(Attachment::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Set<Long> retainIds = Set.of(TimiJava.firstNotEmpty(request.getAttachmentIds(), new Long[0]));
|
||||||
|
dbAttachSet.removeAll(retainIds);
|
||||||
|
for (Long removeId : dbAttachSet) {
|
||||||
|
attachmentService.deleteMedia(removeId);
|
||||||
|
}
|
||||||
|
// 新增
|
||||||
|
for (String tempFileId : TimiJava.defaultIfNull(request.getTempFileIds(), new String[0])) {
|
||||||
|
Attachment tempFileAttach = attachmentService.getByMongoId(tempFileId);
|
||||||
|
|
||||||
|
Attachment sourceAttach = new Attachment();
|
||||||
|
sourceAttach.setName(tempFileAttach.getName());
|
||||||
|
sourceAttach.setBizType(Attachment.BizType.JOURNAL);
|
||||||
|
sourceAttach.setBizId(journal.getId());
|
||||||
|
sourceAttach.setInputStream(tempFileAttach.openInputStream());
|
||||||
|
attachmentService.createMedia(sourceAttach);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void delete(Long id) {
|
||||||
|
super.delete(id);
|
||||||
|
attachmentService.deleteByBizId(Attachment.BizType.JOURNAL, id);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void appendItems(AppendRequest request) throws TimiException {
|
public List<Journal> listByIds(Long... ids) {
|
||||||
TimiException.required(request.getTempFileIds(), "not found or empty tempFileIds");
|
return mapper.listByIds(ids);
|
||||||
try {
|
|
||||||
Journal journal = get(request.getId());
|
|
||||||
|
|
||||||
String[] tempFileIds = request.getTempFileIds();
|
|
||||||
for (int i = 0; i < tempFileIds.length; i++) {
|
|
||||||
TempFileMetaData metadata = tempFileService.metadata(tempFileIds[i]);
|
|
||||||
File file = tempFileService.get(tempFileIds[i]);
|
|
||||||
|
|
||||||
AttachmentRequest sourceAttach = new AttachmentRequest();
|
|
||||||
sourceAttach.setName(metadata.getOriginalName());
|
|
||||||
sourceAttach.setBizType(Attachment.BizType.JOURNAL);
|
|
||||||
sourceAttach.setBizId(journal.getId());
|
|
||||||
sourceAttach.setInputStream(IO.getInputStream(file));
|
|
||||||
attachmentService.createMedia(sourceAttach);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("create journal error", e);
|
|
||||||
throw new TimiException(TimiCode.ERROR).msgKey("TODO create journal error");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -149,16 +144,15 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
|
|||||||
try {
|
try {
|
||||||
List<Attachment> thumbResult = new ArrayList<>();
|
List<Attachment> thumbResult = new ArrayList<>();
|
||||||
for (int i = 0; i < tempFileIds.length; i++) {
|
for (int i = 0; i < tempFileIds.length; i++) {
|
||||||
TempFileMetaData metadata = tempFileService.metadata(tempFileIds[i]);
|
Attachment tempFileAttach = attachmentService.getByMongoId(tempFileIds[i]);
|
||||||
File file = tempFileService.get(tempFileIds[i]);
|
|
||||||
|
|
||||||
AttachmentRequest sourceAttach = new AttachmentRequest();
|
Attachment sourceAttach = new Attachment();
|
||||||
sourceAttach.setName(metadata.getOriginalName());
|
sourceAttach.setName(tempFileAttach.getName());
|
||||||
sourceAttach.setBizType(Attachment.BizType.JOURNAL_MOMENT);
|
sourceAttach.setBizType(Attachment.BizType.JOURNAL_MOMENT);
|
||||||
sourceAttach.setBizId(0L);
|
sourceAttach.setBizId(0L);
|
||||||
sourceAttach.setAttachTypeValue(MediaAttach.Type.SOURCE);
|
sourceAttach.setAttachTypeValue(MediaAttach.Type.SOURCE);
|
||||||
sourceAttach.setSize(file.length());
|
sourceAttach.setSize(tempFileAttach.getSize());
|
||||||
sourceAttach.setInputStream(IO.getInputStream(file));
|
sourceAttach.setInputStream(tempFileAttach.openInputStream());
|
||||||
thumbResult.add(attachmentService.createMedia(sourceAttach));
|
thumbResult.add(attachmentService.createMedia(sourceAttach));
|
||||||
}
|
}
|
||||||
return thumbResult;
|
return thumbResult;
|
||||||
@@ -168,15 +162,6 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Attachment> listMoment() throws TimiException {
|
|
||||||
Page page = new Page(0, Integer.MAX_VALUE);
|
|
||||||
LinkedHashMap<String, BaseMapper.OrderType> orderMap = new LinkedHashMap<>();
|
|
||||||
orderMap.put("created_at", BaseMapper.OrderType.DESC);
|
|
||||||
page.setOrderMap(orderMap);
|
|
||||||
return attachmentService.pageByBizId(Attachment.BizType.JOURNAL_MOMENT, 0L, List.of(MediaAttach.Type.THUMB), page).getList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteMoment(Long[] thumbIds) throws TimiException {
|
public void deleteMoment(Long[] thumbIds) throws TimiException {
|
||||||
for (int i = 0; i < thumbIds.length; i++) {
|
for (int i = 0; i < thumbIds.length; i++) {
|
||||||
@@ -187,10 +172,16 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
|
|||||||
@Override
|
@Override
|
||||||
public void archiveMoment(ArchiveRequest request) throws TimiException {
|
public void archiveMoment(ArchiveRequest request) throws TimiException {
|
||||||
try {
|
try {
|
||||||
Journal journal = new Journal();
|
Journal journal;
|
||||||
|
if (request.getId() == null) {
|
||||||
|
// 归档为新纪录
|
||||||
|
journal = new Journal();
|
||||||
BeanUtils.copyProperties(request, journal);
|
BeanUtils.copyProperties(request, journal);
|
||||||
super.create(journal);
|
super.create(journal);
|
||||||
|
} else {
|
||||||
|
// 归档到指定记录
|
||||||
|
journal = get(request.getId());
|
||||||
|
}
|
||||||
Long[] thumbIds = request.getThumbIds();
|
Long[] thumbIds = request.getThumbIds();
|
||||||
if (TimiJava.isEmpty(thumbIds)) {
|
if (TimiJava.isEmpty(thumbIds)) {
|
||||||
return;
|
return;
|
||||||
@@ -201,8 +192,8 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
|
|||||||
thumbAttach.setBizId(journal.getId());
|
thumbAttach.setBizId(journal.getId());
|
||||||
attachmentService.update(thumbAttach);
|
attachmentService.update(thumbAttach);
|
||||||
|
|
||||||
MediaAttach.ExtData extData = gson.fromJson(thumbAttach.getExt(), MediaAttach.ExtData.class);
|
Metadata.ThumbImage thumbMetadata = jackson.convertValue(thumbAttach.getMetadata(), Metadata.ThumbImage.class);
|
||||||
Attachment sourceAttach = attachmentService.get(extData.getSourceId());
|
Attachment sourceAttach = attachmentService.get(thumbMetadata.getSourceId());
|
||||||
sourceAttach.setBizType(Attachment.BizType.JOURNAL);
|
sourceAttach.setBizType(Attachment.BizType.JOURNAL);
|
||||||
sourceAttach.setBizId(journal.getId());
|
sourceAttach.setBizId(journal.getId());
|
||||||
attachmentService.update(sourceAttach);
|
attachmentService.update(sourceAttach);
|
||||||
@@ -212,19 +203,4 @@ public class JournalServiceImplement extends AbstractEntityService<Journal, Long
|
|||||||
throw new TimiException(TimiCode.ERROR).msgKey("TODO archive journal error");
|
throw new TimiException(TimiCode.ERROR).msgKey("TODO archive journal error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Travel getTravel() throws TimiException {
|
|
||||||
return gson.fromJson(settingService.getAsJsonObject(SettingKey.JOURNAL_TRAVEL), Travel.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateTravelLuggage(Travel.Luggage luggage) throws TimiException {
|
|
||||||
Travel dbTravel = getTravel();
|
|
||||||
dbTravel.setLuggage(luggage);
|
|
||||||
Setting setting = settingService.getByKey(SettingKey.JOURNAL_TRAVEL);
|
|
||||||
setting.setValue(gson.toJson(dbTravel));
|
|
||||||
settingService.update(setting);
|
|
||||||
settingService.clearCache(SettingKey.JOURNAL_TRAVEL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.common.bean.MediaAttach;
|
||||||
|
import com.imyeyu.api.modules.common.entity.Attachment;
|
||||||
|
import com.imyeyu.api.modules.common.service.AttachmentService;
|
||||||
|
import com.imyeyu.api.modules.journal.entity.TravelLocation;
|
||||||
|
import com.imyeyu.api.modules.journal.mapper.TravelLocationMapper;
|
||||||
|
import com.imyeyu.api.modules.journal.service.TravelLocationService;
|
||||||
|
import com.imyeyu.api.modules.journal.service.TravelService;
|
||||||
|
import com.imyeyu.java.TimiJava;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行地点服务实现
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:45
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
||||||
|
public class TravelLocationServiceImplement extends AbstractEntityService<TravelLocation, Long> implements TravelLocationService {
|
||||||
|
|
||||||
|
private final TravelService travelService;
|
||||||
|
private final AttachmentService attachmentService;
|
||||||
|
|
||||||
|
private final TravelLocationMapper mapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BaseMapper<TravelLocation, Long> mapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void create(TravelLocation travelLocation) {
|
||||||
|
super.create(travelLocation);
|
||||||
|
for (String tempFileId : travelLocation.getTempFileIds()) {
|
||||||
|
Attachment tempFileAttach = attachmentService.getByMongoId(tempFileId);
|
||||||
|
|
||||||
|
Attachment attach = new Attachment();
|
||||||
|
attach.setName(tempFileAttach.getName());
|
||||||
|
attach.setBizType(Attachment.BizType.JOURNAL_TRAVEL);
|
||||||
|
attach.setBizId(travelLocation.getId());
|
||||||
|
attach.setInputStream(tempFileAttach.openInputStream());
|
||||||
|
attachmentService.createMedia(attach);
|
||||||
|
}
|
||||||
|
// 更新操作时间以保证排序
|
||||||
|
travelService.update(travelService.get(travelLocation.getTravelId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void update(TravelLocation travelLocation) {
|
||||||
|
// 使用 update 以允许设置 score 和 importance 为 null
|
||||||
|
mapper.update(travelLocation);
|
||||||
|
// 删除
|
||||||
|
if (TimiJava.isNotEmpty(travelLocation.getAttachmentIds())) {
|
||||||
|
Set<Long> dbAttachSet = attachmentService.listByBizId(Attachment.BizType.JOURNAL_TRAVEL, travelLocation.getId(), MediaAttach.Type.THUMB)
|
||||||
|
.stream()
|
||||||
|
.map(Attachment::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Set<Long> retainIds = Set.of(travelLocation.getAttachmentIds());
|
||||||
|
dbAttachSet.removeAll(retainIds);
|
||||||
|
for (Long removeId : dbAttachSet) {
|
||||||
|
attachmentService.deleteMedia(removeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 新增
|
||||||
|
for (String tempFileId : TimiJava.defaultIfNull(travelLocation.getTempFileIds(), new String[0])) {
|
||||||
|
Attachment tempFileAttach = attachmentService.getByMongoId(tempFileId);
|
||||||
|
|
||||||
|
Attachment attach = new Attachment();
|
||||||
|
attach.setName(tempFileAttach.getName());
|
||||||
|
attach.setBizType(Attachment.BizType.JOURNAL_TRAVEL);
|
||||||
|
attach.setBizId(travelLocation.getId());
|
||||||
|
attach.setInputStream(tempFileAttach.openInputStream());
|
||||||
|
attachmentService.createMedia(attach);
|
||||||
|
}
|
||||||
|
// 更新操作时间以保证排序
|
||||||
|
travelService.update(travelService.get(travelLocation.getTravelId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void delete(Long id) {
|
||||||
|
super.delete(id);
|
||||||
|
attachmentService.deleteByBizId(Attachment.BizType.JOURNAL_TRAVEL, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TravelLocation> listByIds(Long... ids) {
|
||||||
|
return mapper.listByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void deleteByTravelId(Long travelId) throws TimiException {
|
||||||
|
TravelLocation example = new TravelLocation();
|
||||||
|
example.setTravelId(travelId);
|
||||||
|
List<TravelLocation> locationList = mapper.selectAllByExample(example);
|
||||||
|
for (TravelLocation location : locationList) {
|
||||||
|
delete(location.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.imyeyu.api.modules.journal.service.implement;
|
||||||
|
|
||||||
|
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
|
||||||
|
import com.imyeyu.api.modules.journal.entity.Travel;
|
||||||
|
import com.imyeyu.api.modules.journal.mapper.TravelMapper;
|
||||||
|
import com.imyeyu.api.modules.journal.service.TravelLocationService;
|
||||||
|
import com.imyeyu.api.modules.journal.service.TravelService;
|
||||||
|
import com.imyeyu.spring.mapper.BaseMapper;
|
||||||
|
import com.imyeyu.spring.service.AbstractEntityService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旅行计划服务实现
|
||||||
|
*
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2025-12-12 14:45
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
||||||
|
public class TravelServiceImplement extends AbstractEntityService<Travel, Long> implements TravelService {
|
||||||
|
|
||||||
|
private final TravelMapper mapper;
|
||||||
|
private final TravelLocationService travelLocationService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BaseMapper<Travel, Long> mapper() {
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void update(Travel travel) {
|
||||||
|
// 使用 update 以允许设置 travelAt 和 days 为 null
|
||||||
|
mapper.update(travel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public void delete(Long id) {
|
||||||
|
super.delete(id);
|
||||||
|
travelLocationService.deleteByTravelId(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.imyeyu.api.modules.journal.util;
|
|||||||
|
|
||||||
import com.imyeyu.api.modules.common.bean.SettingKey;
|
import com.imyeyu.api.modules.common.bean.SettingKey;
|
||||||
import com.imyeyu.api.modules.common.service.SettingService;
|
import com.imyeyu.api.modules.common.service.SettingService;
|
||||||
|
import com.imyeyu.api.modules.journal.bean.RequiredUploadPermission;
|
||||||
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;
|
||||||
@@ -13,8 +14,11 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.lang.NonNull;
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2023-11-23 17:09
|
* @version 2023-11-23 17:09
|
||||||
@@ -28,20 +32,34 @@ public class JournalAPIInterceptor implements HandlerInterceptor {
|
|||||||
|
|
||||||
private final SettingService settingService;
|
private final SettingService settingService;
|
||||||
|
|
||||||
private String[] keys;
|
private Set<String> keys, openIds;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void postConstruct() {
|
private void postConstruct() {
|
||||||
keys = settingService.getAsString(SettingKey.JOURNAL_KEY).split(",");
|
keys = Set.of(settingService.getAsString(SettingKey.JOURNAL_KEY).split(","));
|
||||||
|
openIds = Set.of(settingService.getAsString(SettingKey.JOURNAL_OPEN_ID_WHITE_LIST).split(","));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean preHandle(@NonNull HttpServletRequest req, @NonNull HttpServletResponse resp, @NonNull Object handler) {
|
public boolean preHandle(@NonNull HttpServletRequest req, @NonNull HttpServletResponse resp, @NonNull Object handler) {
|
||||||
String key = TimiJava.firstNotEmpty(TimiSpring.getHeader("Key"), TimiSpring.getRequestArg("key"));
|
boolean requiredUploadPermission = false;
|
||||||
for (int i = 0; i < keys.length; i++) {
|
if (handler instanceof HandlerMethod handlerMethod) {
|
||||||
if (keys[i].equals(key)) {
|
requiredUploadPermission = handlerMethod.getMethodAnnotation(RequiredUploadPermission.class) != null;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
if (!canAccess()) {
|
||||||
|
throw new TimiException(TimiCode.PERMISSION_MISS).msgKey("invalid.key");
|
||||||
}
|
}
|
||||||
throw new TimiException(TimiCode.ARG_MISS).msgKey("invalid.key");
|
return !requiredUploadPermission || canUploadKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canAccess() {
|
||||||
|
String reqKey = TimiJava.defaultIfEmpty(TimiSpring.getHeader("Key"), TimiSpring.getRequestArg("key"));
|
||||||
|
return TimiJava.isNotEmpty(reqKey) && keys.contains(reqKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canUploadKey() {
|
||||||
|
String reqKey = TimiJava.defaultIfEmpty(TimiSpring.getHeader("Key"), TimiSpring.getRequestArg("key"));
|
||||||
|
// String reqOpenId = TimiJava.defaultIfEmpty(TimiSpring.getHeader("OpenId"), TimiSpring.getRequestArg("openid"));
|
||||||
|
// return canAccess() && reqKey.startsWith("i") && openIds.contains(reqOpenId);
|
||||||
|
return canAccess() && reqKey.startsWith("i");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user