diff --git a/backend/src/main/java/com/openisle/service/PostService.java b/backend/src/main/java/com/openisle/service/PostService.java index a13ee00ed..aaf453cb7 100644 --- a/backend/src/main/java/com/openisle/service/PostService.java +++ b/backend/src/main/java/com/openisle/service/PostService.java @@ -261,7 +261,7 @@ public class PostService { String proposalDescription ) { // 限制访问次数 - boolean limitResult = postRateLimit(username); + boolean limitResult = isPostLimitReached(username); if (!limitResult) { throw new RateLimitException("Too many posts"); } @@ -407,6 +407,7 @@ public class PostService { if (post.getStatus() == PostStatus.PUBLISHED) { searchIndexEventPublisher.publishPostSaved(post); } + markPostLimit(author.getUsername()); return post; } @@ -480,20 +481,23 @@ public class PostService { } /** - * 限制发帖频率 + * 检查用户是否达到发帖限制 * @param username - * @return + * @return true - 允许发帖,false - 已达限制 */ - private boolean postRateLimit(String username) { + private boolean isPostLimitReached(String username) { String key = CachingConfig.LIMIT_CACHE_NAME + ":posts:" + username; String result = (String) redisTemplate.opsForValue().get(key); - //最近没有创建过文章 - if (StringUtils.isEmpty(result)) { - // 限制频率为5分钟 - redisTemplate.opsForValue().set(key, "1", Duration.ofMinutes(5)); - return true; - } - return false; + return StringUtils.isEmpty(result); + } + + /** + * 标记用户发帖,触发limit计时 + * @param username + */ + private void markPostLimit(String username) { + String key = CachingConfig.LIMIT_CACHE_NAME + ":posts:" + username; + redisTemplate.opsForValue().set(key, "1", Duration.ofMinutes(5)); } @CacheEvict(value = CachingConfig.POST_CACHE_NAME, allEntries = true) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 56afacd53..6fd60458a 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -25,6 +25,10 @@ services: timeout: 3s retries: 30 start_period: 20s + profiles: + - dev + - dev_local_backend + - prod # OpenSearch Service opensearch: @@ -61,6 +65,9 @@ services: start_period: 60s networks: - openisle-network + profiles: + - dev + - dev_local_backend dashboards: image: opensearchproject/opensearch-dashboards:3.0.0 @@ -75,6 +82,10 @@ services: restart: unless-stopped networks: - openisle-network + profiles: + - dev + - dev_local_backend + - prod rabbitmq: image: rabbitmq:3.13-management @@ -98,6 +109,10 @@ services: start_period: 30s networks: - openisle-network + profiles: + - dev + - dev_local_backend + - prod redis: image: redis:7 @@ -111,6 +126,10 @@ services: - redis-data:/data networks: - openisle-network + profiles: + - dev + - dev_local_backend + - prod # Java spring boot service (开发便捷镜像,后续可换成打包镜像) springboot: @@ -155,6 +174,9 @@ services: start_period: 60s networks: - openisle-network + profiles: + - dev + - prod websocket-service: image: maven:3.9-eclipse-temurin-17 @@ -186,6 +208,10 @@ services: start_period: 60s networks: - openisle-network + profiles: + - dev + - dev_local_backend + - prod frontend_dev: image: node:20 @@ -208,6 +234,28 @@ services: - openisle-network profiles: - dev + + frontend_dev_local_backend: + image: node:20 + container_name: ${COMPOSE_PROJECT_NAME}-openisle-frontend-dev-local-backend + working_dir: /app + env_file: + - ${ENV_FILE:-../.env} + command: sh -c "npm install && npm run dev" + volumes: + - ../frontend_nuxt:/app + - frontend-node-modules:/app/node_modules + ports: + - "${FRONTEND_PORT:-3000}:3000" + depends_on: + websocket-service: + condition: service_healthy + networks: + - openisle-network + profiles: + - dev_local_backend + extra_hosts: + - "host.docker.internal:host-gateway" frontend_service: build: @@ -226,13 +274,13 @@ services: websocket-service: condition: service_healthy restart: unless-stopped - profiles: ["staging", "prod"] - + profiles: + - prod + # 监听“frontend_dev 容器自身的” 127.0.0.1:8080 → 转发到 springboot:8080 loopback_8080: image: alpine/socat container_name: ${COMPOSE_PROJECT_NAME}-loopback-8080 - # 监听“frontend_dev 容器自身的” 127.0.0.1:8080 → 转发到 springboot:8080 command: - -d - -d @@ -243,13 +291,37 @@ services: springboot: condition: service_healthy network_mode: "service:frontend_dev" - profiles: ["dev"] healthcheck: test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 8080"] interval: 5s timeout: 3s retries: 20 start_period: 10s + profiles: + - dev + + # 监听“frontend_dev 容器自身的” 127.0.0.1:8080 → 转发到 启动docker的本机:8080 + loopback_8080_host: + image: alpine/socat + container_name: ${COMPOSE_PROJECT_NAME}-loopback-8080-host + command: + - -d + - -d + - -ly + - TCP4-LISTEN:8080,bind=127.0.0.1,reuseaddr,fork + - TCP4:host.docker.internal:8080 + network_mode: "service:frontend_dev_local_backend" + depends_on: + frontend_dev_local_backend: + condition: service_started + healthcheck: + test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 8080"] + interval: 5s + timeout: 3s + retries: 20 + start_period: 10s + profiles: + - dev_local_backend loopback_8082: image: alpine/socat @@ -265,13 +337,37 @@ services: websocket-service: condition: service_healthy network_mode: "service:frontend_dev" - profiles: ["dev"] healthcheck: test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 8082"] interval: 5s timeout: 3s retries: 20 start_period: 10s + profiles: + - dev + + loopback_8082_host: + image: alpine/socat + container_name: ${COMPOSE_PROJECT_NAME}-loopback-8082-host + # 监听 127.0.0.1:8082 → 转发到 websocket-service:8082(WS 纯 TCP 可直接过) + command: + - -d + - -d + - -ly + - TCP4-LISTEN:8082,bind=127.0.0.1,reuseaddr,fork + - TCP4:websocket-service:8082 + depends_on: + websocket-service: + condition: service_healthy + network_mode: "service:frontend_dev_local_backend" + healthcheck: + test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 8082"] + interval: 5s + timeout: 3s + retries: 20 + start_period: 10s + profiles: + - dev_local_backend networks: openisle-network: diff --git a/frontend_nuxt/components/ProposalForm.vue b/frontend_nuxt/components/ProposalForm.vue index 847ec1f68..261554069 100644 --- a/frontend_nuxt/components/ProposalForm.vue +++ b/frontend_nuxt/components/ProposalForm.vue @@ -5,10 +5,10 @@ 提案规则说明
-

提案规则1

-

提案规则

-

提案规则

-

提案规则

+

📛 拟议分类名称需保持唯一,请勿与现有分类或正在提案中的名称重复。

+

📝 请在下方详细说明提案目的、预期价值及补充材料,方便大家快速理解。

+

🗳️ 提案提交后将开放 3 天投票,需达到至少 60% 的赞成率并满 10 人参与方可通过。

+

🤝 讨论请遵循社区守则,保持礼貌和善,欢迎附上相关案例或参考链接。