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 人参与方可通过。
+🤝 讨论请遵循社区守则,保持礼貌和善,欢迎附上相关案例或参考链接。