From ba94ceae1315174798ae1967ef62268c6d16cd5b Mon Sep 17 00:00:00 2001
From: Codex Assistant <codex@example.com>
Date: 星期一, 06 十月 2025 22:07:06 +0800
Subject: [PATCH] feat: 评审与活动相关改动 - backend(GraphQL): Activity schema 增加 updateActivityState(id, state);实现 resolver/service 仅更新 state=2 作为逻辑删除 - backend(GraphQL): region.graphqls 新增 Query leafRegions - backend(GraphQL): player.graphqls 的 projectReviewApplications 增加可选参数 regionId - backend(Service): listProjectReviewApplications 绑定 regionId 参数,修复 QueryParameterException - frontend(web): 新增 api/activity.js 的 updateActivityState 并接入 activity-list 删除逻辑 - frontend(web): review-list.vue 权限仅校验登录,移除角色限制;查询参数修正为 name/regionId - frontend(web): 删除未引用的 ActivityList.vue - frontend(web): projectReviewNew.js GraphQL 查询增加 name 参数

---
 wx/pages/judge/review.js                                                                  |    2 
 wx/lib/utils.wxs                                                                          |   58 
 .roocode/spec.yaml                                                                        |  170 ++
 wx/pages/profile/employee-review-detail.wxss                                              |  104 +
 wx/pages/profile/employee-review-detail.wxml                                              |   50 
 web/src/api/activity.js                                                                   |   89 
 backend/src/main/resources/graphql/region.graphqls                                        |    3 
 web/src/utils/cos.ts                                                                      |  322 +-
 backend/src/main/java/com/rongyichuang/region/resolver/RegionResolver.java                |    8 
 backend/src/main/java/com/rongyichuang/dashboard/api/DashboardGraphqlApi.java             |   25 
 web/src/views/activity-list.vue                                                           |  130 +
 backend/logs/backend.out                                                                  |  576 ++++++
 web/src/api/projectReviewNew.js                                                           |    6 
 wx/pages/review/index.js                                                                  |    2 
 web/src/utils/appConfig.js                                                                |   48 
 backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java    |   27 
 web/package.json                                                                          |    1 
 wx/pages/profile/employee-review.wxml                                                     |   40 
 backend/src/main/resources/graphql/player.graphqls                                        |    2 
 wx/pages/profile/employee-review.wxss                                                     |   93 
 web/vite.config.ts                                                                        |    2 
 wx/pages/profile/employee-review-detail.js                                                |  118 +
 backend/src/main/java/com/rongyichuang/region/service/RegionService.java                  |    7 
 web/src/utils/cos-config.ts                                                               |  148 
 PasswordTest.java                                                                         |   34 
 web/src/utils/cos-simple.ts                                                               |  128 
 wx/pages/profile/employee-review.js                                                       |   28 
 backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java            |  130 +
 backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java              |   32 
 backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java                   |    3 
 backend/src/main/resources/graphql/dashboard.graphqls                                     |   32 
 web/src/views/review-list.vue                                                             |  458 ++---
 web/src/views/dashboard/index.vue                                                         |  324 +++
 backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java       |   16 
 /dev/null                                                                                 |  990 -----------
 backend/src/main/java/com/rongyichuang/activity/resolver/ActivityResolver.java            |    9 
 web/src/utils/upload-logo-browser.ts                                                      |  222 +-
 web/src/api/region.js                                                                     |   49 
 backend/logs/backend.err                                                                  |    0 
 backend/src/main/java/com/rongyichuang/dashboard/dto/response/RegistrationTrendPoint.java |   41 
 backend/src/main/java/com/rongyichuang/dashboard/dto/response/RegionRegistrationStat.java |   69 
 backend/src/main/resources/graphql/activity.graphqls                                      |  125 +
 wx/pages/project/detail.js                                                                |  248 --
 backend/src/main/java/com/rongyichuang/region/repository/RegionRepository.java            |    2 
 web/src/api/dashboard.js                                                                  |   43 
 45 files changed, 2,799 insertions(+), 2,215 deletions(-)

diff --git a/.roocode/spec.yaml b/.roocode/spec.yaml
new file mode 100644
index 0000000..0688822
--- /dev/null
+++ b/.roocode/spec.yaml
@@ -0,0 +1,170 @@
+# RooCode Spec v1
+version: 1
+project:
+  name: your-repo
+  shell:
+    type: powershell
+    flags: "-NoLogo -NoProfile -ExecutionPolicy Bypass"
+  os: windows
+  line_endings: crlf
+  encoding: utf-8
+
+objectives:
+  - "浠呬慨鏀逛笌褰撳墠浠诲姟鐩存帴鐩稿叧鐨勪唬鐮侊紱闈炵浉鍏虫枃浠朵繚鎸佷笉鍙樸��"
+  - "鍏叡妯″潡鎴栧叡浜唬鐮佷慨鏀瑰墠蹇呴』瀹屾垚褰卞搷璇勪及骞惰幏寰楁壒鍑嗐��"
+  - "闇�姹備笉娓呮櫚鎴栧瓨鍦ㄥ啿绐佹椂搴旂珛鍗虫殏鍋滀慨鏀癸紝鎻愬嚭闂骞剁瓑寰呯‘璁ゃ��"
+  - "浠g爜鍛藉悕蹇呴』涓庢暟鎹簱瀛楁涓�鑷达紝涓嶅緱闅忔剰閲嶅懡鍚嶆垨绉佽嚜鍒涢�犲瓧娈靛悕銆�"
+  - "閬靛畧缁熶竴浠g爜瑙勮寖涓庢牸寮忚姹傘��"
+
+# 鈥斺�� 鏂囦欢鑼冨洿鎺у埗 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+scope:
+  blocked_paths:            # 绂佹淇敼鐨勬枃浠舵垨鐩綍锛堟牴鎹」鐩疄闄呰ˉ鍏咃級
+
+  # 浠呭畾涔夆�滅姝慨鏀光�濊寖鍥达紝鍏朵粬鏂囦欢榛樿鍙慨鏀�
+  # 涓�鏃︽娴嬪埌淇敼杩欎簺璺緞锛屽皢寮哄埗涓鎵ц骞惰姹傝鏄庣悊鐢�
+
+# 鈥斺�� 璇㈤棶/鏆傚仠绛栫暐 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+ask_policy:
+  must_ask_when:
+    - "闇�姹傛弿杩颁笉瀹屾暣銆佺洰鏍囦笉鏄庣‘鎴栦笌褰撳墠浠诲姟鍐茬獊"
+    - "娑夊強鍏叡妯″潡銆佹帴鍙c�佹暟鎹簱缁撴瀯銆佽法鏈嶅姟渚濊禆"
+    - "淇敼鍐呭鍙兘寮曞彂澶ц寖鍥村奖鍝嶆垨鍏煎鎬ч棶棰�"
+    - "娴嬭瘯瑕嗙洊涓嶅叏鎴栧瓨鍦ㄦ綔鍦ㄥ洖褰掗闄�"
+  stop_and_wait_for_confirmation: true
+  question_template: |
+    **涓嶆竻妤氱偣/椋庨櫓锛�**
+    1. 鈥�
+    2. 鈥�
+    **寤鸿鏂规锛�**
+    - A: 鈥�
+    - B: 鈥�
+    **璇锋眰纭锛�**
+    璇风‘璁ゅ悗缁х画淇敼銆�
+
+# 鈥斺�� 鍏叡浠g爜鏀瑰姩涓庡奖鍝嶈瘎浼� 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+public_code:
+  globs:
+    - "common/**"
+    - "packages/shared/**"
+    - "libs/**"
+  require_impact_assessment: true
+  impact_assessment_file: ".roocode/impact_assessment.md"
+  impact_assessment_must_include:
+    - "鍙樻洿鐩殑涓庡姩鏈�"
+    - "鍙楀奖鍝嶆ā鍧椾笌鍏煎鎬ц瘎浼�"
+    - "鏁版嵁杩佺Щ鎴栧洖婊氭柟妗�"
+    - "娴嬭瘯楠岃瘉璁″垝"
+    - "椋庨櫓涓庣紦瑙f帾鏂�"
+
+# 鈥斺�� 鍛藉悕涓庢暟鎹簱涓�鑷存�� 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+naming_conventions:
+  db_schema_source_of_truth:
+    - "docs/db/schema.sql"
+    - "migrations/**"
+  rules:
+    - "瀛楁鍛藉悕浠ユ暟鎹簱瀹氫箟涓哄熀鍑嗐��"
+    - "濡傞渶 camelCase 鏄犲皠锛屽繀椤讳竴涓�瀵瑰簲涓旀敞鏄庛��"
+    - "鏂板瀛楁鍓嶅繀椤诲湪鏁版嵁搴撲腑瀹氫箟骞剁‘璁ら粯璁ゅ�笺��"
+    - "绂佹鍒犻櫎鎴栭噸鍛藉悕瀛楁鑰屾棤杩佺Щ鏂规銆�"-+
+
+# 鈥斺�� 鏂囨湰涓庢枃浠舵牸寮忚鑼冿紙鏂板閮ㄥ垎锛� 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+text_format:
+  encoding:
+    required: "UTF-8 (鏃� BOM)"
+    description: |
+      鎵�鏈変唬鐮佹枃浠跺繀椤讳娇鐢� **UTF-8 鏃� BOM** 缂栫爜銆�
+      涓嶅厑璁� UTF-16銆丟BK銆丄NSI銆佹垨 UTF-8 with BOM銆�
+      鑻� IDE 鎴� AI 宸ュ叿鑷姩鐢熸垚鏂囦欢锛屽簲寮哄埗杞崲涓� UTF-8 鏃� BOM銆�
+      AI 缂栬緫鍣ㄨ涓ユ牸閬靛畧姝ゆ牸寮忥紝涓嶅緱鑷姩鏇存敼鏂囦欢缂栫爜銆�
+  line_endings:
+    required: "CRLF (Windows)"
+    description: |
+      鎵�鏈夋枃鏈枃浠舵崲琛岀蹇呴』涓� CRLF (`\r\n`)銆�
+      绂佹娣风敤 LF 涓� CRLF锛涢伩鍏嶆彁浜ゆ椂瑙﹀彂澶ч潰绉� diff銆�
+      鑻ユ娴嬪埌娣风敤鎹㈣绗︼紝搴旇嚜鍔ㄤ慨澶嶄负 CRLF銆�
+  indentation:
+    style: "spaces"
+    size: 2
+    description: |
+      缁熶竴浣跨敤绌烘牸缂╄繘锛岀姝娇鐢� Tab銆�
+      YAML / JSON / JS / TS / Java / XML / HTML 绛夋枃浠跺潎淇濇寔 2 涓┖鏍肩缉杩涖��
+  trailing_spaces:
+    remove: true
+    description: "鎻愪氦鍓嶈嚜鍔ㄧЩ闄よ灏惧浣欑┖鏍笺��"
+  final_newline:
+    ensure: true
+    description: "姣忎釜鏂囦欢鏈熬蹇呴』淇濈暀涓�涓┖琛岋紙鍗曚釜鎹㈣绗︼級銆�"
+  ai_editing_guidelines:
+    - "AI 鐢熸垚鎴栦慨鏀规枃浠舵椂蹇呴』淇濈暀鍘熸湁缂栫爜銆佺缉杩涘拰鎹㈣椋庢牸銆�"
+    - "AI 涓嶅緱鑷姩杞崲涓� LF 鎴栨坊鍔� BOM銆�"
+    - "AI 涓嶅緱鍦ㄦ棤鍐呭鍙樺寲鏃堕噸鍐欐暣涓枃浠舵垨鏍煎紡鍖栦笉鐩稿叧鍖哄煙銆�"
+    - "鑻ユ枃浠舵牸寮忔湭鐭ワ紝搴斿厛璇诲彇骞舵娴嬬幇鏈夌紪鐮佷笌缂╄繘鍐嶄慨鏀广��"
+    - "绂佹鍑虹幇涔辩爜銆佸弻瀛楄妭绗﹀彿鎴栨贩鐢ㄥ叏瑙�/鍗婅鏍囩偣銆�"
+  comment_style:
+    preferred:
+      - "//"       # Java, JS, TS, Go
+      - "#"        # YAML, Python, Shell
+      - "<!-- -->" # XML, HTML
+
+# 鈥斺�� 浠g爜椋庢牸涓庢牎楠� 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+code_style:
+  formatters:
+    - "Java: Spotless / GoogleJavaFormat"
+    - "TS/JS: Prettier / ESLint"
+  rules_of_thumb:
+    - "涓ユ牸绫诲瀷妫�鏌�"
+    - "鍏叡鎺ュ彛蹇呴』甯︽敞閲�"
+    - "鏂板閫昏緫蹇呴』鏈夋祴璇曡鐩�"
+    - "鎻愪氦鍓嶅簲閫氳繃 lint 涓庢祴璇�"
+
+# 鈥斺�� 棰勬鏌ュ懡浠わ紙Windows PowerShell 鍏煎锛� 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+checks:
+  pre_commit:
+    - name: "Java Build & Test"
+      run: |
+        if (Test-Path ".\mvnw.cmd") { .\mvnw.cmd -q test } `
+        elseif (Test-Path ".\gradlew.bat") { .\gradlew.bat test } `
+        elseif (Get-Command mvn -ErrorAction SilentlyContinue) { mvn -q test } `
+        else { Write-Host "No Java build tool found." }
+
+    - name: "Node Lint & Test"
+      run: |
+        if (Test-Path ".\package.json") {
+          npm ci
+          npm run -s lint
+          npm test --silent
+        }
+
+# 鈥斺�� 鍗遍櫓鎿嶄綔鎷︽埅 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+dangerous_operations:
+  block_without_explicit_approval:
+    - "璺ㄦā鍧楅噸鍛藉悕鎴栭噸鏋�"
+    - "渚濊禆澶х増鏈崌绾�"
+    - "鏁版嵁搴撶粨鏋勫彉鏇达紙鏂板/鍒犻櫎/閲嶅懡鍚嶈〃鎴栧瓧娈碉級"
+    - "鍏叡 API 鎴� DTO 鐨勭牬鍧忔�т慨鏀�"
+  require_ask_policy: true
+
+# 鈥斺�� 鎻愪氦涓庤鏄庤鑼� 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+commit_policy:
+  single_purpose_commits: true
+  message_template: |
+    <type>(<scope>): <subject>
+
+    WHY:
+    - 鍙樻洿鐩殑 鈥�
+    WHAT:
+    - 淇敼鍐呭 鈥�
+    TEST:
+    - 娴嬭瘯璇存槑 鈥�
+    RISK:
+    - 娼滃湪椋庨櫓 鈥�
+  require:
+    - "褰卞搷璇勪及閾炬帴锛堝娑夊強鍏叡妯″潡锛�"
+    - "浠诲姟缂栧彿鎴栭渶姹� ID"
+    - "娴嬭瘯鎶ュ憡鎽樿"
+
+# 鈥斺�� 鏈�缁堜繚鎶ゆ帾鏂� 鈥斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺�斺��
+guardrails:
+  fail_on_blocked_path_change: true
+  fail_on_public_code_change_without_assessment: true
+  deny_unapproved_mass_changes: true
diff --git a/PasswordTest.java b/PasswordTest.java
index 87bd3d1..cccdc80 100644
--- a/PasswordTest.java
+++ b/PasswordTest.java
@@ -1,17 +1,17 @@
-锘縤mport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-
-public class PasswordTest {
-    public static void main(String[] args) {
-        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
-        String hashedPassword = "$2a$10$VBHNbQlhM1OnQ8QTLkEVSeXBfLAlD9AJqNjErsYC664SUzMZZxjp.";
-        String plainPassword = "123456";
-        
-        boolean matches = encoder.matches(plainPassword, hashedPassword);
-        System.out.println("Password matches: " + matches);
-        
-        // 涔熸祴璇曚竴涓嬫柊鐢熸垚鐨勫瘑鐮�
-        String newHash = encoder.encode(plainPassword);
-        System.out.println("New hash: " + newHash);
-        System.out.println("New hash matches: " + encoder.matches(plainPassword, newHash));
-    }
-}
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+public class PasswordTest {
+    public static void main(String[] args) {
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        String hashedPassword = "$2a$10$VBHNbQlhM1OnQ8QTLkEVSeXBfLAlD9AJqNjErsYC664SUzMZZxjp.";
+        String plainPassword = "123456";
+        
+        boolean matches = encoder.matches(plainPassword, hashedPassword);
+        System.out.println("Password matches: " + matches);
+        
+        // 涔熸祴璇曚竴涓嬫柊鐢熸垚鐨勫瘑鐮�
+        String newHash = encoder.encode(plainPassword);
+        System.out.println("New hash: " + newHash);
+        System.out.println("New hash matches: " + encoder.matches(plainPassword, newHash));
+    }
+}

diff --git a/backend/logs/backend.err b/backend/logs/backend.err
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/backend/logs/backend.err
diff --git a/backend/logs/backend.out b/backend/logs/backend.out
new file mode 100644
index 0000000..48186a0
--- /dev/null
+++ b/backend/logs/backend.out
@@ -0,0 +1,576 @@
+[INFO] Scanning for projects...
+[INFO] 
+[INFO] --------------------< com.rongyichuang:ryc-backend >--------------------
+[INFO] Building 蓉易创后端服务 1.0.0
+[INFO]   from pom.xml
+[INFO] --------------------------------[ jar ]---------------------------------
+[INFO] 
+[INFO] >>> spring-boot:3.2.0:run (default-cli) > test-compile @ ryc-backend >>>
+[WARNING] The artifact mysql:mysql-connector-java:jar:8.0.33 has been relocated to com.mysql:mysql-connector-j:jar:8.0.33: MySQL Connector/J artifacts moved to reverse-DNS compliant Maven 2+ coordinates.
+[INFO] 
+[INFO] --- resources:3.3.1:resources (default-resources) @ ryc-backend ---
+[INFO] Copying 1 resource from src\main\resources to target\classes
+[INFO] Copying 22 resources from src\main\resources to target\classes
+[INFO] 
+[INFO] --- compiler:3.11.0:compile (default-compile) @ ryc-backend ---
+[INFO] Nothing to compile - all classes are up to date
+[INFO] 
+[INFO] --- resources:3.3.1:testResources (default-testResources) @ ryc-backend ---
+[INFO] skip non existing resourceDirectory D:\code\new-ryc\backend\src\test\resources
+[INFO] 
+[INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ ryc-backend ---
+[INFO] Nothing to compile - all classes are up to date
+[INFO] 
+[INFO] <<< spring-boot:3.2.0:run (default-cli) < test-compile @ ryc-backend <<<
+[INFO] 
+[INFO] 
+[INFO] --- spring-boot:3.2.0:run (default-cli) @ ryc-backend ---
+[INFO] Attaching agents: []
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+
+  .   ____          _            __ _ _
+ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
+( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
+ \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
+  '  |____| .__|_| |_|_| |_\__, | / / / /
+ =========|_|==============|___/=/_/_/_/
+ :: Spring Boot ::                (v3.2.0)
+
+2025-10-06 09:09:26.653 [background-preinit] INFO  org.hibernate.validator.internal.util.Version - HV000001: Hibernate Validator 8.0.1.Final
+2025-10-06 09:09:26.679 [restartedMain] INFO  com.rongyichuang.RycBackendApplication - Starting RycBackendApplication using Java 21.0.8 with PID 91916 (D:\code\new-ryc\backend\target\classes started by Owen in D:\code\new-ryc\backend)
+2025-10-06 09:09:26.680 [restartedMain] DEBUG com.rongyichuang.RycBackendApplication - Running with Spring Boot v3.2.0, Spring v6.1.1
+2025-10-06 09:09:26.681 [restartedMain] INFO  com.rongyichuang.RycBackendApplication - No active profile set, falling back to 1 default profile: "default"
+2025-10-06 09:09:26.709 [restartedMain] INFO  o.s.b.d.env.DevToolsPropertyDefaultsPostProcessor - Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
+2025-10-06 09:09:26.709 [restartedMain] INFO  o.s.b.d.env.DevToolsPropertyDefaultsPostProcessor - For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
+2025-10-06 09:09:27.136 [restartedMain] INFO  o.s.d.r.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
+2025-10-06 09:09:27.226 [restartedMain] INFO  o.s.d.r.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 81 ms. Found 18 JPA repository interfaces.
+2025-10-06 09:09:27.633 [restartedMain] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat initialized with port 8080 (http)
+2025-10-06 09:09:27.637 [restartedMain] INFO  org.apache.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]
+2025-10-06 09:09:27.639 [restartedMain] INFO  org.apache.catalina.core.StandardService - Starting service [Tomcat]
+2025-10-06 09:09:27.639 [restartedMain] INFO  org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16]
+2025-10-06 09:09:27.664 [restartedMain] INFO  o.a.c.c.ContainerBase.[Tomcat].[localhost].[/api] - Initializing Spring embedded WebApplicationContext
+2025-10-06 09:09:27.664 [restartedMain] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 953 ms
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+2025-10-06 09:09:27.740 [restartedMain] INFO  org.hibernate.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
+2025-10-06 09:09:27.764 [restartedMain] INFO  org.hibernate.Version - HHH000412: Hibernate ORM core version 6.3.1.Final
+2025-10-06 09:09:27.780 [restartedMain] INFO  o.hibernate.cache.internal.RegionFactoryInitiator - HHH000026: Second-level cache disabled
+2025-10-06 09:09:27.875 [restartedMain] INFO  o.s.o.j.persistenceunit.SpringPersistenceUnitInfo - No LoadTimeWeaver setup: ignoring JPA class transformer
+2025-10-06 09:09:27.887 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
+2025-10-06 09:09:28.352 [restartedMain] INFO  com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@673acd5c
+2025-10-06 09:09:28.359 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
+2025-10-06 09:09:28.404 [restartedMain] WARN  org.hibernate.orm.deprecation - HHH90000025: MySQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
+2025-10-06 09:09:28.991 [restartedMain] INFO  o.h.e.t.jta.platform.internal.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
+2025-10-06 09:09:28.993 [restartedMain] INFO  o.s.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'
+2025-10-06 09:09:29.169 [restartedMain] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - Filter 'jwtAuthenticationFilter' configured for use
+2025-10-06 09:09:29.209 [restartedMain] INFO  o.s.data.jpa.repository.query.QueryEnhancerFactory - Hibernate is in classpath; If applicable, HQL parser will be used.
+2025-10-06 09:09:30.080 [restartedMain] WARN  o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
+2025-10-06 09:09:30.099 [restartedMain] DEBUG o.s.s.c.a.a.c.AuthenticationConfiguration$DefaultPasswordEncoderAuthenticationManagerBuilder - No authenticationProviders and no parentAuthenticationManager defined. Returning null.
+2025-10-06 09:09:30.337 [restartedMain] INFO  o.s.g.e.DefaultSchemaResourceGraphQlSourceBuilder - Loaded 16 resource(s) in the GraphQL schema.
+2025-10-06 09:09:30.475 [restartedMain] INFO  o.s.b.a.graphql.GraphQlAutoConfiguration - GraphQL schema inspection:
+	Unmapped fields: {Query=[getActivities, organizerStats, _, getBanners, getSubmissionDetail, checkReviewStatus, media, tag, tagsByName], EmployeeResponse=[state], ActivityPlayer=[stateName, activity], Mutation=[echo, _, saveReviewDraft, submitReview, saveRole, deleteRole, saveTag, deleteTag], Subscription=[messageAdded]}
+	Unmapped registrations: {Query.playerRegistration=PlayerGraphqlApi#playerRegistration[1 args], Query.leafRegions=RegionResolver#leafRegions[0 args], Query.competitionParticipants=PlayerGraphqlApi#competitionParticipants[3 args], Mutation.updateActivityRegistration=PlayerGraphqlApi#updateActivityRegistration[2 args]}
+	Skipped types: []
+2025-10-06 09:09:30.483 [restartedMain] INFO  o.s.b.a.g.servlet.GraphQlWebMvcAutoConfiguration - GraphQL endpoint HTTP POST /graphql
+2025-10-06 09:09:30.516 [restartedMain] INFO  o.s.security.web.DefaultSecurityFilterChain - Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@61863bb0, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@1bb47453, org.springframework.security.web.context.SecurityContextHolderFilter@7acc0642, org.springframework.security.web.header.HeaderWriterFilter@37c1a568, org.springframework.web.filter.CorsFilter@1163d449, org.springframework.security.web.authentication.logout.LogoutFilter@23e0f4ee, com.rongyichuang.auth.filter.JwtAuthenticationFilter@5450d7ba, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@3c06054d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@50645f61, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5d3fa7a9, org.springframework.security.web.session.SessionManagementFilter@ee92c20, org.springframework.security.web.access.ExceptionTranslationFilter@4c110f19, org.springframework.security.web.access.intercept.AuthorizationFilter@68924b58]
+2025-10-06 09:09:30.710 [restartedMain] INFO  o.s.b.d.autoconfigure.OptionalLiveReloadServer - LiveReload server is running on port 35729
+2025-10-06 09:09:30.720 [restartedMain] INFO  org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
+2025-10-06 09:09:30.738 [restartedMain] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port 8080 (http) with context path '/api'
+2025-10-06 09:09:30.742 [restartedMain] INFO  com.rongyichuang.RycBackendApplication - Started RycBackendApplication in 4.361 seconds (process running for 4.63)
+2025-10-06 09:09:30.804 [restartedMain] DEBUG org.hibernate.SQL - 
+    select
+        count(*) 
+    from
+        t_tag t1_0 
+    where
+        (
+            t1_0.state = 1
+        )
+Hibernate: 
+    select
+        count(*) 
+    from
+        t_tag t1_0 
+    where
+        (
+            t1_0.state = 1
+        )
+2025-10-06 09:29:59.790 [File Watcher] INFO  o.s.b.d.a.LocalDevToolsAutoConfiguration$RestartingClassPathChangeChangedEventListener - Restarting due to 218 class path changes (0 additions, 218 deletions, 0 modifications)
+2025-10-06 09:29:59.794 [Thread-6] INFO  org.apache.coyote.http11.Http11NioProtocol - Stopping ProtocolHandler ["http-nio-8080"]
+2025-10-06 09:29:59.806 [Thread-6] INFO  o.s.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
+2025-10-06 09:29:59.808 [Thread-6] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
+2025-10-06 09:29:59.811 [Thread-6] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+
+  .   ____          _            __ _ _
+ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
+( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
+ \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
+  '  |____| .__|_| |_|_| |_\__, | / / / /
+ =========|_|==============|___/=/_/_/_/
+ :: Spring Boot ::                (v3.2.0)
+
+2025-10-06T09:29:59.901+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] com.rongyichuang.RycBackendApplication   : Starting RycBackendApplication using Java 21.0.8 with PID 91916 (D:\code\new-ryc\backend\target\classes started by Owen in D:\code\new-ryc\backend)
+2025-10-06T09:29:59.902+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] com.rongyichuang.RycBackendApplication   : No active profile set, falling back to 1 default profile: "default"
+2025-10-06T09:30:00.007+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
+2025-10-06T09:30:00.012+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 4 ms. Found 0 JPA repository interfaces.
+2025-10-06T09:30:00.087+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
+2025-10-06T09:30:00.088+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
+2025-10-06T09:30:00.088+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.16]
+2025-10-06T09:30:00.103+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
+2025-10-06T09:30:00.104+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 200 ms
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+2025-10-06T09:30:00.113+08:00  WARN 91916 --- [ryc-backend] [  restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception with message: Failed to determine a suitable driver class
+2025-10-06T09:30:00.114+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
+2025-10-06T09:30:00.118+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] .s.b.a.l.ConditionEvaluationReportLogger : 
+
+Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
+2025-10-06T09:30:00.128+08:00 ERROR 91916 --- [ryc-backend] [  restartedMain] o.s.b.d.LoggingFailureAnalysisReporter   : 
+
+***************************
+APPLICATION FAILED TO START
+***************************
+
+Description:
+
+Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
+
+Reason: Failed to determine a suitable driver class
+
+
+Action:
+
+Consider the following:
+	If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
+	If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
+
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+
+  .   ____          _            __ _ _
+ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
+( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
+ \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
+  '  |____| .__|_| |_|_| |_\__, | / / / /
+ =========|_|==============|___/=/_/_/_/
+ :: Spring Boot ::                (v3.2.0)
+
+2025-10-06 09:30:02.477 [restartedMain] INFO  com.rongyichuang.RycBackendApplication - Starting RycBackendApplication using Java 21.0.8 with PID 91916 (D:\code\new-ryc\backend\target\classes started by Owen in D:\code\new-ryc\backend)
+2025-10-06 09:30:02.477 [restartedMain] DEBUG com.rongyichuang.RycBackendApplication - Running with Spring Boot v3.2.0, Spring v6.1.1
+2025-10-06 09:30:02.478 [restartedMain] INFO  com.rongyichuang.RycBackendApplication - No active profile set, falling back to 1 default profile: "default"
+2025-10-06 09:30:02.657 [restartedMain] INFO  o.s.d.r.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
+2025-10-06 09:30:02.682 [restartedMain] INFO  o.s.d.r.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 24 ms. Found 18 JPA repository interfaces.
+2025-10-06 09:30:02.768 [restartedMain] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat initialized with port 8080 (http)
+2025-10-06 09:30:02.769 [restartedMain] INFO  org.apache.catalina.core.StandardService - Starting service [Tomcat]
+2025-10-06 09:30:02.770 [restartedMain] INFO  org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16]
+2025-10-06 09:30:02.784 [restartedMain] INFO  o.a.c.c.C.[Tomcat-1].[localhost].[/api] - Initializing Spring embedded WebApplicationContext
+2025-10-06 09:30:02.784 [restartedMain] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 304 ms
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+2025-10-06 09:30:02.815 [restartedMain] INFO  org.hibernate.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
+2025-10-06 09:30:02.817 [restartedMain] INFO  o.hibernate.cache.internal.RegionFactoryInitiator - HHH000026: Second-level cache disabled
+2025-10-06 09:30:02.820 [restartedMain] INFO  o.s.o.j.persistenceunit.SpringPersistenceUnitInfo - No LoadTimeWeaver setup: ignoring JPA class transformer
+2025-10-06 09:30:02.820 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Starting...
+2025-10-06 09:30:03.204 [restartedMain] INFO  com.zaxxer.hikari.pool.HikariPool - HikariPool-2 - Added connection com.mysql.cj.jdbc.ConnectionImpl@1247b78b
+2025-10-06 09:30:03.209 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Start completed.
+2025-10-06 09:30:03.216 [restartedMain] WARN  org.hibernate.orm.deprecation - HHH90000025: MySQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
+2025-10-06 09:30:03.360 [restartedMain] INFO  o.h.e.t.jta.platform.internal.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
+2025-10-06 09:30:03.361 [restartedMain] INFO  o.s.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'
+2025-10-06 09:30:03.407 [restartedMain] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - Filter 'jwtAuthenticationFilter' configured for use
+2025-10-06 09:30:03.901 [restartedMain] WARN  o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
+2025-10-06 09:30:03.914 [restartedMain] DEBUG o.s.s.c.a.a.c.AuthenticationConfiguration$DefaultPasswordEncoderAuthenticationManagerBuilder - No authenticationProviders and no parentAuthenticationManager defined. Returning null.
+2025-10-06 09:30:04.014 [restartedMain] INFO  o.s.g.e.DefaultSchemaResourceGraphQlSourceBuilder - Loaded 16 resource(s) in the GraphQL schema.
+2025-10-06 09:30:04.080 [restartedMain] INFO  o.s.b.a.graphql.GraphQlAutoConfiguration - GraphQL schema inspection:
+	Unmapped fields: {Query=[getActivities, organizerStats, _, getBanners, getSubmissionDetail, checkReviewStatus, media, tag, tagsByName], EmployeeResponse=[state], ActivityPlayer=[stateName, activity], Mutation=[echo, _, saveReviewDraft, submitReview, saveRole, deleteRole, saveTag, deleteTag], Subscription=[messageAdded]}
+	Unmapped registrations: {Query.playerRegistration=PlayerGraphqlApi#playerRegistration[1 args], Query.leafRegions=RegionResolver#leafRegions[0 args], Query.competitionParticipants=PlayerGraphqlApi#competitionParticipants[3 args], Mutation.updateActivityRegistration=PlayerGraphqlApi#updateActivityRegistration[2 args]}
+	Skipped types: []
+2025-10-06 09:30:04.084 [restartedMain] INFO  o.s.b.a.g.servlet.GraphQlWebMvcAutoConfiguration - GraphQL endpoint HTTP POST /graphql
+2025-10-06 09:30:04.097 [restartedMain] INFO  o.s.security.web.DefaultSecurityFilterChain - Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@f56ebd4, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@47c21e24, org.springframework.security.web.context.SecurityContextHolderFilter@7a9effb5, org.springframework.security.web.header.HeaderWriterFilter@1733faa0, org.springframework.web.filter.CorsFilter@698ef73e, org.springframework.security.web.authentication.logout.LogoutFilter@6402dfc6, com.rongyichuang.auth.filter.JwtAuthenticationFilter@7c09463e, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@457b0984, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4d150d71, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@53d6cf0e, org.springframework.security.web.session.SessionManagementFilter@52fd62ed, org.springframework.security.web.access.ExceptionTranslationFilter@1a04abc1, org.springframework.security.web.access.intercept.AuthorizationFilter@7d58e756]
+2025-10-06 09:30:04.173 [restartedMain] INFO  o.s.b.d.autoconfigure.OptionalLiveReloadServer - LiveReload server is running on port 35729
+2025-10-06 09:30:04.185 [restartedMain] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port 8080 (http) with context path '/api'
+2025-10-06 09:30:04.186 [restartedMain] INFO  com.rongyichuang.RycBackendApplication - Started RycBackendApplication in 1.766 seconds (process running for 1238.074)
+2025-10-06 09:30:04.214 [restartedMain] DEBUG org.hibernate.SQL - 
+    select
+        count(*) 
+    from
+        t_tag t1_0 
+    where
+        (
+            t1_0.state = 1
+        )
+Hibernate: 
+    select
+        count(*) 
+    from
+        t_tag t1_0 
+    where
+        (
+            t1_0.state = 1
+        )
+2025-10-06 09:30:04.247 [restartedMain] INFO  o.s.b.d.a.ConditionEvaluationDeltaLoggingListener - Condition evaluation unchanged
+2025-10-06 10:01:16.107 [HikariPool-2 housekeeper] WARN  com.zaxxer.hikari.pool.HikariPool - HikariPool-2 - Thread starvation or clock leap detected (housekeeper delta=14m42s537ms514碌s800ns).
+2025-10-06 10:13:21.820 [File Watcher] INFO  o.s.b.d.a.LocalDevToolsAutoConfiguration$RestartingClassPathChangeChangedEventListener - Restarting due to 218 class path changes (0 additions, 218 deletions, 0 modifications)
+2025-10-06 10:13:21.831 [Thread-8] INFO  o.s.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
+2025-10-06 10:13:21.832 [Thread-8] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown initiated...
+2025-10-06 10:13:21.834 [Thread-8] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-2 - Shutdown completed.
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+
+  .   ____          _            __ _ _
+ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
+( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
+ \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
+  '  |____| .__|_| |_|_| |_\__, | / / / /
+ =========|_|==============|___/=/_/_/_/
+ :: Spring Boot ::                (v3.2.0)
+
+2025-10-06T10:13:21.932+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] com.rongyichuang.RycBackendApplication   : Starting RycBackendApplication using Java 21.0.8 with PID 91916 (D:\code\new-ryc\backend\target\classes started by Owen in D:\code\new-ryc\backend)
+2025-10-06T10:13:21.932+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] com.rongyichuang.RycBackendApplication   : No active profile set, falling back to 1 default profile: "default"
+2025-10-06T10:13:22.155+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
+2025-10-06T10:13:22.209+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 53 ms. Found 12 JPA repository interfaces.
+2025-10-06T10:13:22.397+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
+2025-10-06T10:13:22.398+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
+2025-10-06T10:13:22.399+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.16]
+2025-10-06T10:13:22.497+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.a.c.c.C.[Tomcat-1].[localhost].[/]     : Initializing Spring embedded WebApplicationContext
+2025-10-06T10:13:22.497+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 562 ms
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+2025-10-06T10:13:22.505+08:00 ERROR 91916 --- [ryc-backend] [  restartedMain] o.s.b.web.embedded.tomcat.TomcatStarter  : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'jwtAuthenticationFilter': Unsatisfied dependency expressed through field 'jwtUtil': Error creating bean with name 'jwtUtil': Injection of autowired dependencies failed
+2025-10-06T10:13:22.506+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
+2025-10-06T10:13:22.507+08:00  WARN 91916 --- [ryc-backend] [  restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server
+2025-10-06T10:13:22.509+08:00  INFO 91916 --- [ryc-backend] [  restartedMain] .s.b.a.l.ConditionEvaluationReportLogger : 
+
+Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
+2025-10-06T10:13:22.512+08:00 ERROR 91916 --- [ryc-backend] [  restartedMain] o.s.boot.SpringApplication               : Application run failed
+
+org.springframework.context.ApplicationContextException: Unable to start web server
+	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:165) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:610) ~[spring-context-6.1.1.jar:6.1.1]
+	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) ~[spring-boot-3.2.0.jar:3.2.0]
+	at com.rongyichuang.RycBackendApplication.main(RycBackendApplication.java:15) ~[classes/:na]
+	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
+	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
+	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:50) ~[spring-boot-devtools-3.2.0.jar:3.2.0]
+Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
+	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:142) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:501) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:218) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:188) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162) ~[spring-boot-3.2.0.jar:3.2.0]
+	... 11 common frames omitted
+Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jwtAuthenticationFilter': Unsatisfied dependency expressed through field 'jwtUtil': Error creating bean with name 'jwtUtil': Injection of autowired dependencies failed
+	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:772) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:752) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:210) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:173) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:168) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAdaptableBeans(ServletContextInitializerBeans.java:153) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:86) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:266) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:240) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:52) ~[spring-boot-3.2.0.jar:3.2.0]
+	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:4850) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1332) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1322) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
+	at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145) ~[na:na]
+	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:866) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:845) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1332) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1322) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
+	at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:145) ~[na:na]
+	at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:866) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:240) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.core.StandardService.startInternal(StandardService.java:433) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:917) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:171) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.apache.catalina.startup.Tomcat.start(Tomcat.java:488) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
+	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123) ~[spring-boot-3.2.0.jar:3.2.0]
+	... 16 common frames omitted
+Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jwtUtil': Injection of autowired dependencies failed
+	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:499) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1420) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1441) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:769) ~[spring-beans-6.1.1.jar:6.1.1]
+	... 58 common frames omitted
+Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'app.jwt.secret' in value "${app.jwt.secret}"
+	at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-6.1.1.jar:6.1.1]
+	at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-6.1.1.jar:6.1.1]
+	at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239) ~[spring-core-6.1.1.jar:6.1.1]
+	at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210) ~[spring-core-6.1.1.jar:6.1.1]
+	at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:200) ~[spring-context-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:921) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1372) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1348) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:769) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:752) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:145) ~[spring-beans-6.1.1.jar:6.1.1]
+	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:493) ~[spring-beans-6.1.1.jar:6.1.1]
+	... 69 common frames omitted
+
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+
+  .   ____          _            __ _ _
+ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
+( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
+ \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
+  '  |____| .__|_| |_|_| |_\__, | / / / /
+ =========|_|==============|___/=/_/_/_/
+ :: Spring Boot ::                (v3.2.0)
+
+2025-10-06 10:13:23.996 [restartedMain] INFO  com.rongyichuang.RycBackendApplication - Starting RycBackendApplication using Java 21.0.8 with PID 91916 (D:\code\new-ryc\backend\target\classes started by Owen in D:\code\new-ryc\backend)
+2025-10-06 10:13:23.996 [restartedMain] DEBUG com.rongyichuang.RycBackendApplication - Running with Spring Boot v3.2.0, Spring v6.1.1
+2025-10-06 10:13:23.997 [restartedMain] INFO  com.rongyichuang.RycBackendApplication - No active profile set, falling back to 1 default profile: "default"
+2025-10-06 10:13:24.123 [restartedMain] INFO  o.s.d.r.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
+2025-10-06 10:13:24.149 [restartedMain] INFO  o.s.d.r.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 25 ms. Found 18 JPA repository interfaces.
+2025-10-06 10:13:24.238 [restartedMain] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat initialized with port 8080 (http)
+2025-10-06 10:13:24.238 [restartedMain] INFO  org.apache.catalina.core.StandardService - Starting service [Tomcat]
+2025-10-06 10:13:24.239 [restartedMain] INFO  org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16]
+2025-10-06 10:13:24.253 [restartedMain] INFO  o.a.c.c.C.[Tomcat-2].[localhost].[/api] - Initializing Spring embedded WebApplicationContext
+2025-10-06 10:13:24.253 [restartedMain] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 254 ms
+Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts
+2025-10-06 10:13:24.286 [restartedMain] INFO  org.hibernate.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
+2025-10-06 10:13:24.287 [restartedMain] INFO  o.hibernate.cache.internal.RegionFactoryInitiator - HHH000026: Second-level cache disabled
+2025-10-06 10:13:24.290 [restartedMain] INFO  o.s.o.j.persistenceunit.SpringPersistenceUnitInfo - No LoadTimeWeaver setup: ignoring JPA class transformer
+2025-10-06 10:13:24.292 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Starting...
+2025-10-06 10:13:24.710 [restartedMain] INFO  com.zaxxer.hikari.pool.HikariPool - HikariPool-3 - Added connection com.mysql.cj.jdbc.ConnectionImpl@36e5b2f
+2025-10-06 10:13:24.715 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-3 - Start completed.
+2025-10-06 10:13:24.736 [restartedMain] WARN  org.hibernate.orm.deprecation - HHH90000025: MySQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
+2025-10-06 10:13:25.014 [restartedMain] INFO  o.h.e.t.jta.platform.internal.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
+2025-10-06 10:13:25.019 [restartedMain] INFO  o.s.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'
+2025-10-06 10:13:25.085 [restartedMain] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - Filter 'jwtAuthenticationFilter' configured for use
+2025-10-06 10:13:25.724 [restartedMain] WARN  o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
+2025-10-06 10:13:25.736 [restartedMain] DEBUG o.s.s.c.a.a.c.AuthenticationConfiguration$DefaultPasswordEncoderAuthenticationManagerBuilder - No authenticationProviders and no parentAuthenticationManager defined. Returning null.
+2025-10-06 10:13:25.830 [restartedMain] INFO  o.s.g.e.DefaultSchemaResourceGraphQlSourceBuilder - Loaded 16 resource(s) in the GraphQL schema.
+2025-10-06 10:13:25.892 [restartedMain] INFO  o.s.b.a.graphql.GraphQlAutoConfiguration - GraphQL schema inspection:
+	Unmapped fields: {Query=[getActivities, organizerStats, _, getBanners, getSubmissionDetail, checkReviewStatus, media, tag, tagsByName], EmployeeResponse=[state], ActivityPlayer=[stateName, activity], Mutation=[echo, _, saveReviewDraft, submitReview, saveRole, deleteRole, saveTag, deleteTag], Subscription=[messageAdded]}
+	Unmapped registrations: {Query.playerRegistration=PlayerGraphqlApi#playerRegistration[1 args], Query.leafRegions=RegionResolver#leafRegions[0 args], Query.competitionParticipants=PlayerGraphqlApi#competitionParticipants[3 args], Mutation.updateActivityRegistration=PlayerGraphqlApi#updateActivityRegistration[2 args]}
+	Skipped types: []
+2025-10-06 10:13:25.895 [restartedMain] INFO  o.s.b.a.g.servlet.GraphQlWebMvcAutoConfiguration - GraphQL endpoint HTTP POST /graphql
+2025-10-06 10:13:25.911 [restartedMain] INFO  o.s.security.web.DefaultSecurityFilterChain - Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@dea1ef4, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@5c63d1f5, org.springframework.security.web.context.SecurityContextHolderFilter@5b0c1d4f, org.springframework.security.web.header.HeaderWriterFilter@3f68b717, org.springframework.web.filter.CorsFilter@37aaf5a0, org.springframework.security.web.authentication.logout.LogoutFilter@a59708e, com.rongyichuang.auth.filter.JwtAuthenticationFilter@2f7cfe5d, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5d7c0a4e, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@2756cef9, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4df1b85b, org.springframework.security.web.session.SessionManagementFilter@61e94078, org.springframework.security.web.access.ExceptionTranslationFilter@1322ba8a, org.springframework.security.web.access.intercept.AuthorizationFilter@4cf6cae4]
+2025-10-06 10:13:25.988 [restartedMain] INFO  o.s.b.d.autoconfigure.OptionalLiveReloadServer - LiveReload server is running on port 35729
+2025-10-06 10:13:25.997 [restartedMain] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port 8080 (http) with context path '/api'
+2025-10-06 10:13:25.998 [restartedMain] INFO  com.rongyichuang.RycBackendApplication - Started RycBackendApplication in 2.04 seconds (process running for 3839.887)
+2025-10-06 10:13:26.115 [restartedMain] DEBUG org.hibernate.SQL - 
+    select
+        count(*) 
+    from
+        t_tag t1_0 
+    where
+        (
+            t1_0.state = 1
+        )
+Hibernate: 
+    select
+        count(*) 
+    from
+        t_tag t1_0 
+    where
+        (
+            t1_0.state = 1
+        )
+2025-10-06 10:13:26.160 [restartedMain] INFO  o.s.b.d.a.ConditionEvaluationDeltaLoggingListener - Condition evaluation unchanged
+2025-10-06 10:37:51.463 [http-nio-8080-exec-1] INFO  o.a.c.c.C.[Tomcat-2].[localhost].[/api] - Initializing Spring DispatcherServlet 'dispatcherServlet'
+2025-10-06 10:37:51.468 [http-nio-8080-exec-1] INFO  org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
+2025-10-06 10:37:51.470 [http-nio-8080-exec-1] INFO  org.springframework.web.servlet.DispatcherServlet - Completed initialization in 1 ms
+2025-10-06 10:37:51.478 [http-nio-8080-exec-1] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:37:51.495 [http-nio-8080-exec-1] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:37:51.495 [http-nio-8080-exec-1] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:37:51.497 [http-nio-8080-exec-1] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:37:51.531 [http-nio-8080-exec-1] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:37:51Z, a difference of 35818530 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:37:51.835 [http-nio-8080-exec-3] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+2025-10-06 10:37:51.835 [http-nio-8080-exec-9] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+2025-10-06 10:37:51.835 [http-nio-8080-exec-2] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:37:51.835 [http-nio-8080-exec-5] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+2025-10-06 10:37:51.836 [http-nio-8080-exec-3] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:37:51.836 [http-nio-8080-exec-3] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:37:51.836 [http-nio-8080-exec-9] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:37:51.836 [http-nio-8080-exec-2] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:37:51.837 [http-nio-8080-exec-3] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:37:51.837 [http-nio-8080-exec-5] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:37:51.837 [http-nio-8080-exec-9] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:37:51.837 [http-nio-8080-exec-2] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:37:51.837 [http-nio-8080-exec-5] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:37:51.837 [http-nio-8080-exec-9] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:37:51.837 [http-nio-8080-exec-2] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:37:51.837 [http-nio-8080-exec-5] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:37:51.839 [http-nio-8080-exec-3] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:37:51Z, a difference of 35818839 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:37:51.839 [http-nio-8080-exec-9] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:37:51Z, a difference of 35818839 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:37:51.840 [http-nio-8080-exec-2] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:37:51Z, a difference of 35818839 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:37:51.840 [http-nio-8080-exec-5] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:37:51Z, a difference of 35818840 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:37:54.381 [http-nio-8080-exec-6] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:37:54.383 [http-nio-8080-exec-6] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:37:54.383 [http-nio-8080-exec-6] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:37:54.384 [http-nio-8080-exec-6] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:37:54.385 [http-nio-8080-exec-6] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:37:54Z, a difference of 35821385 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:37:55.897 [http-nio-8080-exec-4] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+2025-10-06 10:37:55.897 [http-nio-8080-exec-7] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:37:55.903 [http-nio-8080-exec-4] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:37:55.903 [http-nio-8080-exec-7] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:37:55.903 [http-nio-8080-exec-4] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:37:55.903 [http-nio-8080-exec-7] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:37:55.903 [http-nio-8080-exec-4] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:37:55.903 [http-nio-8080-exec-7] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:37:55.904 [http-nio-8080-exec-4] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:37:55Z, a difference of 35822904 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:37:55.904 [http-nio-8080-exec-7] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:37:55Z, a difference of 35822904 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:37:55.913 [http-nio-8080-exec-8] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:37:55.914 [http-nio-8080-exec-8] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:37:55.914 [http-nio-8080-exec-8] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:37:55.915 [http-nio-8080-exec-8] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:37:55.915 [http-nio-8080-exec-8] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:37:55Z, a difference of 35822915 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:38:03.512 [http-nio-8080-exec-10] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+2025-10-06 10:38:03.512 [http-nio-8080-exec-3] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+2025-10-06 10:38:03.512 [http-nio-8080-exec-9] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+2025-10-06 10:38:03.512 [http-nio-8080-exec-1] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:38:03.517 [http-nio-8080-exec-9] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:38:03.517 [http-nio-8080-exec-10] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:38:03.517 [http-nio-8080-exec-3] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:38:03.518 [http-nio-8080-exec-9] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:38:03.518 [http-nio-8080-exec-1] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:38:03.518 [http-nio-8080-exec-10] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:38:03.518 [http-nio-8080-exec-3] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:38:03.518 [http-nio-8080-exec-9] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:38:03.518 [http-nio-8080-exec-1] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:38:03.518 [http-nio-8080-exec-10] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:38:03.519 [http-nio-8080-exec-3] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:38:03.519 [http-nio-8080-exec-1] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:38:03.520 [http-nio-8080-exec-9] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:38:03Z, a difference of 35830520 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:38:03.520 [http-nio-8080-exec-10] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:38:03Z, a difference of 35830520 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:38:03.520 [http-nio-8080-exec-3] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:38:03Z, a difference of 35830520 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:38:03.520 [http-nio-8080-exec-1] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:38:03Z, a difference of 35830520 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:38:09.284 [http-nio-8080-exec-2] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:38:09.289 [http-nio-8080-exec-2] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:38:09.290 [http-nio-8080-exec-2] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:38:09.290 [http-nio-8080-exec-2] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:38:09.291 [http-nio-8080-exec-2] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:38:09Z, a difference of 35836291 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:38:40.377 [http-nio-8080-exec-5] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:38:40.382 [http-nio-8080-exec-5] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:38:40.383 [http-nio-8080-exec-5] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:38:40.383 [http-nio-8080-exec-5] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:38:40.385 [http-nio-8080-exec-5] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:38:40Z, a difference of 35867385 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:38:40.481 [http-nio-8080-exec-6] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:38:40.482 [http-nio-8080-exec-6] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:38:40.483 [http-nio-8080-exec-6] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:38:40.483 [http-nio-8080-exec-6] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:38:40.484 [http-nio-8080-exec-6] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:38:40Z, a difference of 35867484 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:38:44.492 [http-nio-8080-exec-4] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:38:44.498 [http-nio-8080-exec-4] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:38:44.498 [http-nio-8080-exec-4] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:38:44.498 [http-nio-8080-exec-4] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:38:44.499 [http-nio-8080-exec-4] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:38:44Z, a difference of 35871499 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:38:44.581 [http-nio-8080-exec-7] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:38:44.582 [http-nio-8080-exec-7] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:38:44.582 [http-nio-8080-exec-7] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:38:44.582 [http-nio-8080-exec-7] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:38:44.583 [http-nio-8080-exec-7] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:38:44Z, a difference of 35871583 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:40:38.327 [http-nio-8080-exec-8] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+2025-10-06 10:40:38.328 [http-nio-8080-exec-10] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+2025-10-06 10:40:38.328 [http-nio-8080-exec-9] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:40:38.329 [http-nio-8080-exec-3] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:40:38.333 [http-nio-8080-exec-8] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:40:38.334 [http-nio-8080-exec-9] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:40:38.334 [http-nio-8080-exec-10] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:40:38.334 [http-nio-8080-exec-8] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:40:38.334 [http-nio-8080-exec-3] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:40:38.334 [http-nio-8080-exec-9] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:40:38.334 [http-nio-8080-exec-10] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:40:38.334 [http-nio-8080-exec-8] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:40:38.334 [http-nio-8080-exec-3] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:40:38.335 [http-nio-8080-exec-9] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:40:38.335 [http-nio-8080-exec-10] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:40:38.335 [http-nio-8080-exec-3] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:40:38.336 [http-nio-8080-exec-8] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:40:38Z, a difference of 35985336 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:40:38.336 [http-nio-8080-exec-9] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:40:38Z, a difference of 35985336 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:40:38.336 [http-nio-8080-exec-3] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:40:38Z, a difference of 35985336 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:40:38.337 [http-nio-8080-exec-10] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:40:38Z, a difference of 35985337 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:40:39.408 [http-nio-8080-exec-1] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:40:39.413 [http-nio-8080-exec-1] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:40:39.413 [http-nio-8080-exec-1] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:40:39.413 [http-nio-8080-exec-1] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:40:39.414 [http-nio-8080-exec-1] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:40:39Z, a difference of 35986414 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:40:40.634 [http-nio-8080-exec-2] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:40:40.640 [http-nio-8080-exec-2] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:40:40.640 [http-nio-8080-exec-2] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:40:40.640 [http-nio-8080-exec-2] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:40:40.641 [http-nio-8080-exec-2] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:40:40Z, a difference of 35987641 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:40:40.727 [http-nio-8080-exec-5] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:40:40.728 [http-nio-8080-exec-5] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:40:40.728 [http-nio-8080-exec-5] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:40:40.728 [http-nio-8080-exec-5] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:40:40.730 [http-nio-8080-exec-5] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:40:40Z, a difference of 35987730 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:40:41.708 [http-nio-8080-exec-6] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:40:41.715 [http-nio-8080-exec-6] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:40:41.715 [http-nio-8080-exec-6] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:40:41.715 [http-nio-8080-exec-6] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:40:41.716 [http-nio-8080-exec-6] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:40:41Z, a difference of 35988716 milliseconds.  Allowed clock skew: 0 milliseconds.
+2025-10-06 10:40:41.800 [http-nio-8080-exec-4] DEBUG org.springframework.security.web.FilterChainProxy - Securing POST /graphql
+=== JWT过滤器被调用 === 原始URI: /api/graphql, 去掉context path后: /graphql
+2025-10-06 10:40:41.801 [http-nio-8080-exec-4] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - JWT杩囨护鍣ㄥ紑濮嬪鐞嗚姹�: /graphql
+2025-10-06 10:40:41.801 [http-nio-8080-exec-4] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌GraphQL璇锋眰
+2025-10-06 10:40:41.802 [http-nio-8080-exec-4] DEBUG c.rongyichuang.auth.filter.JwtAuthenticationFilter - 妫�娴嬪埌闇�瑕佽璇佺殑GraphQL璇锋眰锛屽紑濮嬮獙璇丣WT
+2025-10-06 10:40:41.803 [http-nio-8080-exec-4] ERROR c.rongyichuang.auth.filter.JwtAuthenticationFilter - GraphQL璇锋眰JWT楠岃瘉澶辫触: JWT expired at 2025-10-05T16:40:53Z. Current time: 2025-10-06T02:40:41Z, a difference of 35988803 milliseconds.  Allowed clock skew: 0 milliseconds.
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD FAILURE
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time:  01:31 h
+[INFO] Finished at: 2025-10-06T10:41:06+08:00
+[INFO] ------------------------------------------------------------------------
+[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:3.2.0:run (default-cli) on project ryc-backend: Process terminated with exit code: 1 -> [Help 1]
+[ERROR] 
+[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
+[ERROR] Re-run Maven using the -X switch to enable full debug logging.
+[ERROR] 
+[ERROR] For more information about the errors and possible solutions, please read the following articles:
+[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
diff --git a/backend/src/main/java/com/rongyichuang/activity/resolver/ActivityResolver.java b/backend/src/main/java/com/rongyichuang/activity/resolver/ActivityResolver.java
index 12d1796..afeaee8 100644
--- a/backend/src/main/java/com/rongyichuang/activity/resolver/ActivityResolver.java
+++ b/backend/src/main/java/com/rongyichuang/activity/resolver/ActivityResolver.java
@@ -37,9 +37,9 @@
      * 鍒嗛〉鏌ヨ姣旇禌鍒楄〃
      */
     @QueryMapping
-    public PageResponse<ActivityResponse> activities(@Argument int page, @Argument int size, @Argument String name) {
+    public PageResponse<ActivityResponse> activities(@Argument int page, @Argument int size, @Argument String name, @Argument Integer state) {
         PageRequest pageRequest = new PageRequest(page, size);
-        return activityService.findActivities(pageRequest, name);
+        return activityService.findActivities(pageRequest, name, state);
     }
     
     /**
@@ -97,6 +97,11 @@
     public Boolean deleteActivity(@Argument Long id) {
         return activityService.deleteActivity(id);
     }
+
+    @MutationMapping
+    public Boolean updateActivityState(@Argument Long id, @Argument Integer state) {
+        return activityService.updateActivityState(id, state);
+    }
     
     /**
      * 瑙f瀽Activity鐨刢overImage瀛楁
diff --git a/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java b/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
index c5254f0..845d515 100644
--- a/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
+++ b/backend/src/main/java/com/rongyichuang/activity/service/ActivityService.java
@@ -60,25 +60,22 @@
     /**
      * 鍒嗛〉鏌ヨ姣旇禌鍒楄〃
      */
-    public PageResponse<ActivityResponse> findActivities(PageRequest pageRequest, String name) {
+    public PageResponse<ActivityResponse> findActivities(PageRequest pageRequest, String name, Integer state) {
         Pageable pageable = pageRequest.toPageable();
         Page<Activity> page;
 
-        if (StringUtils.hasText(name)) {
+        boolean hasName = StringUtils.hasText(name);
+        if (state != null) {
+            if (hasName) {
+                page = activityRepository.findByPidAndStateAndNameContainingOrderByCreateTimeDesc(0L, state, name, pageable);
+            } else {
+                page = activityRepository.findByPidAndStateOrderByCreateTimeDesc(0L, state, pageable);
+            }
+        } else if (hasName) {
             page = activityRepository.findByPidAndNameContainingOrderByCreateTimeDesc(0L, name, pageable);
         } else {
             // 鏌ヨ鎵�鏈変富娲诲姩锛坧id = 0锛�
             page = activityRepository.findByPidOrderByCreateTimeDesc(0L, pageable);
-        }
-
-        // 璋冭瘯锛氭墦鍗板垎椤靛師濮嬫椿鍔ㄧ殑鎶ュ悕鎴鏃堕棿
-        try {
-            log.info("鍒嗛〉鏌ヨ姣旇禌鍒楄〃锛氭�绘暟={}, 褰撳墠椤�={}, 姣忛〉={}", page.getTotalElements(), page.getNumber(), page.getSize());
-            page.getContent().stream().limit(10).forEach(a ->
-                log.info("Activity(id={}, name={}) signupDeadline={}, matchTime={}", a.getId(), a.getName(), a.getSignupDeadline(), a.getMatchTime())
-            );
-        } catch (Exception e) {
-            log.warn("鎵撳嵃娲诲姩鎶ュ悕鎴鏃堕棿鏃ュ織澶辫触: {}", e.getMessage());
         }
 
         List<ActivityResponse> content = page.getContent().stream()
@@ -372,6 +369,17 @@
         }
         return false;
     }
+
+    public boolean updateActivityState(Long id, Integer state) {
+        Optional<Activity> activityOpt = activityRepository.findById(id);
+        if (activityOpt.isPresent()) {
+            Activity activity = activityOpt.get();
+            activity.setState(state);
+            activityRepository.save(activity);
+            return true;
+        }
+        return false;
+    }
     
     /**
      * 鑾峰彇鎵�鏈夋湁鏁堜富姣旇禌锛堢敤浜庝笅鎷夐�夋嫨锛�
diff --git a/backend/src/main/java/com/rongyichuang/dashboard/api/DashboardGraphqlApi.java b/backend/src/main/java/com/rongyichuang/dashboard/api/DashboardGraphqlApi.java
index d52b108..e8fc091 100644
--- a/backend/src/main/java/com/rongyichuang/dashboard/api/DashboardGraphqlApi.java
+++ b/backend/src/main/java/com/rongyichuang/dashboard/api/DashboardGraphqlApi.java
@@ -1,11 +1,16 @@
 package com.rongyichuang.dashboard.api;
 
 import com.rongyichuang.dashboard.dto.response.DashboardStatsResponse;
+import com.rongyichuang.dashboard.dto.response.RegionRegistrationStat;
+import com.rongyichuang.dashboard.dto.response.RegistrationTrendPoint;
 import com.rongyichuang.dashboard.service.DashboardService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.graphql.data.method.annotation.Argument;
 import org.springframework.graphql.data.method.annotation.QueryMapping;
 import org.springframework.stereotype.Controller;
+
+import java.util.List;
 
 /**
  * Dashboard GraphQL API
@@ -29,4 +34,22 @@
         log.info("鑾峰彇Dashboard缁熻鏁版嵁");
         return dashboardService.getDashboardStats();
     }
-}
\ No newline at end of file
+
+    /**
+     * 鑾峰彇鎶ュ悕瓒嬪娍鏁版嵁
+     */
+    @QueryMapping
+    public List<RegistrationTrendPoint> registrationTrend(@Argument Integer days) {
+        log.info("鑾峰彇鎶ュ悕瓒嬪娍锛屽ぉ鏁�: {}", days);
+        return dashboardService.getRegistrationTrend(days);
+    }
+
+    /**
+     * 鑾峰彇鍖哄煙鎶ュ悕鍒嗗竷
+     */
+    @QueryMapping
+    public List<RegionRegistrationStat> registrationRegionStats() {
+        log.info("鑾峰彇鍖哄煙鎶ュ悕鍒嗗竷");
+        return dashboardService.getRegionRegistrationStats();
+    }
+}
diff --git a/backend/src/main/java/com/rongyichuang/dashboard/dto/response/RegionRegistrationStat.java b/backend/src/main/java/com/rongyichuang/dashboard/dto/response/RegionRegistrationStat.java
new file mode 100644
index 0000000..98802cc
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/dashboard/dto/response/RegionRegistrationStat.java
@@ -0,0 +1,69 @@
+package com.rongyichuang.dashboard.dto.response;
+
+/**
+ * 鍖哄煙鎶ュ悕缁熻
+ */
+public class RegionRegistrationStat {
+
+    /**
+     * 鍖哄煙ID锛堝彲鑳戒负鏁板瓧鎴朥UID瀛楃涓诧級
+     */
+    private String regionId;
+
+    /**
+     * 鍖哄煙鍚嶇О
+     */
+    private String regionName;
+
+    /**
+     * 鏄惁鍙跺瓙鑺傜偣
+     */
+    private Boolean leafFlag;
+
+    /**
+     * 鎶ュ悕鏁伴噺
+     */
+    private Long count;
+
+    public RegionRegistrationStat() {
+    }
+
+    public RegionRegistrationStat(String regionId, String regionName, Boolean leafFlag, Long count) {
+        this.regionId = regionId;
+        this.regionName = regionName;
+        this.leafFlag = leafFlag;
+        this.count = count;
+    }
+
+    public String getRegionId() {
+        return regionId;
+    }
+
+    public void setRegionId(String regionId) {
+        this.regionId = regionId;
+    }
+
+    public String getRegionName() {
+        return regionName;
+    }
+
+    public void setRegionName(String regionName) {
+        this.regionName = regionName;
+    }
+
+    public Boolean getLeafFlag() {
+        return leafFlag;
+    }
+
+    public void setLeafFlag(Boolean leafFlag) {
+        this.leafFlag = leafFlag;
+    }
+
+    public Long getCount() {
+        return count;
+    }
+
+    public void setCount(Long count) {
+        this.count = count;
+    }
+}
diff --git a/backend/src/main/java/com/rongyichuang/dashboard/dto/response/RegistrationTrendPoint.java b/backend/src/main/java/com/rongyichuang/dashboard/dto/response/RegistrationTrendPoint.java
new file mode 100644
index 0000000..9a7bcfe
--- /dev/null
+++ b/backend/src/main/java/com/rongyichuang/dashboard/dto/response/RegistrationTrendPoint.java
@@ -0,0 +1,41 @@
+package com.rongyichuang.dashboard.dto.response;
+
+/**
+ * 鎶ュ悕瓒嬪娍鏁版嵁鐐�
+ */
+public class RegistrationTrendPoint {
+
+    /**
+     * 鏃ユ湡锛坹yyy-MM-dd锛�
+     */
+    private String date;
+
+    /**
+     * 鎶ュ悕鏁伴噺
+     */
+    private Long count;
+
+    public RegistrationTrendPoint() {
+    }
+
+    public RegistrationTrendPoint(String date, Long count) {
+        this.date = date;
+        this.count = count;
+    }
+
+    public String getDate() {
+        return date;
+    }
+
+    public void setDate(String date) {
+        this.date = date;
+    }
+
+    public Long getCount() {
+        return count;
+    }
+
+    public void setCount(Long count) {
+        this.count = count;
+    }
+}
diff --git a/backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java b/backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java
index b97fdf0..ae96164 100644
--- a/backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java
+++ b/backend/src/main/java/com/rongyichuang/dashboard/service/DashboardService.java
@@ -1,12 +1,22 @@
 package com.rongyichuang.dashboard.service;
 
 import com.rongyichuang.activity.repository.ActivityRepository;
+import com.rongyichuang.dashboard.dto.response.DashboardStatsResponse;
+import com.rongyichuang.dashboard.dto.response.RegionRegistrationStat;
+import com.rongyichuang.dashboard.dto.response.RegistrationTrendPoint;
 import com.rongyichuang.judge.repository.JudgeRepository;
 import com.rongyichuang.player.repository.ActivityPlayerRepository;
 import com.rongyichuang.player.repository.PlayerRepository;
-import com.rongyichuang.dashboard.dto.response.DashboardStatsResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Dashboard 缁熻鏁版嵁鏈嶅姟
@@ -14,15 +24,19 @@
 @Service
 public class DashboardService {
 
+    private static final int DEFAULT_TREND_DAYS = 15;
+    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+    private static final String UNASSIGNED_REGION_NAME = "鏈�夋嫨鍖哄煙";
+
     @Autowired
     private ActivityRepository activityRepository;
-    
+
     @Autowired
     private PlayerRepository playerRepository;
-    
+
     @Autowired
     private ActivityPlayerRepository activityPlayerRepository;
-    
+
     @Autowired
     private JudgeRepository judgeRepository;
 
@@ -31,23 +45,117 @@
      */
     public DashboardStatsResponse getDashboardStats() {
         DashboardStatsResponse stats = new DashboardStatsResponse();
-        
+
         // 褰撳墠杩涜姣旇禌鏁伴噺锛堢姸鎬佷负1鐨勬瘮璧涳紝pid=0琛ㄧず涓绘瘮璧涳級
         long activeActivities = activityRepository.countActiveActivities();
         stats.setActiveActivities((int) activeActivities);
-        
-        // 鍙傝禌鎬讳汉鏁帮紙鐘舵�佷负1鐨勯�夋墜锛�
+
+        // 鍙傝禌鎬讳汉鏁帮紙鐘舵�佷负1鐨勫弬璧涢�夋墜锛�
         long totalPlayers = playerRepository.countByState(1);
         stats.setTotalPlayers((int) totalPlayers);
-        
+
         // 鎶ュ悕寰呭鏍镐汉鏁帮紙activityPlayer鐘舵�佷负0琛ㄧず寰呭鏍革級
         long pendingReviews = activityPlayerRepository.countByState(0);
         stats.setPendingReviews((int) pendingReviews);
-        
+
         // 璇勫鎬绘暟锛堢姸鎬佷负1鐨勮瘎濮旓級
         long totalJudges = judgeRepository.countByState(1);
         stats.setTotalJudges((int) totalJudges);
-        
+
         return stats;
     }
-}
\ No newline at end of file
+
+    /**
+     * 鑾峰彇鏈�杩戞姤鍚嶈秼鍔匡紙榛樿鏈�杩�15澶╋紝浠呯粺璁$涓�闃舵锛�
+     */
+    public List<RegistrationTrendPoint> getRegistrationTrend(Integer days) {
+        int queryDays = (days == null || days <= 0) ? DEFAULT_TREND_DAYS : days;
+        LocalDate today = LocalDate.now();
+        LocalDate startDate = today.minusDays(queryDays - 1L);
+        LocalDateTime startDateTime = startDate.atStartOfDay();
+
+        List<Object[]> rawData = activityPlayerRepository.countFirstStageRegistrationsByDate(startDateTime);
+        Map<String, Long> dataMap = new HashMap<>();
+
+        for (Object[] row : rawData) {
+            if (row == null || row.length < 2) {
+                continue;
+            }
+            Object dateObj = row[0];
+            LocalDate date;
+            if (dateObj instanceof java.sql.Date sqlDate) {
+                date = sqlDate.toLocalDate();
+            } else if (dateObj instanceof LocalDate localDate) {
+                date = localDate;
+            } else {
+                date = LocalDate.parse(dateObj.toString());
+            }
+            Long count = row[1] != null ? ((Number) row[1]).longValue() : 0L;
+            dataMap.put(date.format(DATE_FORMATTER), count);
+        }
+
+        List<RegistrationTrendPoint> result = new ArrayList<>(queryDays);
+        for (int i = 0; i < queryDays; i++) {
+            LocalDate date = startDate.plusDays(i);
+            String key = date.format(DATE_FORMATTER);
+            Long count = dataMap.getOrDefault(key, 0L);
+            result.add(new RegistrationTrendPoint(key, count));
+        }
+        return result;
+    }
+
+    /**
+     * 鑾峰彇鍖哄煙鎶ュ悕鍒嗗竷锛堜粎缁熻绗竴闃舵锛屽墧闄ら潪鍙跺瓙鍖哄煙锛�
+     */
+    public List<RegionRegistrationStat> getRegionRegistrationStats() {
+        List<Object[]> rawData = activityPlayerRepository.countFirstStageRegistrationsByRegion();
+        List<RegionRegistrationStat> stats = new ArrayList<>();
+
+        for (Object[] row : rawData) {
+            if (row == null || row.length < 4) {
+                continue;
+            }
+            String regionId = null;
+            if (row[0] != null) {
+                Object regionIdObj = row[0];
+                if (regionIdObj instanceof Number number) {
+                    regionId = String.valueOf(number.longValue());
+                } else {
+                    regionId = regionIdObj.toString();
+                }
+            }
+            String regionName = row[1] != null ? row[1].toString() : UNASSIGNED_REGION_NAME;
+            // leaf_flag瀛楁鍦ㄤ笉鍚岄┍鍔ㄤ笅鐨勭被鍨嬪彲鑳藉樊寮傦紝闇�瑕佺粺涓�杞崲
+            Boolean leafFlag = null;
+            Object leafObj = row[2];
+            if (leafObj instanceof Number number) {
+                leafFlag = number.intValue() == 1;
+            } else if (leafObj instanceof Boolean bool) {
+                leafFlag = bool;
+            } else if (leafObj != null) {
+                String leafText = leafObj.toString().trim().toLowerCase();
+                if (!leafText.isEmpty()) {
+                    if ("1".equals(leafText) || "true".equals(leafText)) {
+                        leafFlag = true;
+                    } else if ("0".equals(leafText) || "false".equals(leafText)) {
+                        leafFlag = false;
+                    }
+                }
+            }
+            Long count = row[3] != null ? ((Number) row[3]).longValue() : 0L;
+
+            if (Boolean.FALSE.equals(leafFlag)) {
+                // 闈炲彾瀛愬尯鍩熶笉鍙備笌缁熻
+                continue;
+            }
+
+            if (regionName == null || regionName.isBlank()) {
+                regionName = UNASSIGNED_REGION_NAME;
+            }
+
+            stats.add(new RegionRegistrationStat(regionId, regionName, leafFlag, count));
+        }
+
+        return stats;
+    }
+}
diff --git a/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java b/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
index 6f1be89..a58e054 100644
--- a/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
+++ b/backend/src/main/java/com/rongyichuang/player/api/PlayerGraphqlApi.java
@@ -87,12 +87,13 @@
     public ProjectReviewApplicationPageResponse projectReviewApplications(
             @Argument String name,
             @Argument Long activityId,
+            @Argument Long regionId,
             @Argument Integer state,
             @Argument Integer page,
             @Argument Integer size
     ) {
         PageResponse<ActivityPlayerApplicationResponse> pageResponse = 
-            service.listProjectReviewApplications(name, activityId, state, page, size);
+            service.listProjectReviewApplications(name, activityId, regionId, state, page, size);
         return ProjectReviewApplicationPageResponse.from(pageResponse);
     }
 
diff --git a/backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java b/backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java
index 82e151e..419dcbe 100644
--- a/backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java
+++ b/backend/src/main/java/com/rongyichuang/player/repository/ActivityPlayerRepository.java
@@ -8,6 +8,7 @@
 
 import java.util.List;
 import java.util.Optional;
+import java.time.LocalDateTime;
 
 /**
  * 娲诲姩閫夋墜Repository鎺ュ彛
@@ -90,6 +91,32 @@
     List<ActivityPlayer> findTopRankedPlayers(@Param("activityId") Long activityId);
 
     /**
+     * 缁熻鏈�杩戞姤鍚嶈秼鍔匡紙浠呯涓�闃舵锛宻tate鍖呭惈寰呭鏍镐笌閫氳繃锛�
+     */
+    @Query(value = "SELECT DATE(ap.create_time) AS signup_date, COUNT(*) AS total " +
+            "FROM t_activity_player ap " +
+            "JOIN t_activity stage ON stage.id = ap.stage_id " +
+            "WHERE stage.sort_order = 1 AND stage.state = 1 " +
+            "  AND ap.state IN (0, 1) " +
+            "  AND ap.create_time >= :startDate " +
+            "GROUP BY DATE(ap.create_time) " +
+            "ORDER BY signup_date", nativeQuery = true)
+    List<Object[]> countFirstStageRegistrationsByDate(@Param("startDate") LocalDateTime startDate);
+
+    /**
+     * 缁熻鍚勫尯鍩熸姤鍚嶆暟閲忥紙浠呯涓�闃舵锛宻tate鍖呭惈寰呭鏍镐笌閫氳繃锛�
+     */
+    @Query(value = "SELECT ap.region_id, r.name, r.leaf_flag, COUNT(*) AS total " +
+            "FROM t_activity_player ap " +
+            "JOIN t_activity stage ON stage.id = ap.stage_id " +
+            "LEFT JOIN t_region r ON r.id = ap.region_id " +
+            "WHERE stage.sort_order = 1 AND stage.state = 1 " +
+            "  AND ap.state IN (0, 1) " +
+            "GROUP BY ap.region_id, r.name, r.leaf_flag " +
+            "ORDER BY total DESC", nativeQuery = true)
+    List<Object[]> countFirstStageRegistrationsByRegion();
+
+    /**
      * 缁熻鎸囧畾鐘舵�佺殑鍙傝禌閫夋墜鏁伴噺
      */
     long countByState(Integer state);
diff --git a/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java b/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java
index 022a785..442d5ec 100644
--- a/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java
+++ b/backend/src/main/java/com/rongyichuang/player/service/PlayerApplicationService.java
@@ -83,6 +83,9 @@
         if (activityId != null) {
             q.setParameter("activityId", activityId);
         }
+        if (regionId != null) {
+            q.setParameter("regionId", regionId);
+        }
         if (state != null) {
             q.setParameter("state", state);
         }
@@ -126,7 +129,7 @@
      * 涓巐istApplications鐨勫尯鍒細涓嶈繃婊ゅ璧涘拰鍐宠禌闃舵
      */
     @SuppressWarnings("unchecked")
-    public PageResponse<ActivityPlayerApplicationResponse> listProjectReviewApplications(String name, Long activityId, Integer state, Integer page, Integer size) {
+    public PageResponse<ActivityPlayerApplicationResponse> listProjectReviewApplications(String name, Long activityId, Long regionId, Integer state, Integer page, Integer size) {
         String baseSql =
             "SELECT ap.id, CONCAT(p.name, '锛�', ap.project_name, '锛�') AS player_name, stage.name AS activity_name, ap.project_name AS project_name, p.phone AS phone, ap.create_time AS apply_time, ap.state AS state, " +
             "COALESCE(rating_stats.rating_count, 0) AS rating_count, rating_stats.average_score " +
@@ -159,6 +162,14 @@
             }
             // 鐩存帴鏌ヨ鎸囧畾闃舵ID鐨勬姤鍚嶉」鐩�
             whereClause.append("ap.stage_id = :activityId");
+            hasCondition = true;
+        }
+
+        if (regionId != null) {
+            if (hasCondition) {
+                whereClause.append(" AND ");
+            }
+            whereClause.append("ap.region_id = :regionId");
             hasCondition = true;
         }
         
@@ -197,6 +208,9 @@
         if (activityId != null) {
             q.setParameter("activityId", activityId);
         }
+        if (regionId != null) {
+            q.setParameter("regionId", regionId);
+        }
         if (state != null) {
             q.setParameter("state", state);
         }
diff --git a/backend/src/main/java/com/rongyichuang/region/repository/RegionRepository.java b/backend/src/main/java/com/rongyichuang/region/repository/RegionRepository.java
index 07e6c45..82d5f76 100644
--- a/backend/src/main/java/com/rongyichuang/region/repository/RegionRepository.java
+++ b/backend/src/main/java/com/rongyichuang/region/repository/RegionRepository.java
@@ -28,6 +28,8 @@
      */
     List<Region> findByStateOrderByIdAsc(Integer state);
     
+    List<Region> findByLeafFlagAndStateOrderByNameAsc(Boolean leafFlag, Integer state);
+    
     /**
      * 鏍规嵁鍚嶇О妯$硦鏌ヨ
      */
diff --git a/backend/src/main/java/com/rongyichuang/region/resolver/RegionResolver.java b/backend/src/main/java/com/rongyichuang/region/resolver/RegionResolver.java
index 3451bd2..a567c38 100644
--- a/backend/src/main/java/com/rongyichuang/region/resolver/RegionResolver.java
+++ b/backend/src/main/java/com/rongyichuang/region/resolver/RegionResolver.java
@@ -45,6 +45,14 @@
     }
     
     /**
+     * 鑾峰彇鍙跺瓙鍖哄煙鍒楄〃
+     */
+    @QueryMapping(name = "leafRegions")
+    public List<Region> leafRegions() {
+        return regionService.getLeafRegions();
+    }
+    
+    /**
      * 鏍规嵁ID鏌ヨ鍖哄煙
      */
     @QueryMapping(name = "region")
diff --git a/backend/src/main/java/com/rongyichuang/region/service/RegionService.java b/backend/src/main/java/com/rongyichuang/region/service/RegionService.java
index 2dab588..19c3b37 100644
--- a/backend/src/main/java/com/rongyichuang/region/service/RegionService.java
+++ b/backend/src/main/java/com/rongyichuang/region/service/RegionService.java
@@ -41,6 +41,13 @@
     }
     
     /**
+     * 鑾峰彇鍙跺瓙鍖哄煙鍒楄〃
+     */
+    public List<Region> getLeafRegions() {
+        return regionRepository.findByLeafFlagAndStateOrderByNameAsc(true, 1);
+    }
+    
+    /**
      * 鏍规嵁ID鏌ユ壘鍖哄煙
      */
     public Optional<Region> findById(Long id) {
diff --git a/backend/src/main/resources/graphql/activity.graphqls b/backend/src/main/resources/graphql/activity.graphqls
index 4520da0..ba530a4 100644
--- a/backend/src/main/resources/graphql/activity.graphqls
+++ b/backend/src/main/resources/graphql/activity.graphqls
@@ -1,112 +1,225 @@
 extend type Query {
+
     activity(id: ID!): Activity
-    activities(page: Int!, size: Int!, name: String): PageResponse
+
+    activities(page: Int!, size: Int!, name: String, state: Int): PageResponse
+
     allActivities: [Activity]
+
     allActivityStages: [Activity]
+
     activityStages(activityId: ID!): [Activity]
+
     ongoingActivities: [Activity]
-    # 寰俊绔幏鍙栨椿鍔ㄥ垪琛�
+
+    # 瀵邦喕淇婄粩顖濆箯閸欐牗妞块崝銊ュ灙鐞�?
+
     getActivities: [Activity]
-    # 鑾峰彇涓诲姙鏂圭粺璁℃暟鎹�
+
+    # 閼惧嘲褰囨稉璇插閺傚湱绮虹拋鈩冩殶閹�?
+
     organizerStats: OrganizerStatsResponse
+
 }
+
+
 
 extend type Mutation {
+
     saveActivity(input: ActivityInput!): Activity
+
     deleteActivity(id: ID!): Boolean
+    updateActivityState(id: ID!, state: Int!): Boolean
+
 }
+
+
 
 type Activity {
+
     id: ID
+
     pid: ID
+
     path: String
+
     name: String
+
     description: String
+
     signupDeadline: String
+
     matchTime: String
+
     address: String
+
     ratingSchemeId: ID
+
     ratingScheme: RatingSchemeResponse
+
     playerCount: Int
+
     playerMax: Int
+
     sortOrder: Int
+
     state: Int
+
     stateName: String
+
     createTime: String
+
     updateTime: String
+
     coverImage: MediaResponse
+
     images: [MediaResponse]
+
     videos: [MediaResponse]
+
     stages: [Activity]
+
     parent: Activity
+
     judges: [ActivityJudgeResponse]
+
 }
+
+
 
 type PageResponse {
+
     content: [Activity]
+
     page: Int
+
     size: Int
+
     total: Int
+
     totalPages: Int
+
     totalElements: Int
+
     number: Int
+
     first: Boolean
+
     last: Boolean
+
 }
+
+
 
 input ActivityInput {
+
     id: ID
+
     pid: ID
+
     name: String!
+
     description: String
+
     signupDeadline: String
+
     matchTime: String
+
     address: String
+
     ratingSchemeId: ID
+
     playerMax: Int
+
     sortOrder: Int
+
     state: Int
+
     stages: [ActivityStageInput]
+
     judges: [ActivityJudgeInput]
+
 }
+
+
 
 input ActivityStageInput {
+
     id: ID
+
     name: String!
+
     description: String
+
     matchTime: String
+
     address: String
+
     ratingSchemeId: ID
+
     playerMax: Int
+
     sortOrder: Int
+
     state: Int
+
 }
+
+
 
 input ActivityJudgeInput {
+
     judgeId: ID!
+
     judgeName: String
+
     stageIds: [ID]
+
 }
+
+
 
 type ActivityJudgeResponse {
+
     id: ID
+
     name: String
+
     phone: String
+
     description: String
+
     stageIds: [ID]
+
 }
 
-# 娲诲姩璇︽儏鍜岀姸鎬佸搷搴旂被鍨嬶紙寰俊绔娇鐢級
+
+
+# 濞茶濮╃拠锔藉剰閸滃瞼濮搁幀浣告惙鎼存梻琚崹瀣剁礄瀵邦喕淇婄粩顖欏▏閻㈩煉绱�
+
 type ActivityDetailAndStatusResponse {
+
     activity: Activity
+
     registrationStatus: String
+
     canRegister: Boolean
+
     playerRegistration: ActivityPlayer
+
 }
 
-# 涓诲姙鏂圭粺璁″搷搴旂被鍨�
+
+
+# 娑撹濮欓弬鍦埠鐠佲�虫惙鎼存梻琚崹?
+
 type OrganizerStatsResponse {
+
     activeActivities: Int!
+
     totalParticipants: Int!
+
     totalActivities: Int!
-}
\ No newline at end of file
+
+}
+
diff --git a/backend/src/main/resources/graphql/dashboard.graphqls b/backend/src/main/resources/graphql/dashboard.graphqls
index cb26e77..8016add 100644
--- a/backend/src/main/resources/graphql/dashboard.graphqls
+++ b/backend/src/main/resources/graphql/dashboard.graphqls
@@ -15,8 +15,38 @@
     totalJudges: Int!
 }
 
+# 鎶ュ悕瓒嬪娍鏁版嵁鐐�
+type RegistrationTrendPoint {
+    # 鏃ユ湡锛坹yyy-MM-dd锛�
+    date: String!
+
+    # 鎶ュ悕鏁伴噺
+    count: Long!
+}
+
+# 鍖哄煙鎶ュ悕缁熻
+type RegionRegistrationStat {
+    # 鍖哄煙ID锛堝彲鑳戒负鏁板瓧鎴朥UID瀛楃涓诧紝鏈�夋嫨鏃朵负绌猴級
+    regionId: ID
+
+    # 鍖哄煙鍚嶇О
+    regionName: String!
+
+    # 鏄惁鍙跺瓙鑺傜偣锛堟暟鎹己澶辨椂涓虹┖锛�
+    leafFlag: Boolean
+
+    # 鎶ュ悕鏁伴噺
+    count: Long!
+}
+
 # 鎵╁睍鏌ヨ
 extend type Query {
     # 鑾峰彇Dashboard缁熻鏁版嵁
     dashboardStats: DashboardStats!
-}
\ No newline at end of file
+
+    # 鑾峰彇鏈�杩戞姤鍚嶈秼鍔�
+    registrationTrend(days: Int): [RegistrationTrendPoint!]!
+
+    # 鑾峰彇鍖哄煙鎶ュ悕鍒嗗竷
+    registrationRegionStats: [RegionRegistrationStat!]!
+}
diff --git a/backend/src/main/resources/graphql/player.graphqls b/backend/src/main/resources/graphql/player.graphqls
index e686a1d..3c61dce 100644
--- a/backend/src/main/resources/graphql/player.graphqls
+++ b/backend/src/main/resources/graphql/player.graphqls
@@ -2,7 +2,7 @@
     myActivityPlayer(activityId: ID!): ActivityPlayer
     activityPlayerApplications(name: String, activityId: ID, state: Int, page: Int, size: Int): PlayerApplicationPageResponse
     # 椤圭洰璇勫涓撶敤鏌ヨ锛屽寘鍚墍鏈夐樁娈垫暟鎹紙鍖呮嫭澶嶈禌銆佸喅璧涳級
-    projectReviewApplications(name: String, activityId: ID, state: Int, page: Int, size: Int): ProjectReviewApplicationPageResponse
+    projectReviewApplications(name: String, activityId: ID, regionId: ID, state: Int, page: Int, size: Int): ProjectReviewApplicationPageResponse
     # 鑾峰彇鎸囧畾閫夋墜鐨勬墍鏈夎瘎濮旇瘎鍒嗙姸鎬�
     judgeRatingsForPlayer(activityPlayerId: ID!): [JudgeRatingStatusResponse!]!
     # 鑾峰彇鎸囧畾閫夋墜鐨勫钩鍧囧垎
diff --git a/backend/src/main/resources/graphql/region.graphqls b/backend/src/main/resources/graphql/region.graphqls
index 92eefe6..5eda079 100644
--- a/backend/src/main/resources/graphql/region.graphqls
+++ b/backend/src/main/resources/graphql/region.graphqls
@@ -56,6 +56,9 @@
     
     # 鑾峰彇瀛愬尯鍩熷垪琛�
     regionChildren(parentId: ID!): [Region!]!
+    
+    # 鑾峰彇鍙跺瓙鍖哄煙鍒楄〃
+    leafRegions: [Region!]!
 }
 
 extend type Mutation {
diff --git a/web/package.json b/web/package.json
index cd13aad..bbd2e19 100644
--- a/web/package.json
+++ b/web/package.json
@@ -16,6 +16,7 @@
     "axios": "^1.6.2",
     "cos-js-sdk-v5": "^1.10.1",
     "dayjs": "^1.11.10",
+    "echarts": "^6.0.0",
     "element-plus": "^2.4.4",
     "graphql": "^16.11.0",
     "graphql-tag": "^2.12.6",
diff --git a/web/src/api/activity.js b/web/src/api/activity.js
index b91db36..8c42f87 100644
--- a/web/src/api/activity.js
+++ b/web/src/api/activity.js
@@ -1,11 +1,11 @@
 // 姣旇禌绠$悊 API
 
-import { API_CONFIG, graphqlRequest } from '@/config/api';
+import { API_CONFIG, graphqlRequest } from '@/config/api'
 
 // GraphQL 鏌ヨ璇彞
 const GET_ACTIVITIES_QUERY = `
-  query GetActivities($page: Int!, $size: Int!, $name: String) {
-    activities(page: $page, size: $size, name: $name) {
+  query GetActivities($page: Int!, $size: Int!, $name: String, $state: Int) {
+    activities(page: $page, size: $size, name: $name, state: $state) {
       content {
         id
         name
@@ -25,7 +25,7 @@
       size
     }
   }
-`;
+`
 
 const GET_ACTIVITY_QUERY = `
   query GetActivity($id: ID!) {
@@ -75,7 +75,7 @@
       }
     }
   }
-`;
+`
 
 const GET_ALL_ACTIVITIES_QUERY = `
   query GetAllActivities {
@@ -93,7 +93,7 @@
       }
     }
   }
-`;
+`
 
 const SAVE_ACTIVITY_MUTATION = `
   mutation SaveActivity($input: ActivityInput!) {
@@ -110,59 +110,84 @@
       stateName
     }
   }
-`;
+`
 
 const DELETE_ACTIVITY_MUTATION = `
   mutation DeleteActivity($id: ID!) {
     deleteActivity(id: $id)
   }
-`;
+`
+
+const UPDATE_ACTIVITY_STATE_MUTATION = `
+  mutation UpdateActivityState($id: ID!, $state: Int!) {
+    updateActivityState(id: $id, state: $state)
+  }
+`
 
 // API 鍑芥暟
-export const getActivities = async (page = 0, size = 10, name = '') => {
+export const getActivities = async (page = 0, size = 10, name = '', state) => {
   try {
-    const result = await graphqlRequest(GET_ACTIVITIES_QUERY, { page, size, name });
-    return result.data.activities;
+    const variables = {
+      page,
+      size,
+      name
+    }
+
+    if (state !== undefined && state !== null && state !== '') {
+      variables.state = Number(state)
+    }
+
+    const result = await graphqlRequest(GET_ACTIVITIES_QUERY, variables)
+    return result.data.activities
   } catch (error) {
-    console.error('鑾峰彇姣旇禌鍒楄〃澶辫触:', error);
-    throw new Error(error.message || '鑾峰彇姣旇禌鍒楄〃澶辫触');
+    console.error('鑾峰彇姣旇禌鍒楄〃澶辫触:', error)
+    throw new Error(error && error.message ? error.message : '鑾峰彇姣旇禌鍒楄〃澶辫触')
   }
-};
+}
 
 export const getActivity = async (id) => {
   try {
-    const result = await graphqlRequest(GET_ACTIVITY_QUERY, { id });
-    return result.data.activity;
+    const result = await graphqlRequest(GET_ACTIVITY_QUERY, { id })
+    return result.data.activity
   } catch (error) {
-    throw new Error(error.message || '鑾峰彇姣旇禌璇︽儏澶辫触');
+    throw new Error(error && error.message ? error.message : '鑾峰彇姣旇禌璇︽儏澶辫触')
   }
-};
+}
 
 export const getAllActivities = async () => {
   try {
-    const result = await graphqlRequest(GET_ALL_ACTIVITIES_QUERY);
-    return result.data.allActivities;
+    const result = await graphqlRequest(GET_ALL_ACTIVITIES_QUERY)
+    return result.data.allActivities
   } catch (error) {
-    console.error('鑾峰彇鎵�鏈夋瘮璧涘け璐�:', error);
-    throw new Error(error.message || '鑾峰彇鎵�鏈夋瘮璧涘け璐�');
+    console.error('鑾峰彇鎵�鏈夋瘮璧涘け璐�:', error)
+    throw new Error(error && error.message ? error.message : '鑾峰彇鎵�鏈夋瘮璧涘け璐�')
   }
-};
+}
 
 export const saveActivity = async (activityData) => {
   try {
-    const data = await graphqlRequest(SAVE_ACTIVITY_MUTATION, { input: activityData });
-    return data.data.saveActivity;
+    const data = await graphqlRequest(SAVE_ACTIVITY_MUTATION, { input: activityData })
+    return data.data.saveActivity
   } catch (error) {
-    console.error('淇濆瓨姣旇禌澶辫触:', error);
-    throw new Error(error.message || '淇濆瓨姣旇禌澶辫触');
+    console.error('淇濆瓨姣旇禌澶辫触:', error)
+    throw new Error(error && error.message ? error.message : '淇濆瓨姣旇禌澶辫触')
   }
-};
+}
 
 export const deleteActivity = async (id) => {
   try {
-    const data = await graphqlRequest(DELETE_ACTIVITY_MUTATION, { id });
-    return data.data.deleteActivity;
+    const data = await graphqlRequest(DELETE_ACTIVITY_MUTATION, { id })
+    return data.data.deleteActivity
   } catch (error) {
-    throw new Error(error.message || '鍒犻櫎姣旇禌澶辫触');
+    throw new Error(error && error.message ? error.message : '鍒犻櫎姣旇禌澶辫触')
   }
-};
\ No newline at end of file
+}
+
+export const updateActivityState = async (id, state) => {
+  try {
+    const data = await graphqlRequest(UPDATE_ACTIVITY_STATE_MUTATION, { id, state })
+    return data.data.updateActivityState
+  } catch (error) {
+    throw new Error(error && error.message ? error.message : '鏇存柊姣旇禌鐘舵�佸け璐�')
+  }
+}
\ No newline at end of file
diff --git a/web/src/api/dashboard.js b/web/src/api/dashboard.js
index 5d93132..a7adc52 100644
--- a/web/src/api/dashboard.js
+++ b/web/src/api/dashboard.js
@@ -1,6 +1,6 @@
 import { graphqlRequest } from '../config/api'
 
-// GraphQL 鏌ヨ璇彞
+// Dashboard 鎸囨爣鏌ヨ
 const GET_DASHBOARD_STATS_QUERY = `
   query GetDashboardStats {
     dashboardStats {
@@ -10,10 +10,41 @@
       totalJudges
     }
   }
-`;
+`
 
-// API 鍑芥暟
+// 鎶ュ悕瓒嬪娍缁熻
+const GET_REGISTRATION_TREND_QUERY = `
+  query RegistrationTrend($days: Int) {
+    registrationTrend(days: $days) {
+      date
+      count
+    }
+  }
+`
+
+// 鍖哄煙鎶ュ悕缁熻
+const GET_REGION_REGISTRATION_STATS_QUERY = `
+  query RegistrationRegionStats {
+    registrationRegionStats {
+      regionId
+      regionName
+      leafFlag
+      count
+    }
+  }
+`
+
 export const getDashboardStats = async () => {
-  const result = await graphqlRequest(GET_DASHBOARD_STATS_QUERY);
-  return result.data.dashboardStats;
-};
\ No newline at end of file
+  const result = await graphqlRequest(GET_DASHBOARD_STATS_QUERY)
+  return result.data.dashboardStats
+}
+
+export const getRegistrationTrend = async (days = 15) => {
+  const result = await graphqlRequest(GET_REGISTRATION_TREND_QUERY, { days })
+  return result.data.registrationTrend || []
+}
+
+export const getRegionRegistrationStats = async () => {
+  const result = await graphqlRequest(GET_REGION_REGISTRATION_STATS_QUERY)
+  return result.data.registrationRegionStats || []
+}
diff --git a/web/src/api/projectReviewNew.js b/web/src/api/projectReviewNew.js
index 5a2a8c7..223dd00 100644
--- a/web/src/api/projectReviewNew.js
+++ b/web/src/api/projectReviewNew.js
@@ -2,8 +2,8 @@
 
 // 鑾峰彇椤圭洰璇勫鍒楄〃锛堝寘鍚墍鏈夐樁娈垫暟鎹紝鍖呮嫭澶嶈禌銆佸喅璧涳級
 const GET_PROJECT_REVIEW_APPLICATIONS_QUERY = `
-  query GetProjectReviewApplications($activityId: ID, $page: Int, $size: Int) {
-    projectReviewApplications(activityId: $activityId, page: $page, size: $size) {
+  query GetProjectReviewApplications($activityId: ID, $regionId: ID, $name: String, $page: Int, $size: Int) {
+    projectReviewApplications(activityId: $activityId, regionId: $regionId, name: $name, page: $page, size: $size) {
       totalElements
       page
       size
@@ -30,4 +30,4 @@
 
 export default {
   getProjectReviewApplications
-}
\ No newline at end of file
+}
diff --git a/web/src/api/region.js b/web/src/api/region.js
index d78274b..6a134e2 100644
--- a/web/src/api/region.js
+++ b/web/src/api/region.js
@@ -1,6 +1,6 @@
 import { API_CONFIG, graphqlRequest } from '@/config/api'
 
-// GraphQL璇锋眰鍑芥暟 - 浣跨敤缁熶竴鐨刧raphqlRequest
+// GraphQL 璇锋眰鍑芥暟 - 缁熶竴浣跨敤 graphqlRequest
 
 // 鑾峰彇鎵�鏈夊湴鍖�
 export const getRegions = async () => {
@@ -15,7 +15,7 @@
       }
     }
   `
-  
+
   try {
     const result = await graphqlRequest(query)
     return result.data.regions || []
@@ -24,7 +24,7 @@
   }
 }
 
-// 鏍规嵁ID鑾峰彇鍦板尯
+// 鏍规嵁 ID 鑾峰彇鍦板尯璇︽儏
 export const getRegion = async (id) => {
   const query = `
     query GetRegion($id: ID!) {
@@ -37,7 +37,7 @@
       }
     }
   `
-  
+
   try {
     const result = await graphqlRequest(query, { id })
     return result.data.region
@@ -59,7 +59,7 @@
       }
     }
   `
-  
+
   try {
     const data = await graphqlRequest(mutation, { input: regionData })
     return data.saveRegion
@@ -75,7 +75,7 @@
       deleteRegion(id: $id)
     }
   `
-  
+
   try {
     const data = await graphqlRequest(mutation, { id })
     return data.deleteRegion
@@ -96,7 +96,7 @@
       }
     }
   `
-  
+
   try {
     const result = await graphqlRequest(query)
     return result.data.provinces || []
@@ -105,7 +105,7 @@
   }
 }
 
-// 鏍规嵁鐪佷唤ID鑾峰彇鍩庡競
+// 鏍规嵁鐪佷唤 ID 鑾峰彇鍩庡競
 export const getCities = async (provinceId) => {
   const query = `
     query GetCities($provinceId: ID!) {
@@ -117,7 +117,7 @@
       }
     }
   `
-  
+
   try {
     const result = await graphqlRequest(query, { provinceId })
     return result.data.cities || []
@@ -126,7 +126,7 @@
   }
 }
 
-// 鏍规嵁鍩庡競ID鑾峰彇鍖哄幙
+// 鏍规嵁鍩庡競 ID 鑾峰彇鍖哄幙
 export const getDistricts = async (cityId) => {
   const query = `
     query GetDistricts($cityId: ID!) {
@@ -138,7 +138,7 @@
       }
     }
   `
-  
+
   try {
     const result = await graphqlRequest(query, { cityId })
     return result.data.districts || []
@@ -147,7 +147,27 @@
   }
 }
 
-// RegionApi 瀵硅薄锛屽寘鍚墍鏈夊尯鍩熺浉鍏崇殑API鏂规硶
+// 鑾峰彇鍙跺瓙鍦板尯鍒楄〃
+export const getLeafRegions = async () => {
+  const query = `
+    query LeafRegions {
+      leafRegions {
+        id
+        name
+        leafFlag
+      }
+    }
+  `
+
+  try {
+    const result = await graphqlRequest(query)
+    return result.data.leafRegions || []
+  } catch (error) {
+    throw new Error(error.message || '鑾峰彇鍙跺瓙鍦板尯鍒楄〃澶辫触')
+  }
+}
+
+// RegionApi 瀵硅薄锛屽寘鍚墍鏈夊尯鍩熺浉鍏崇殑 API 鏂规硶
 export const RegionApi = {
   getRegions,
   getRegion,
@@ -155,5 +175,6 @@
   deleteRegion,
   getProvinces,
   getCities,
-  getDistricts
-}
\ No newline at end of file
+  getDistricts,
+  getLeafRegions
+}
diff --git a/web/src/utils/appConfig.js b/web/src/utils/appConfig.js
index 48c75f0..5b47beb 100644
--- a/web/src/utils/appConfig.js
+++ b/web/src/utils/appConfig.js
@@ -1,25 +1,25 @@
-import { graphqlRequest } from '../config/api.ts';
-
-const GET_APP_CONFIG = `
-  query AppConfig {
-    appConfig {
-      mediaBaseUrl
-    }
-  }
-`;
-
-export async function loadAppConfig() {
-  try {
-    const result = await graphqlRequest(GET_APP_CONFIG);
-    const mediaBaseUrl = result.data?.appConfig?.mediaBaseUrl || '';
-    // 浣滀负鍏ㄥ眬鍙橀噺鏆撮湶
-    window.__APP_MEDIA_BASE_URL__ = mediaBaseUrl;
-    return mediaBaseUrl;
-  } catch (e) {
-    // 濡傛灉GraphQL鏌ヨ澶辫触锛屼娇鐢ㄩ粯璁ら厤缃�
-    console.warn('loadAppConfig failed, using default config:', e?.message || e);
-    const defaultMediaBaseUrl = 'http://localhost:8080';
-    window.__APP_MEDIA_BASE_URL__ = defaultMediaBaseUrl;
-    return defaultMediaBaseUrl;
-  }
+import { graphqlRequest } from '../config/api.ts';

+

+const GET_APP_CONFIG = `

+  query AppConfig {

+    appConfig {

+      mediaBaseUrl

+    }

+  }

+`;

+

+export async function loadAppConfig() {

+  try {

+    const result = await graphqlRequest(GET_APP_CONFIG);

+    const mediaBaseUrl = result.data?.appConfig?.mediaBaseUrl || '';

+    // 浣滀负鍏ㄥ眬鍙橀噺鏆撮湶

+    window.__APP_MEDIA_BASE_URL__ = mediaBaseUrl;

+    return mediaBaseUrl;

+  } catch (e) {

+    // 濡傛灉GraphQL鏌ヨ澶辫触锛屼娇鐢ㄩ粯璁ら厤缃�

+    console.warn('loadAppConfig failed, using default config:', e?.message || e);

+    const defaultMediaBaseUrl = 'http://localhost:8080';

+    window.__APP_MEDIA_BASE_URL__ = defaultMediaBaseUrl;

+    return defaultMediaBaseUrl;

+  }

 }
\ No newline at end of file
diff --git a/web/src/utils/cos-config.ts b/web/src/utils/cos-config.ts
index c7e9576..9ad1ec7 100644
--- a/web/src/utils/cos-config.ts
+++ b/web/src/utils/cos-config.ts
@@ -1,75 +1,75 @@
-// 鑵捐浜慍OS閰嶇疆鏂囦欢
-// 娉ㄦ剰锛氳繖涓枃浠跺寘鍚晱鎰熶俊鎭紝鍦ㄧ敓浜х幆澧冧腑搴旇閫氳繃鐜鍙橀噺鎴栧悗绔疉PI鑾峰彇
-
-export interface COSConfig {
-  Region: string
-  Bucket: string
-  SecretId?: string
-  SecretKey?: string
-  SecurityToken?: string
-}
-
-// 寮�鍙戠幆澧冮厤缃紙璇锋浛鎹负瀹為檯鐨勯厤缃俊鎭級
-export const DEV_COS_CONFIG: COSConfig = {
-  Region: 'ap-chengdu', // 鎴愰兘鍦板煙
-  Bucket: 'ryc-media-1234567890', // 璇锋浛鎹负瀹為檯鐨勫瓨鍌ㄦ《鍚嶇О
-  // 娉ㄦ剰锛氬湪鐢熶骇鐜涓紝涓嶅簲璇ュ湪鍓嶇浠g爜涓洿鎺ュ啓鍏ュ瘑閽�
-  // 搴旇閫氳繃鍚庣鎺ュ彛鑾峰彇涓存椂瀵嗛挜
-  SecretId: 'AKID_YOUR_SECRET_ID', // 璇锋浛鎹负瀹為檯鐨凷ecretId
-  SecretKey: 'YOUR_SECRET_KEY', // 璇锋浛鎹负瀹為檯鐨凷ecretKey
-}
-
-// 鐢熶骇鐜搴旇閫氳繃鍚庣API鑾峰彇涓存椂瀵嗛挜
-export const getTemporaryCredentials = async (): Promise<{
-  TmpSecretId: string
-  TmpSecretKey: string
-  SecurityToken: string
-  StartTime: number
-  ExpiredTime: number
-}> => {
-  // 杩欓噷搴旇璋冪敤鍚庣API鑾峰彇涓存椂瀵嗛挜
-  // 绀轰緥锛�
-  // const response = await fetch('/api/cos/credentials')
-  // return response.json()
-  
-  // 涓存椂杩斿洖妯℃嫙鏁版嵁锛堜粎鐢ㄤ簬寮�鍙戞祴璇曪級
-  return {
-    TmpSecretId: DEV_COS_CONFIG.SecretId || '',
-    TmpSecretKey: DEV_COS_CONFIG.SecretKey || '',
-    SecurityToken: DEV_COS_CONFIG.SecurityToken || '',
-    StartTime: Math.round(Date.now() / 1000),
-    ExpiredTime: Math.round(Date.now() / 1000) + 1800, // 30鍒嗛挓鍚庤繃鏈�
-  }
-}
-
-// 鏂囦欢瀛樺偍鐩綍閰嶇疆
-export const STORAGE_FOLDERS = {
-  AVATARS: 'avatars/',
-  DOCUMENTS: 'documents/',
-  IMAGES: 'images/',
-  VIDEOS: 'videos/',
-  AUDIOS: 'audios/',
-  OTHERS: 'others/'
-} as const
-
-// 鏂囦欢澶у皬闄愬埗锛堝瓧鑺傦級
-export const FILE_SIZE_LIMITS = {
-  IMAGE: 10 * 1024 * 1024, // 10MB
-  VIDEO: 100 * 1024 * 1024, // 100MB
-  AUDIO: 300 * 1024 * 1024, // 300MB
-  DOCUMENT: 20 * 1024 * 1024, // 20MB
-  DEFAULT: 10 * 1024 * 1024 // 10MB
-} as const
-
-// 鏀寔鐨勬枃浠剁被鍨�
-export const SUPPORTED_FILE_TYPES = {
-  IMAGE: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
-  VIDEO: ['video/mp4', 'video/avi', 'video/mov', 'video/wmv'],
-  AUDIO: ['audio/mp3', 'audio/wav', 'audio/aac', 'audio/ogg'],
-  DOCUMENT: [
-    'application/pdf',
-    'application/msword',
-    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
-    'text/plain'
-  ]
+// 鑵捐浜慍OS閰嶇疆鏂囦欢

+// 娉ㄦ剰锛氳繖涓枃浠跺寘鍚晱鎰熶俊鎭紝鍦ㄧ敓浜х幆澧冧腑搴旇閫氳繃鐜鍙橀噺鎴栧悗绔疉PI鑾峰彇

+

+export interface COSConfig {

+  Region: string

+  Bucket: string

+  SecretId?: string

+  SecretKey?: string

+  SecurityToken?: string

+}

+

+// 寮�鍙戠幆澧冮厤缃紙璇锋浛鎹负瀹為檯鐨勯厤缃俊鎭級

+export const DEV_COS_CONFIG: COSConfig = {

+  Region: 'ap-chengdu', // 鎴愰兘鍦板煙

+  Bucket: 'ryc-media-1234567890', // 璇锋浛鎹负瀹為檯鐨勫瓨鍌ㄦ《鍚嶇О

+  // 娉ㄦ剰锛氬湪鐢熶骇鐜涓紝涓嶅簲璇ュ湪鍓嶇浠g爜涓洿鎺ュ啓鍏ュ瘑閽�

+  // 搴旇閫氳繃鍚庣鎺ュ彛鑾峰彇涓存椂瀵嗛挜

+  SecretId: 'AKID_YOUR_SECRET_ID', // 璇锋浛鎹负瀹為檯鐨凷ecretId

+  SecretKey: 'YOUR_SECRET_KEY', // 璇锋浛鎹负瀹為檯鐨凷ecretKey

+}

+

+// 鐢熶骇鐜搴旇閫氳繃鍚庣API鑾峰彇涓存椂瀵嗛挜

+export const getTemporaryCredentials = async (): Promise<{

+  TmpSecretId: string

+  TmpSecretKey: string

+  SecurityToken: string

+  StartTime: number

+  ExpiredTime: number

+}> => {

+  // 杩欓噷搴旇璋冪敤鍚庣API鑾峰彇涓存椂瀵嗛挜

+  // 绀轰緥锛�

+  // const response = await fetch('/api/cos/credentials')

+  // return response.json()

+  

+  // 涓存椂杩斿洖妯℃嫙鏁版嵁锛堜粎鐢ㄤ簬寮�鍙戞祴璇曪級

+  return {

+    TmpSecretId: DEV_COS_CONFIG.SecretId || '',

+    TmpSecretKey: DEV_COS_CONFIG.SecretKey || '',

+    SecurityToken: DEV_COS_CONFIG.SecurityToken || '',

+    StartTime: Math.round(Date.now() / 1000),

+    ExpiredTime: Math.round(Date.now() / 1000) + 1800, // 30鍒嗛挓鍚庤繃鏈�

+  }

+}

+

+// 鏂囦欢瀛樺偍鐩綍閰嶇疆

+export const STORAGE_FOLDERS = {

+  AVATARS: 'avatars/',

+  DOCUMENTS: 'documents/',

+  IMAGES: 'images/',

+  VIDEOS: 'videos/',

+  AUDIOS: 'audios/',

+  OTHERS: 'others/'

+} as const

+

+// 鏂囦欢澶у皬闄愬埗锛堝瓧鑺傦級

+export const FILE_SIZE_LIMITS = {

+  IMAGE: 10 * 1024 * 1024, // 10MB

+  VIDEO: 100 * 1024 * 1024, // 100MB

+  AUDIO: 300 * 1024 * 1024, // 300MB

+  DOCUMENT: 20 * 1024 * 1024, // 20MB

+  DEFAULT: 10 * 1024 * 1024 // 10MB

+} as const

+

+// 鏀寔鐨勬枃浠剁被鍨�

+export const SUPPORTED_FILE_TYPES = {

+  IMAGE: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],

+  VIDEO: ['video/mp4', 'video/avi', 'video/mov', 'video/wmv'],

+  AUDIO: ['audio/mp3', 'audio/wav', 'audio/aac', 'audio/ogg'],

+  DOCUMENT: [

+    'application/pdf',

+    'application/msword',

+    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',

+    'text/plain'

+  ]

 } as const
\ No newline at end of file
diff --git a/web/src/utils/cos-simple.ts b/web/src/utils/cos-simple.ts
index 24a983d..6f3e835 100644
--- a/web/src/utils/cos-simple.ts
+++ b/web/src/utils/cos-simple.ts
@@ -1,65 +1,65 @@
-import axios from 'axios'
-
-// GraphQL鏌ヨ鑾峰彇涓婁紶鍑瘉
-const GET_UPLOAD_CREDENTIALS = `
-  query GetUploadCredentials {
-    getUploadCredentials {
-      bucket
-      region
-      key
-      presignedUrl
-      expiration
-    }
-  }
-`
-
-// 浠庡悗绔疓raphQL鑾峰彇涓婁紶鍑瘉
-const getUploadCredentials = async () => {
-  try {
-    const response = await axios.post('http://localhost:8080/graphql', {
-      query: GET_UPLOAD_CREDENTIALS
-    })
-    
-    if (response.data.errors) {
-      throw new Error(response.data.errors[0].message)
-    }
-    
-    return response.data.data.getUploadCredentials
-  } catch (error) {
-    console.error('鑾峰彇涓婁紶鍑瘉澶辫触:', error)
-    throw error
-  }
-}
-
-/**
- * 浣跨敤棰勭鍚峌RL涓婁紶鏂囦欢鍒拌吘璁簯COS
- * @param file 瑕佷笂浼犵殑鏂囦欢
- * @returns Promise<string> 杩斿洖鏂囦欢鐨勮闂甎RL
- */
-export const uploadToCOS = async (file: File): Promise<string> => {
-  try {
-    // 鑾峰彇涓婁紶鍑瘉
-    const credentials = await getUploadCredentials()
-    
-    // 浣跨敤棰勭鍚峌RL
-    const uploadUrl = credentials.presignedUrl
-    
-    // 浣跨敤棰勭鍚峌RL涓婁紶鏂囦欢
-    const uploadResponse = await axios.put(uploadUrl, file, {
-      headers: {
-        'Content-Type': file.type,
-      }
-    })
-    
-    if (uploadResponse.status === 200) {
-      // 杩斿洖鏂囦欢鐨勮闂甎RL锛堝幓鎺夋煡璇㈠弬鏁帮級
-      const fileUrl = `https://${credentials.bucket}.cos.${credentials.region}.myqcloud.com/${credentials.key}`
-      return fileUrl
-    } else {
-      throw new Error(`涓婁紶澶辫触锛岀姸鎬佺爜: ${uploadResponse.status}`)
-    }
-    
-  } catch (error) {
-    throw error
-  }
+import axios from 'axios'

+

+// GraphQL鏌ヨ鑾峰彇涓婁紶鍑瘉

+const GET_UPLOAD_CREDENTIALS = `

+  query GetUploadCredentials {

+    getUploadCredentials {

+      bucket

+      region

+      key

+      presignedUrl

+      expiration

+    }

+  }

+`

+

+// 浠庡悗绔疓raphQL鑾峰彇涓婁紶鍑瘉

+const getUploadCredentials = async () => {

+  try {

+    const response = await axios.post('http://localhost:8080/graphql', {

+      query: GET_UPLOAD_CREDENTIALS

+    })

+    

+    if (response.data.errors) {

+      throw new Error(response.data.errors[0].message)

+    }

+    

+    return response.data.data.getUploadCredentials

+  } catch (error) {

+    console.error('鑾峰彇涓婁紶鍑瘉澶辫触:', error)

+    throw error

+  }

+}

+

+/**

+ * 浣跨敤棰勭鍚峌RL涓婁紶鏂囦欢鍒拌吘璁簯COS

+ * @param file 瑕佷笂浼犵殑鏂囦欢

+ * @returns Promise<string> 杩斿洖鏂囦欢鐨勮闂甎RL

+ */

+export const uploadToCOS = async (file: File): Promise<string> => {

+  try {

+    // 鑾峰彇涓婁紶鍑瘉

+    const credentials = await getUploadCredentials()

+    

+    // 浣跨敤棰勭鍚峌RL

+    const uploadUrl = credentials.presignedUrl

+    

+    // 浣跨敤棰勭鍚峌RL涓婁紶鏂囦欢

+    const uploadResponse = await axios.put(uploadUrl, file, {

+      headers: {

+        'Content-Type': file.type,

+      }

+    })

+    

+    if (uploadResponse.status === 200) {

+      // 杩斿洖鏂囦欢鐨勮闂甎RL锛堝幓鎺夋煡璇㈠弬鏁帮級

+      const fileUrl = `https://${credentials.bucket}.cos.${credentials.region}.myqcloud.com/${credentials.key}`

+      return fileUrl

+    } else {

+      throw new Error(`涓婁紶澶辫触锛岀姸鎬佺爜: ${uploadResponse.status}`)

+    }

+    

+  } catch (error) {

+    throw error

+  }

 }
\ No newline at end of file
diff --git a/web/src/utils/cos.ts b/web/src/utils/cos.ts
index b37e3b2..2a61394 100644
--- a/web/src/utils/cos.ts
+++ b/web/src/utils/cos.ts
@@ -1,162 +1,162 @@
-import COS from 'cos-js-sdk-v5'
-import axios from 'axios'
-
-// 浠庡悗绔幏鍙栦复鏃跺瘑閽�
-const getCredentialsFromBackend = async () => {
-  try {
-    const response = await axios.get('http://localhost:8080/api/cos/credentials')
-    return response.data
-  } catch (error) {
-    throw error
-  }
-}
-
-// 鍒涘缓COS瀹炰緥
-const cos = new COS({
-  getAuthorization: async function (options: any, callback: any) {
-    try {
-      console.log('姝e湪浠庡悗绔幏鍙朇OS涓存椂瀵嗛挜...')
-      // 浠庡悗绔幏鍙栦复鏃跺瘑閽�
-      const credentials = await getCredentialsFromBackend()
-      
-      console.log('鎴愬姛鑾峰彇涓存椂瀵嗛挜:', {
-        TmpSecretId: credentials.TmpSecretId?.substring(0, 10) + '...',
-        bucket: credentials.config?.bucket,
-        region: credentials.config?.region
-      })
-      
-      callback({
-        TmpSecretId: credentials.TmpSecretId,
-        TmpSecretKey: credentials.TmpSecretKey,
-        SecurityToken: credentials.SecurityToken,
-        StartTime: credentials.StartTime,
-        ExpiredTime: credentials.ExpiredTime,
-      })
-    } catch (error) {
-      console.error('鑾峰彇涓存椂瀵嗛挜澶辫触:', error)
-      callback(error)
-    }
-  }
-})
-
-// 鑾峰彇COS閰嶇疆淇℃伅
-export const getCOSConfig = async () => {
-  try {
-    const response = await axios.get('http://localhost:8080/api/cos/config')
-    return response.data
-  } catch (error) {
-    console.error('鑾峰彇COS閰嶇疆澶辫触:', error)
-    throw error
-  }
-}
-
-/**
- * 涓婁紶鏂囦欢鍒拌吘璁簯COS
- * @param file 瑕佷笂浼犵殑鏂囦欢
- * @param folder 瀛樺偍鏂囦欢澶硅矾寰勶紝濡� 'avatars/', 'documents/'
- * @returns Promise<string> 杩斿洖鏂囦欢鐨勮闂甎RL
- */
-export const uploadToCOS = async (file: File, folder: string = ''): Promise<string> => {
-  try {
-    // 鑾峰彇COS閰嶇疆
-    const config = await getCOSConfig()
-    
-    // 鐢熸垚鍞竴鏂囦欢鍚�
-    const timestamp = Date.now()
-    const randomStr = Math.random().toString(36).substring(2, 8)
-    const fileExt = file.name.split('.').pop()
-    const fileName = `${folder}${timestamp}_${randomStr}.${fileExt}`
-    
-    console.log('寮�濮嬩笂浼犳枃浠�:', fileName, '鍒板瓨鍌ㄦ《:', config.bucket)
-    
-    return new Promise((resolve, reject) => {
-      cos.uploadFile({
-        Bucket: config.bucket,
-        Region: config.region,
-        Key: fileName,
-        Body: file,
-        SliceSize: 1024 * 1024 * 5, // 澶т簬5MB鐨勬枃浠朵娇鐢ㄥ垎鍧椾笂浼�
-        onProgress: (progressData) => {
-          console.log('涓婁紶杩涘害:', Math.round(progressData.percent * 100) + '%')
-        }
-      }, (err, data) => {
-        if (err) {
-          console.error('涓婁紶澶辫触:', err)
-          reject(err)
-        } else {
-          console.log('涓婁紶鎴愬姛:', data)
-          // 杩斿洖鏂囦欢鐨勮闂甎RL
-          const fileUrl = `https://${data.Location}`
-          resolve(fileUrl)
-        }
-      })
-    })
-  } catch (error) {
-    console.error('涓婁紶鏂囦欢澶辫触:', error)
-    throw error
-  }
-}
-
-/**
- * 鍒犻櫎COS涓殑鏂囦欢
- * @param key 鏂囦欢鐨凨ey锛堣矾寰勶級
- * @returns Promise<boolean>
- */
-export const deleteFromCOS = async (key: string): Promise<boolean> => {
-  try {
-    const config = await getCOSConfig()
-    
-    return new Promise((resolve, reject) => {
-      cos.deleteObject({
-        Bucket: config.bucket,
-        Region: config.region,
-        Key: key
-      }, (err, data) => {
-        if (err) {
-          console.error('鍒犻櫎澶辫触:', err)
-          reject(err)
-        } else {
-          console.log('鍒犻櫎鎴愬姛:', data)
-          resolve(true)
-        }
-      })
-    })
-  } catch (error) {
-    console.error('鍒犻櫎鏂囦欢澶辫触:', error)
-    throw error
-  }
-}
-
-/**
- * 鑾峰彇鏂囦欢鐨勪复鏃惰闂甎RL锛堢敤浜庣鏈夎鍙栫殑鏂囦欢锛�
- * @param key 鏂囦欢鐨凨ey锛堣矾寰勶級
- * @param expires 杩囨湡鏃堕棿锛堢锛夛紝榛樿1灏忔椂
- * @returns Promise<string>
- */
-export const getObjectUrl = async (key: string, expires: number = 3600): Promise<string> => {
-  try {
-    const config = await getCOSConfig()
-    
-    return new Promise((resolve, reject) => {
-      cos.getObjectUrl({
-        Bucket: config.bucket,
-        Region: config.region,
-        Key: key,
-        Expires: expires,
-        Sign: true
-      }, (err, data) => {
-        if (err) {
-          console.error('鑾峰彇URL澶辫触:', err)
-          reject(err)
-        } else {
-          resolve(data.Url)
-        }
-      })
-    })
-  } catch (error) {
-    console.error('鑾峰彇鏂囦欢URL澶辫触:', error)
-    throw error
-  }
-}
-
+import COS from 'cos-js-sdk-v5'

+import axios from 'axios'

+

+// 浠庡悗绔幏鍙栦复鏃跺瘑閽�

+const getCredentialsFromBackend = async () => {

+  try {

+    const response = await axios.get('http://localhost:8080/api/cos/credentials')

+    return response.data

+  } catch (error) {

+    throw error

+  }

+}

+

+// 鍒涘缓COS瀹炰緥

+const cos = new COS({

+  getAuthorization: async function (options: any, callback: any) {

+    try {

+      console.log('姝e湪浠庡悗绔幏鍙朇OS涓存椂瀵嗛挜...')

+      // 浠庡悗绔幏鍙栦复鏃跺瘑閽�

+      const credentials = await getCredentialsFromBackend()

+      

+      console.log('鎴愬姛鑾峰彇涓存椂瀵嗛挜:', {

+        TmpSecretId: credentials.TmpSecretId?.substring(0, 10) + '...',

+        bucket: credentials.config?.bucket,

+        region: credentials.config?.region

+      })

+      

+      callback({

+        TmpSecretId: credentials.TmpSecretId,

+        TmpSecretKey: credentials.TmpSecretKey,

+        SecurityToken: credentials.SecurityToken,

+        StartTime: credentials.StartTime,

+        ExpiredTime: credentials.ExpiredTime,

+      })

+    } catch (error) {

+      console.error('鑾峰彇涓存椂瀵嗛挜澶辫触:', error)

+      callback(error)

+    }

+  }

+})

+

+// 鑾峰彇COS閰嶇疆淇℃伅

+export const getCOSConfig = async () => {

+  try {

+    const response = await axios.get('http://localhost:8080/api/cos/config')

+    return response.data

+  } catch (error) {

+    console.error('鑾峰彇COS閰嶇疆澶辫触:', error)

+    throw error

+  }

+}

+

+/**

+ * 涓婁紶鏂囦欢鍒拌吘璁簯COS

+ * @param file 瑕佷笂浼犵殑鏂囦欢

+ * @param folder 瀛樺偍鏂囦欢澶硅矾寰勶紝濡� 'avatars/', 'documents/'

+ * @returns Promise<string> 杩斿洖鏂囦欢鐨勮闂甎RL

+ */

+export const uploadToCOS = async (file: File, folder: string = ''): Promise<string> => {

+  try {

+    // 鑾峰彇COS閰嶇疆

+    const config = await getCOSConfig()

+    

+    // 鐢熸垚鍞竴鏂囦欢鍚�

+    const timestamp = Date.now()

+    const randomStr = Math.random().toString(36).substring(2, 8)

+    const fileExt = file.name.split('.').pop()

+    const fileName = `${folder}${timestamp}_${randomStr}.${fileExt}`

+    

+    console.log('寮�濮嬩笂浼犳枃浠�:', fileName, '鍒板瓨鍌ㄦ《:', config.bucket)

+    

+    return new Promise((resolve, reject) => {

+      cos.uploadFile({

+        Bucket: config.bucket,

+        Region: config.region,

+        Key: fileName,

+        Body: file,

+        SliceSize: 1024 * 1024 * 5, // 澶т簬5MB鐨勬枃浠朵娇鐢ㄥ垎鍧椾笂浼�

+        onProgress: (progressData) => {

+          console.log('涓婁紶杩涘害:', Math.round(progressData.percent * 100) + '%')

+        }

+      }, (err, data) => {

+        if (err) {

+          console.error('涓婁紶澶辫触:', err)

+          reject(err)

+        } else {

+          console.log('涓婁紶鎴愬姛:', data)

+          // 杩斿洖鏂囦欢鐨勮闂甎RL

+          const fileUrl = `https://${data.Location}`

+          resolve(fileUrl)

+        }

+      })

+    })

+  } catch (error) {

+    console.error('涓婁紶鏂囦欢澶辫触:', error)

+    throw error

+  }

+}

+

+/**

+ * 鍒犻櫎COS涓殑鏂囦欢

+ * @param key 鏂囦欢鐨凨ey锛堣矾寰勶級

+ * @returns Promise<boolean>

+ */

+export const deleteFromCOS = async (key: string): Promise<boolean> => {

+  try {

+    const config = await getCOSConfig()

+    

+    return new Promise((resolve, reject) => {

+      cos.deleteObject({

+        Bucket: config.bucket,

+        Region: config.region,

+        Key: key

+      }, (err, data) => {

+        if (err) {

+          console.error('鍒犻櫎澶辫触:', err)

+          reject(err)

+        } else {

+          console.log('鍒犻櫎鎴愬姛:', data)

+          resolve(true)

+        }

+      })

+    })

+  } catch (error) {

+    console.error('鍒犻櫎鏂囦欢澶辫触:', error)

+    throw error

+  }

+}

+

+/**

+ * 鑾峰彇鏂囦欢鐨勪复鏃惰闂甎RL锛堢敤浜庣鏈夎鍙栫殑鏂囦欢锛�

+ * @param key 鏂囦欢鐨凨ey锛堣矾寰勶級

+ * @param expires 杩囨湡鏃堕棿锛堢锛夛紝榛樿1灏忔椂

+ * @returns Promise<string>

+ */

+export const getObjectUrl = async (key: string, expires: number = 3600): Promise<string> => {

+  try {

+    const config = await getCOSConfig()

+    

+    return new Promise((resolve, reject) => {

+      cos.getObjectUrl({

+        Bucket: config.bucket,

+        Region: config.region,

+        Key: key,

+        Expires: expires,

+        Sign: true

+      }, (err, data) => {

+        if (err) {

+          console.error('鑾峰彇URL澶辫触:', err)

+          reject(err)

+        } else {

+          resolve(data.Url)

+        }

+      })

+    })

+  } catch (error) {

+    console.error('鑾峰彇鏂囦欢URL澶辫触:', error)

+    throw error

+  }

+}

+

 export default cos
\ No newline at end of file
diff --git a/web/src/utils/upload-logo-browser.ts b/web/src/utils/upload-logo-browser.ts
index c685217..d930b73 100644
--- a/web/src/utils/upload-logo-browser.ts
+++ b/web/src/utils/upload-logo-browser.ts
@@ -1,112 +1,112 @@
-// 娴忚鍣ㄧ涓婁紶logo鏂囦欢鐨勫伐鍏峰嚱鏁�
-import { uploadToCOS } from './cos'
-
-/**
- * 灏哢I鐩綍涓嬬殑logo鏂囦欢涓婁紶鍒癈OS
- * 杩欎釜鍑芥暟鐢ㄤ簬鍦ㄦ祻瑙堝櫒涓笂浼爈ogo鏂囦欢
- */
-export const uploadLogoToCOS = async (): Promise<{
-  success: boolean
-  url?: string
-  error?: string
-}> => {
-  try {
-    // 鑾峰彇logo鏂囦欢
-    const logoFile = await fetchLogoFile()
-    
-    if (!logoFile) {
-      return {
-        success: false,
-        error: 'Logo鏂囦欢涓嶅瓨鍦ㄦ垨鏃犳硶璁块棶'
-      }
-    }
-    
-    // 涓婁紶鍒癈OS鐨刟vatars鐩綍
-    const url = await uploadToCOS(logoFile, 'avatars/')
-    
-    return {
-      success: true,
-      url: url
-    }
-    
-  } catch (error: any) {
-    console.error('Logo涓婁紶澶辫触:', error)
-    return {
-      success: false,
-      error: error.message || '涓婁紶澶辫触'
-    }
-  }
-}
-
-/**
- * 鑾峰彇logo鏂囦欢
- * 鐢变簬娴忚鍣ㄥ畨鍏ㄩ檺鍒讹紝鏃犳硶鐩存帴璇诲彇鏈湴鏂囦欢绯荤粺
- * 杩欓噷鎻愪緵鍑犵鑾峰彇logo鏂囦欢鐨勬柟寮�
- */
-async function fetchLogoFile(): Promise<File | null> {
-  try {
-    // 鏂瑰紡1: 浠巔ublic鐩綍鑾峰彇锛堝鏋渓ogo鏂囦欢鏀惧湪public鐩綍涓嬶級
-    const response = await fetch('/logo.jpg')
-    if (response.ok) {
-      const blob = await response.blob()
-      return new File([blob], 'logo.jpg', { type: 'image/jpeg' })
-    }
-  } catch (error) {
-    // 浠巔ublic鐩綍鑾峰彇logo澶辫触锛屽皾璇曞叾浠栨柟寮�
-  }
-  
-  try {
-    // 鏂瑰紡2: 浠嶶I鐩綍鑾峰彇锛堥渶瑕侀厤缃潤鎬佹枃浠舵湇鍔★級
-    const response = await fetch('/UI/logo.jpg')
-    if (response.ok) {
-      const blob = await response.blob()
-      return new File([blob], 'logo.jpg', { type: 'image/jpeg' })
-    }
-  } catch (error) {
-    // 浠嶶I鐩綍鑾峰彇logo澶辫触
-  }
-  
-  // 鏂瑰紡3: 杩斿洖null锛岄渶瑕佺敤鎴锋墜鍔ㄩ�夋嫨鏂囦欢
-  return null
-}
-
-/**
- * 鍒涘缓涓�涓猯ogo鏂囦欢閫夋嫨鍣�
- * 褰撴棤娉曡嚜鍔ㄨ幏鍙杔ogo鏂囦欢鏃讹紝璁╃敤鎴锋墜鍔ㄩ�夋嫨
- */
-export const createLogoFileSelector = (): Promise<File | null> => {
-  return new Promise((resolve) => {
-    const input = document.createElement('input')
-    input.type = 'file'
-    input.accept = 'image/*'
-    input.style.display = 'none'
-    
-    input.onchange = (event) => {
-      const target = event.target as HTMLInputElement
-      const file = target.files?.[0]
-      
-      if (file) {
-        // 楠岃瘉鏄惁涓簂ogo鏂囦欢
-        if (file.name.toLowerCase().includes('logo')) {
-          resolve(file)
-        } else {
-          console.warn('閫夋嫨鐨勬枃浠跺悕涓嶅寘鍚�"logo"锛岃纭鏄惁涓烘纭殑logo鏂囦欢')
-          resolve(file) // 浠嶇劧杩斿洖鏂囦欢锛岃鐢ㄦ埛鍐冲畾
-        }
-      } else {
-        resolve(null)
-      }
-      
-      // 娓呯悊DOM
-      document.body.removeChild(input)
-    }
-    
-    input.oncancel = () => {
-      resolve(null)
-      document.body.removeChild(input)
-    }
-    
-    document.body.appendChild(input)
-    input.click()
-  })
+// 娴忚鍣ㄧ涓婁紶logo鏂囦欢鐨勫伐鍏峰嚱鏁�

+import { uploadToCOS } from './cos'

+

+/**

+ * 灏哢I鐩綍涓嬬殑logo鏂囦欢涓婁紶鍒癈OS

+ * 杩欎釜鍑芥暟鐢ㄤ簬鍦ㄦ祻瑙堝櫒涓笂浼爈ogo鏂囦欢

+ */

+export const uploadLogoToCOS = async (): Promise<{

+  success: boolean

+  url?: string

+  error?: string

+}> => {

+  try {

+    // 鑾峰彇logo鏂囦欢

+    const logoFile = await fetchLogoFile()

+    

+    if (!logoFile) {

+      return {

+        success: false,

+        error: 'Logo鏂囦欢涓嶅瓨鍦ㄦ垨鏃犳硶璁块棶'

+      }

+    }

+    

+    // 涓婁紶鍒癈OS鐨刟vatars鐩綍

+    const url = await uploadToCOS(logoFile, 'avatars/')

+    

+    return {

+      success: true,

+      url: url

+    }

+    

+  } catch (error: any) {

+    console.error('Logo涓婁紶澶辫触:', error)

+    return {

+      success: false,

+      error: error.message || '涓婁紶澶辫触'

+    }

+  }

+}

+

+/**

+ * 鑾峰彇logo鏂囦欢

+ * 鐢变簬娴忚鍣ㄥ畨鍏ㄩ檺鍒讹紝鏃犳硶鐩存帴璇诲彇鏈湴鏂囦欢绯荤粺

+ * 杩欓噷鎻愪緵鍑犵鑾峰彇logo鏂囦欢鐨勬柟寮�

+ */

+async function fetchLogoFile(): Promise<File | null> {

+  try {

+    // 鏂瑰紡1: 浠巔ublic鐩綍鑾峰彇锛堝鏋渓ogo鏂囦欢鏀惧湪public鐩綍涓嬶級

+    const response = await fetch('/logo.jpg')

+    if (response.ok) {

+      const blob = await response.blob()

+      return new File([blob], 'logo.jpg', { type: 'image/jpeg' })

+    }

+  } catch (error) {

+    // 浠巔ublic鐩綍鑾峰彇logo澶辫触锛屽皾璇曞叾浠栨柟寮�

+  }

+  

+  try {

+    // 鏂瑰紡2: 浠嶶I鐩綍鑾峰彇锛堥渶瑕侀厤缃潤鎬佹枃浠舵湇鍔★級

+    const response = await fetch('/UI/logo.jpg')

+    if (response.ok) {

+      const blob = await response.blob()

+      return new File([blob], 'logo.jpg', { type: 'image/jpeg' })

+    }

+  } catch (error) {

+    // 浠嶶I鐩綍鑾峰彇logo澶辫触

+  }

+  

+  // 鏂瑰紡3: 杩斿洖null锛岄渶瑕佺敤鎴锋墜鍔ㄩ�夋嫨鏂囦欢

+  return null

+}

+

+/**

+ * 鍒涘缓涓�涓猯ogo鏂囦欢閫夋嫨鍣�

+ * 褰撴棤娉曡嚜鍔ㄨ幏鍙杔ogo鏂囦欢鏃讹紝璁╃敤鎴锋墜鍔ㄩ�夋嫨

+ */

+export const createLogoFileSelector = (): Promise<File | null> => {

+  return new Promise((resolve) => {

+    const input = document.createElement('input')

+    input.type = 'file'

+    input.accept = 'image/*'

+    input.style.display = 'none'

+    

+    input.onchange = (event) => {

+      const target = event.target as HTMLInputElement

+      const file = target.files?.[0]

+      

+      if (file) {

+        // 楠岃瘉鏄惁涓簂ogo鏂囦欢

+        if (file.name.toLowerCase().includes('logo')) {

+          resolve(file)

+        } else {

+          console.warn('閫夋嫨鐨勬枃浠跺悕涓嶅寘鍚�"logo"锛岃纭鏄惁涓烘纭殑logo鏂囦欢')

+          resolve(file) // 浠嶇劧杩斿洖鏂囦欢锛岃鐢ㄦ埛鍐冲畾

+        }

+      } else {

+        resolve(null)

+      }

+      

+      // 娓呯悊DOM

+      document.body.removeChild(input)

+    }

+    

+    input.oncancel = () => {

+      resolve(null)

+      document.body.removeChild(input)

+    }

+    

+    document.body.appendChild(input)

+    input.click()

+  })

 }
\ No newline at end of file
diff --git a/web/src/views/ActivityList.vue b/web/src/views/ActivityList.vue
deleted file mode 100644
index 5dc0f3e..0000000
--- a/web/src/views/ActivityList.vue
+++ /dev/null
@@ -1,206 +0,0 @@
-<template>
-  <div class="activity-list">
-    <el-card>
-      <div class="header">
-        <el-input
-          v-model="searchName"
-          placeholder="鎼滅储姣旇禌鍚嶇О"
-          style="width: 300px;"
-          @keyup.enter="handleSearch"
-          clearable
-        />
-        <el-button type="primary" @click="handleAdd">鏂板姣旇禌</el-button>
-      </div>
-
-      <el-table :data="tableData" style="width: 100%" v-loading="loading" size="small">
-        <el-table-column prop="name" label="姣旇禌鍚嶇О" min-width="180">
-          <template #default="{ row }">
-            <el-link type="primary" @click="handleView(row.id)">{{ row.name }}</el-link>
-          </template>
-        </el-table-column>
-        
-        <el-table-column prop="playerCount" label="鎶ュ悕浜烘暟" width="80" align="center">
-          <template #default="{ row }">
-            {{ row.playerCount || 0 }}
-          </template>
-        </el-table-column>
-        
-        <el-table-column prop="matchTime" label="姣旇禌鏃堕棿" width="140">
-          <template #default="{ row }">
-            <span style="font-size: 12px;">{{ formatDateTime(row.matchTime) }}</span>
-          </template>
-        </el-table-column>
-        
-        <el-table-column prop="signupDeadline" label="鎶ュ悕鎴" width="140">
-          <template #default="{ row }">
-            <span style="font-size: 12px;">{{ formatDateTime(row.signupDeadline) }}</span>
-          </template>
-        </el-table-column>
-        
-        <el-table-column prop="stateName" label="鐘舵��" width="80" align="center">
-          <template #default="{ row }">
-            <el-tag :type="getStateType(row.state)" size="small">{{ row.stateName }}</el-tag>
-          </template>
-        </el-table-column>
-        
-        <el-table-column label="鎿嶄綔" width="220" fixed="right" align="center">
-          <template #default="{ row }">
-            <el-button size="small" @click="handleView(row.id)">閫夋墜</el-button>
-            <el-button size="small" type="primary" @click="handleEdit(row.id)">缂栬緫</el-button>
-            <el-button size="small" type="danger" @click="handleDelete(row.id)">鍒犻櫎</el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <div class="pagination">
-        <el-pagination
-          v-model:current-page="currentPage"
-          v-model:page-size="pageSize"
-          :page-sizes="[10, 20, 50, 100]"
-          :total="total"
-          layout="total, sizes, prev, pager, next, jumper"
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange"
-        />
-      </div>
-    </el-card>
-  </div>
-</template>
-
-<script setup>
-import { ref, onMounted, watch } from 'vue'
-import { useRouter } from 'vue-router'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { getActivities, deleteActivity } from '@/api/activity'
-import dayjs from 'dayjs'
-
-const router = useRouter()
-
-// 鍝嶅簲寮忔暟鎹�
-const loading = ref(false)
-const tableData = ref([])
-const searchName = ref('')
-const currentPage = ref(1)
-const pageSize = ref(10)
-const total = ref(0)
-
-// 鎼滅储
-const handleSearch = () => {
-  currentPage.value = 1
-  loadData()
-}
-
-// 鐩戝惉鎼滅储妗嗗彉鍖�
-watch(searchName, () => {
-  if (!searchName.value) {
-    handleSearch()
-  }
-})
-
-// 鍔犺浇鏁版嵁
-const loadData = async () => {
-  try {
-    loading.value = true
-    const result = await getActivities(
-      currentPage.value - 1, 
-      pageSize.value, 
-      searchName.value
-    )
-    
-    console.log('=== ActivityList loadData ===');
-    console.log('getActivities杩斿洖鏁版嵁:', result);
-    tableData.value = result?.content || []
-    total.value = result?.totalElements || 0
-    console.log('璁剧疆tableData:', tableData.value);
-    console.log('璁剧疆total:', total.value);
-  } catch (error) {
-    console.error('鍔犺浇姣旇禌鍒楄〃澶辫触:', error)
-    ElMessage.error('鍔犺浇姣旇禌鍒楄〃澶辫触: ' + error.message)
-  } finally {
-    loading.value = false
-  }
-}
-
-// 鍒嗛〉澶勭悊
-const handleSizeChange = (val) => {
-  pageSize.value = val
-  currentPage.value = 1
-  loadData()
-}
-
-const handleCurrentChange = (val) => {
-  currentPage.value = val
-  loadData()
-}
-
-// 鎿嶄綔澶勭悊
-const handleAdd = () => {
-  router.push('/activity/form')
-}
-
-const handleEdit = (id) => {
-  router.push(`/activity/form/${id}`)
-}
-
-const handleView = (id) => {
-  router.push(`/activity/detail/${id}`)
-}
-
-const handleDelete = async (id) => {
-  try {
-    await ElMessageBox.confirm('纭畾瑕佸垹闄よ繖涓瘮璧涘悧锛�', '鎻愮ず', {
-      confirmButtonText: '纭畾',
-      cancelButtonText: '鍙栨秷',
-      type: 'warning',
-    })
-    
-    await deleteActivity(id)
-    ElMessage.success('鍒犻櫎鎴愬姛')
-    loadData()
-  } catch (error) {
-    if (error !== 'cancel') {
-      console.error('鍒犻櫎姣旇禌澶辫触:', error)
-      ElMessage.error('鍒犻櫎澶辫触: ' + error.message)
-    }
-  }
-}
-
-// 宸ュ叿鍑芥暟
-const formatDateTime = (dateTime) => {
-  if (!dateTime) return '-'
-  // 澶勭悊鍚庣杩斿洖鐨勬椂闂存牸寮� "yyyy-MM-dd HH:mm:ss"
-  return dayjs(dateTime).format('YYYY-MM-DD HH:mm')
-}
-
-const getStateType = (state) => {
-  switch (state) {
-    case 0: return 'info'
-    case 1: return 'success'
-    case 2: return 'danger'
-    default: return 'info'
-  }
-}
-
-// 鐢熷懡鍛ㄦ湡
-onMounted(() => {
-  loadData()
-})
-</script>
-
-<style scoped>
-.activity-list {
-  padding: 20px;
-}
-
-.header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 20px;
-}
-
-.pagination {
-  margin-top: 20px;
-  text-align: right;
-}
-</style>
\ No newline at end of file
diff --git a/web/src/views/activity-list.vue b/web/src/views/activity-list.vue
index 21af0a4..65b8f86 100644
--- a/web/src/views/activity-list.vue
+++ b/web/src/views/activity-list.vue
@@ -19,6 +19,20 @@
           @keyup.enter="handleSearch"
           @clear="handleClear"
         />
+        <el-select
+          v-model="searchForm.state"
+          placeholder="姣旇禌鐘舵��"
+          style="width: 160px"
+          clearable
+          @change="handleStateChange"
+        >
+          <el-option
+            v-for="option in stateOptions"
+            :key="option.value"
+            :label="option.label"
+            :value="option.value"
+          />
+        </el-select>
         <el-button type="primary" @click="handleSearch">
           <el-icon><Search /></el-icon>
           鏌ヨ
@@ -43,18 +57,18 @@
         <el-table-column label="鎿嶄綔" width="120" fixed="right" align="center">
           <template #default="{ row }">
             <div class="table-actions">
-              <el-button 
-                text 
-                :icon="Edit" 
-                size="small" 
+              <el-button
+                text
+                :icon="Edit"
+                size="small"
                 @click="handleEdit(row)"
                 class="action-btn edit-btn"
                 title="缂栬緫"
               />
-              <el-button 
-                text 
-                :icon="Delete" 
-                size="small" 
+              <el-button
+                text
+                :icon="Delete"
+                size="small"
                 @click="handleDelete(row)"
                 class="action-btn delete-btn"
                 title="鍒犻櫎"
@@ -84,18 +98,25 @@
 import { reactive, ref, onMounted, onActivated, watch } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { useRouter } from 'vue-router'
-import { getActivities } from '@/api/activity'
+import { getActivities, updateActivityState } from '@/api/activity'
 import { Search, Plus, Edit, Delete } from '@element-plus/icons-vue'
 
-console.log('=== activity-list.vue 缁勪欢寮�濮嬪姞杞� ===');
+console.log('=== activity-list.vue 缁勪欢寮�濮嬪姞杞� ===')
 
 const loading = ref(false)
 const router = useRouter()
 
 // 鎼滅储琛ㄥ崟
 const searchForm = reactive({
-  name: ''
+  name: '',
+  state: ''
 })
+
+const stateOptions = [
+  { label: '鏈彂甯�', value: 0 },
+  { label: '鍙戝竷', value: 1 },
+  { label: '鍏抽棴', value: 2 }
+]
 
 // 鍒嗛〉淇℃伅
 const pagination = reactive({
@@ -107,19 +128,26 @@
 // 琛ㄦ牸鏁版嵁
 const tableData = ref([])
 
-// 娣诲姞璋冭瘯锛氱洃鍚瑃ableData鍙樺寲
-watch(tableData, (newVal) => {
-  console.log('=== tableData 鍙戠敓鍙樺寲 ===', newVal)
-  console.log('tableData.value.length:', newVal.length)
-}, { deep: true })
+// 璋冭瘯鐢ㄩ�旓細鐩戝惉琛ㄦ牸鏁版嵁鍙樺寲
+watch(
+  tableData,
+  newVal => {
+    console.log('=== tableData 鍙戠敓鍙樺寲 ===', newVal)
+    console.log('tableData.value.length:', newVal.length)
+  },
+  { deep: true }
+)
 
 // 鑾峰彇鐘舵�佹爣绛剧被鍨�
 const getStatusType = (status: string) => {
   const typeMap: Record<string, string> = {
-    '杩涜涓�': 'success',
-    '鎶ュ悕涓�': 'warning',
-    '寰呭紑濮�': 'info',
-    '宸茬粨鏉�': 'info'
+    宸插彂甯�: 'success',
+    鍙戝竷: 'success',
+    杩涜涓�: 'success',
+    鎶ュ悕涓�: 'warning',
+    寰呭紑濮�: 'info',
+    宸茬粨鏉�: 'info',
+    鍏抽棴: 'danger'
   }
   return typeMap[status] || 'info'
 }
@@ -130,35 +158,45 @@
   loadData()
 }
 
+const handleStateChange = () => {
+  pagination.page = 1
+  loadData()
+}
+
 // 娓呯┖鎼滅储
 const handleClear = () => {
   searchForm.name = ''
   loadData()
 }
 
- // 鏂板姣旇禌
+// 鏂板姣旇禌
 const handleAdd = () => {
   router.push('/activity/new')
 }
 
- // 缂栬緫姣旇禌
+// 缂栬緫姣旇禌
 const handleEdit = (row: any) => {
   router.push(`/activity/edit/${row.id}`)
 }
 
- // 鍒犻櫎姣旇禌
+// 鍒犻櫎姣旇禌
 const handleDelete = async (row: any) => {
   try {
-    await ElMessageBox.confirm(`纭畾瑕佸垹闄ゆ瘮璧�"${row.name}"鍚楋紵`, '鎻愮ず', {
-      confirmButtonText: '纭畾',
-      cancelButtonText: '鍙栨秷',
-      type: 'warning'
-    })
-    
+    await ElMessageBox.confirm(
+      `纭畾瑕佸垹闄ゆ瘮璧� 鈥�${row.name}鈥� 鍚楋紵`,
+      '鎻愮ず',
+      {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }
+    )
+
+    await updateActivityState(row.id, 2)
     ElMessage.success('鍒犻櫎鎴愬姛')
     loadData()
   } catch {
-    // 鐢ㄦ埛鍙栨秷
+    // 鐢ㄦ埛鍙栨秷鎿嶄綔
   }
 }
 
@@ -178,20 +216,26 @@
 const loadData = async () => {
   loading.value = true
   try {
-    const data = await getActivities(pagination.page - 1, pagination.size, searchForm.name || '')
-    
-    // 鏁版嵁鏄犲皠锛氬皢API杩斿洖鐨勫瓧娈垫槧灏勫埌琛ㄦ牸鏈熸湜鐨勫瓧娈�
+    const keyword = (searchForm.name || '').trim()
+    const data = await getActivities(
+      pagination.page - 1,
+      pagination.size,
+      keyword,
+      searchForm.state
+    )
+
+    // 鏁版嵁鏄犲皠锛氬皢 API 杩斿洖瀛楁杞崲涓鸿〃鏍奸渶瑕佺殑瀛楁
     const mappedData = (data?.content || []).map(item => ({
       ...item,
       playerCount: item.playerCount || 0,
       stateName: item.stateName || ''
-    }));
-    
+    }))
+
     tableData.value = mappedData
     pagination.total = data?.totalElements || 0
-  } catch (e) {
-    console.error('鍔犺浇鏁版嵁澶辫触:', e);
-    ElMessage.error((e && e.message) ? e.message : '鍔犺浇姣旇禌鍒楄〃澶辫触')
+  } catch (e: any) {
+    console.error('鍔犺浇鏁版嵁澶辫触:', e)
+    ElMessage.error(e?.message || '鍔犺浇姣旇禌鍒楄〃澶辫触')
   } finally {
     loading.value = false
   }
@@ -201,7 +245,7 @@
   loadData()
 })
 
-// 椤甸潰婵�娲绘椂閲嶆柊鍔犺浇鏁版嵁锛堣В鍐充粠鏂板/缂栬緫椤甸潰杩斿洖鏃跺垪琛ㄤ笉鍒锋柊鐨勯棶棰橈級
+// 椤甸潰婵�娲绘椂閲嶆柊鍔犺浇鏁版嵁锛堣В鍐充粠鏂板/缂栬緫杩斿洖鍚庡垪琛ㄤ笉鍚屾鐨勯棶棰橈級
 onActivated(() => {
   loadData()
 })
@@ -264,7 +308,7 @@
   margin-bottom: 20px;
 }
 
-/* 鎼滅储妗嗘牱寮� */
+/* 鎼滅储鎸夐挳 */
 .search-icon {
   color: #999;
 }
@@ -320,15 +364,15 @@
     align-items: stretch;
     gap: 16px;
   }
-  
+
   .toolbar {
     flex-wrap: wrap;
     gap: 8px;
   }
-  
+
   .toolbar .el-input {
     width: 100% !important;
     max-width: 280px;
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/web/src/views/dashboard/index.vue b/web/src/views/dashboard/index.vue
index 05691f8..1254a5e 100644
--- a/web/src/views/dashboard/index.vue
+++ b/web/src/views/dashboard/index.vue
@@ -39,12 +39,31 @@
       </el-col>
     </el-row>
 
+    <div class="chart-section">
+      <div class="chart-card">
+        <div v-if="trendChartLoading" class="chart-mask">鍔犺浇涓�...</div>
+        <div class="chart-header">
+          <h3 class="chart-header-title">鎶ュ悕瓒嬪娍</h3>
+          <span class="chart-header-desc">鏈�杩� 15 澶�</span>
+        </div>
+        <div class="chart-container" ref="trendChartRef"></div>
+      </div>
+      <div class="chart-card">
+        <div v-if="regionChartLoading" class="chart-mask">鍔犺浇涓�...</div>
+        <div class="chart-header">
+          <h3 class="chart-header-title">鍖哄煙鎶ュ悕鍒嗗竷</h3>
+          <span class="chart-header-desc">鎸夊彾瀛愬湴鍖虹粺璁�</span>
+        </div>
+        <div class="chart-container" ref="regionChartRef"></div>
+      </div>
+    </div>
+
     <div class="table-card">
       <div class="table-header">
         <h3 class="table-title">鏈�杩戞瘮璧�</h3>
         <el-button type="primary" @click="$router.push('/activity')">鏌ョ湅鍏ㄩ儴</el-button>
       </div>
-      
+
       <el-table :data="recentActivities" class="recent-table">
         <el-table-column prop="name" label="姣旇禌鍚嶇О" width="180" />
         <el-table-column prop="playerCount" label="鎶ュ悕浜烘暟" width="120" />
@@ -66,14 +85,39 @@
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
 import { useRouter } from 'vue-router'
 import { ElMessage } from 'element-plus'
 import { Trophy, UserFilled, Clock, User } from '@element-plus/icons-vue'
-import { getDashboardStats } from '@/api/dashboard'
+import * as echarts from 'echarts'
+import { getDashboardStats, getRegistrationTrend, getRegionRegistrationStats } from '@/api/dashboard'
 import { getActivities } from '@/api/activity'
 
 const router = useRouter()
+
+interface RegistrationTrendPoint {
+  date: string
+  count: number
+}
+
+interface RegionRegistrationStat {
+  regionId: number | null
+  regionName: string
+  leafFlag?: boolean | null
+  count: number
+}
+
+const UNASSIGNED_REGION_LABEL = '鏈�夋嫨鍖哄煙'
+
+const registrationTrend = ref<RegistrationTrendPoint[]>([])
+const regionStats = ref<RegionRegistrationStat[]>([])
+const trendChartRef = ref<HTMLDivElement | null>(null)
+const regionChartRef = ref<HTMLDivElement | null>(null)
+const trendChartLoading = ref(false)
+const regionChartLoading = ref(false)
+
+let trendChartInstance: echarts.ECharts | null = null
+let regionChartInstance: echarts.ECharts | null = null
 
 // 缁熻鏁版嵁
 const stats = ref({
@@ -84,7 +128,7 @@
 })
 
 // 鏈�杩戞椿鍔ㄦ暟鎹�
-const recentActivities = ref([])
+const recentActivities = ref<any[]>([])
 
 // 鍔犺浇缁熻鏁版嵁
 const loadStats = async () => {
@@ -100,8 +144,8 @@
 // 鍔犺浇鏈�杩戞椿鍔�
 const loadRecentActivities = async () => {
   try {
-    const data = await getActivities(0, 5) // 鑾峰彇鍓�5鏉℃椿鍔�
-    const { content } = data || {};
+    const data = await getActivities(0, 5)
+    const { content } = data || {}
     recentActivities.value = (content || []).map(item => ({
       id: item.id,
       name: item.name,
@@ -116,13 +160,137 @@
   }
 }
 
+const renderTrendChart = () => {
+  if (!trendChartRef.value) return
+  if (!trendChartInstance) {
+    trendChartInstance = echarts.init(trendChartRef.value)
+  }
+
+  const dates = registrationTrend.value.map(item => item.date)
+  const values = registrationTrend.value.map(item => Number(item.count || 0))
+
+  trendChartInstance.setOption({
+    grid: { left: 16, right: 16, top: 32, bottom: 24, containLabel: true },
+    tooltip: { trigger: 'axis' },
+    xAxis: {
+      type: 'category',
+      boundaryGap: false,
+      data: dates
+    },
+    yAxis: {
+      type: 'value',
+      minInterval: 1
+    },
+    series: [
+      {
+        name: '鎶ュ悕鏁伴噺',
+        type: 'line',
+        smooth: true,
+        symbol: 'circle',
+        symbolSize: 8,
+        lineStyle: { width: 3, color: '#409EFF' },
+        areaStyle: { color: 'rgba(64, 158, 255, 0.2)' },
+        data: values
+      }
+    ]
+  })
+}
+
+const renderRegionChart = () => {
+  if (!regionChartRef.value) return
+  if (!regionChartInstance) {
+    regionChartInstance = echarts.init(regionChartRef.value)
+  }
+
+  const pieData = regionStats.value.map(item => ({
+    name: item.regionName || UNASSIGNED_REGION_LABEL,
+    value: Number(item.count || 0)
+  }))
+
+  regionChartInstance.setOption({
+    tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
+    legend: { orient: 'vertical', right: 10, top: 'center' },
+    series: [
+      {
+        name: '鍖哄煙鎶ュ悕',
+        type: 'pie',
+        radius: ['40%', '70%'],
+        avoidLabelOverlap: false,
+        itemStyle: {
+          borderRadius: 8,
+          borderColor: '#fff',
+          borderWidth: 2
+        },
+        label: { formatter: '{b}\n{c}浜�' },
+        data: pieData.length > 0 ? pieData : [{ name: UNASSIGNED_REGION_LABEL, value: 0 }]
+      }
+    ]
+  })
+}
+
+const handleResize = () => {
+  trendChartInstance?.resize()
+  regionChartInstance?.resize()
+}
+
+const loadRegistrationTrend = async () => {
+  trendChartLoading.value = true
+  try {
+    const data = await getRegistrationTrend(15)
+    registrationTrend.value = data || []
+    await nextTick()
+    renderTrendChart()
+  } catch (error) {
+    console.error('鍔犺浇鎶ュ悕瓒嬪娍澶辫触:', error)
+    ElMessage.error('鍔犺浇鎶ュ悕瓒嬪娍澶辫触')
+  } finally {
+    trendChartLoading.value = false
+  }
+}
+
+const loadRegionStats = async () => {
+  regionChartLoading.value = true
+  try {
+    const data = await getRegionRegistrationStats()
+    regionStats.value = (data || [])
+      .filter(item => item.leafFlag !== false)
+      .map(item => ({
+        regionId: item.regionId ?? null,
+        regionName: item.regionName || UNASSIGNED_REGION_LABEL,
+        leafFlag: item.leafFlag,
+        count: item.count ?? 0
+      }))
+    await nextTick()
+    renderRegionChart()
+  } catch (error) {
+    console.error('鍔犺浇鍖哄煙鎶ュ悕缁熻澶辫触:', error)
+    ElMessage.error('鍔犺浇鍖哄煙鎶ュ悕缁熻澶辫触')
+  } finally {
+    regionChartLoading.value = false
+  }
+}
+
 // 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
 onMounted(() => {
   loadStats()
   loadRecentActivities()
+  loadRegistrationTrend()
+  loadRegionStats()
+  window.addEventListener('resize', handleResize)
 })
 
-// 鏌ョ湅姣旇禌
+onBeforeUnmount(() => {
+  window.removeEventListener('resize', handleResize)
+  if (trendChartInstance) {
+    trendChartInstance.dispose()
+    trendChartInstance = null
+  }
+  if (regionChartInstance) {
+    regionChartInstance.dispose()
+    regionChartInstance = null
+  }
+})
+
 const viewActivity = (activity: any) => {
   router.push(`/activity/${activity.id}`)
 }
@@ -130,13 +298,14 @@
 // 鑾峰彇鐘舵�佹牱寮忕被
 const getStatusClass = (status: string) => {
   const statusMap: Record<string, string> = {
-    '宸插彂甯�': 'status-published',
-    '杩涜涓�': 'status-published',
-    '鏈彂甯�': 'status-unpublished',
-    '鎶ュ悕涓�': 'status-unpublished',
-    '鍏抽棴': 'status-closed',
-    '宸茬粨鏉�': 'status-closed',
-    '寰呭紑濮�': 'status-unpublished'
+    宸插彂甯�: 'status-published',
+    鍙戝竷: 'status-published',
+    杩涜涓�: 'status-published',
+    鏈彂甯�: 'status-unpublished',
+    鎶ュ悕涓�: 'status-unpublished',
+    寰呭紑濮�: 'status-unpublished',
+    鍏抽棴: 'status-closed',
+    宸茬粨鏉�: 'status-closed'
   }
   return statusMap[status] || 'status-unpublished'
 }
@@ -146,18 +315,18 @@
 /* 椤甸潰鏁翠綋鏍峰紡 */
 .dashboard {
   padding: 24px;
-  background-color: #FFFFFF;
+  background-color: #ffffff;
   min-height: 100vh;
 }
 
-/* 缁熻鍗$墖琛� */
+/* 缁熻鍗$墖鍖哄煙 */
 .stats-row {
   margin-bottom: 20px;
 }
 
 /* 缁熻鍗$墖鏍峰紡 */
 .stat-card {
-  background: #FFFFFF;
+  background: #ffffff;
   border-radius: 12px;
   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
   border: none;
@@ -173,7 +342,6 @@
   box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
 }
 
-/* 鍥炬爣瀹瑰櫒 */
 .icon-container {
   width: 48px;
   height: 48px;
@@ -187,49 +355,98 @@
 }
 
 .icon-container.blue {
-  background-color: #E0E7FF;
-  color: #6366F1;
+  background-color: #e0e7ff;
+  color: #6366f1;
 }
 
 .icon-container.green {
-  background-color: #D1FAE5;
-  color: #10B981;
+  background-color: #d1fae5;
+  color: #10b981;
 }
 
 .icon-container.yellow {
-  background-color: #FEF3C7;
-  color: #F59E0B;
+  background-color: #fef3c7;
+  color: #f59e0b;
 }
 
 .icon-container.red {
-  background-color: #FECACA;
-  color: #EF4444;
+  background-color: #fecaca;
+  color: #ef4444;
 }
 
-/* 缁熻鏁板瓧 */
 .stat-number {
   font-size: 32px;
   font-weight: 700;
-  color: #1F2937;
+  color: #1f2937;
   position: absolute;
   top: 24px;
   right: 24px;
-  line-height: 1;
 }
 
-/* 缁熻鏍囬 */
 .stat-title {
   font-size: 14px;
   font-weight: 500;
-  color: #6B7280;
+  color: #6b7280;
   position: absolute;
   bottom: 24px;
   left: 24px;
 }
 
-/* 琛ㄦ牸鍗$墖 */
+.chart-section {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
+  gap: 20px;
+  margin-top: 24px;
+}
+
+.chart-card {
+  background: #ffffff;
+  border-radius: 12px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+  border: none;
+  padding: 20px;
+  min-height: 340px;
+  position: relative;
+}
+
+.chart-mask {
+  position: absolute;
+  inset: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: rgba(255, 255, 255, 0.8);
+  z-index: 1;
+  font-size: 14px;
+  color: #6b7280;
+}
+
+.chart-header {
+  display: flex;
+  align-items: baseline;
+  justify-content: space-between;
+  margin-bottom: 12px;
+}
+
+.chart-header-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: #1f2937;
+  margin: 0;
+}
+
+.chart-header-desc {
+  font-size: 13px;
+  color: #9ca3af;
+}
+
+.chart-container {
+  width: 100%;
+  height: 280px;
+}
+
 .table-card {
-  background: #FFFFFF;
+  background: #ffffff;
   border-radius: 12px;
   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
   border: none;
@@ -237,7 +454,6 @@
   margin-top: 20px;
 }
 
-/* 琛ㄦ牸澶撮儴 */
 .table-header {
   margin-bottom: 20px;
   display: flex;
@@ -248,26 +464,25 @@
 .table-title {
   font-size: 18px;
   font-weight: 600;
-  color: #1F2937;
+  color: #1f2937;
   margin: 0;
 }
 
-/* 琛ㄦ牸鏍峰紡 */
 .recent-table {
   width: 100%;
 }
 
 :deep(.el-table__header) {
-  background-color: #F9FAFB;
+  background-color: #f9fafb;
 }
 
 :deep(.el-table__header th) {
-  background-color: #F9FAFB !important;
+  background-color: #f9fafb !important;
   color: #374151;
   font-size: 14px;
   font-weight: 500;
   height: 48px;
-  border-bottom: 1px solid #E5E7EB;
+  border-bottom: 1px solid #e5e7eb;
 }
 
 :deep(.el-table__row) {
@@ -275,48 +490,46 @@
 }
 
 :deep(.el-table__row:nth-child(even)) {
-  background-color: #F9FAFB;
+  background-color: #f9fafb;
 }
 
 :deep(.el-table__row:nth-child(odd)) {
-  background-color: #FFFFFF;
+  background-color: #ffffff;
 }
 
 :deep(.el-table td) {
-  color: #1F2937;
+  color: #1f2937;
   font-size: 14px;
   font-weight: 400;
-  border-bottom: 1px solid #F3F4F6;
+  border-bottom: 1px solid #f3f4f6;
 }
 
-/* 鐘舵�佹爣绛炬牱寮� */
 .status-published {
-  color: #67C23A;
-  background-color: #F0F9FF;
+  color: #67c23a;
+  background-color: #f0f9ff;
   padding: 4px 8px;
   border-radius: 4px;
   font-size: 12px;
 }
 
 .status-unpublished {
-  color: #E6A23C;
-  background-color: #FDF6EC;
+  color: #e6a23c;
+  background-color: #fdf6ec;
   padding: 4px 8px;
   border-radius: 4px;
   font-size: 12px;
 }
 
 .status-closed {
-  color: #F56C6C;
-  background-color: #FEF0F0;
+  color: #f56c6c;
+  background-color: #fef0f0;
   padding: 4px 8px;
   border-radius: 4px;
   font-size: 12px;
 }
 
-/* 鎿嶄綔閾炬帴鏍峰紡 */
 .action-link {
-  color: #409EFF;
+  color: #409eff;
   cursor: pointer;
   font-size: 14px;
   margin: 0 8px;
@@ -324,7 +537,7 @@
 }
 
 .action-link:hover {
-  color: #66B1FF;
+  color: #66b1ff;
   text-decoration: underline;
 }
 
@@ -332,14 +545,13 @@
   margin-left: 0;
 }
 
-/* 鍝嶅簲寮忚璁� */
 @media (max-width: 768px) {
   .dashboard {
     padding: 16px;
   }
-  
+
   .stat-card {
     margin-bottom: 16px;
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/web/src/views/review-list.vue b/web/src/views/review-list.vue
index c63b301..f2e344d 100644
--- a/web/src/views/review-list.vue
+++ b/web/src/views/review-list.vue
@@ -21,10 +21,25 @@
           clearable
         />
         <el-select
+          v-model="selectedRegion"
+          placeholder="璇烽�夋嫨鍖哄煙"
+          @change="handleRegionChange"
+          style="width: 200px"
+          clearable
+          filterable
+        >
+          <el-option
+            v-for="region in regions"
+            :key="region.id"
+            :label="region.name"
+            :value="region.id"
+          />
+        </el-select>
+        <el-select
           v-model="selectedActivity"
           placeholder="璇烽�夋嫨姣旇禌"
           @change="handleActivityChange"
-          style="width: 200px"
+          style="width: 220px"
           clearable
           filterable
         >
@@ -43,9 +58,9 @@
             </span>
           </el-option>
         </el-select>
-        <el-button 
-          type="primary" 
-          :icon="Search" 
+        <el-button
+          type="primary"
+          :icon="Search"
           @click="loadProjects"
           :loading="projectsLoading"
         >
@@ -54,9 +69,9 @@
       </div>
     </div>
 
-    <el-table 
-      :data="projects" 
-      style="width: 100%" 
+    <el-table
+      :data="projects"
+      style="width: 100%"
       v-loading="projectsLoading"
       empty-text="璇峰厛閫夋嫨姣旇禌"
     >
@@ -138,9 +153,9 @@
           <h4>{{ selectedProject.projectName || selectedProject.playerName }} - 璇勫璇︽儏</h4>
           <p class="project-info">鍙傝禌浜猴細{{ selectedProject.playerName }} | 鑱旂郴鐢佃瘽锛歿{ selectedProject.phone }}</p>
         </div>
-        
-        <el-table 
-          :data="judgeRatings" 
+
+        <el-table
+          :data="judgeRatings"
           v-loading="ratingsLoading"
           style="width: 100%"
         >
@@ -175,7 +190,7 @@
           </el-table-column>
         </el-table>
       </div>
-      
+
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="handleRatingListClose">鍏抽棴</el-button>
@@ -190,36 +205,41 @@
       width="50%"
       :before-close="handleRatingDetailClose"
     >
-      <div v-if="selectedRating">
-        <div class="rating-detail-header">
-          <h4>璇勫锛歿{ selectedRating.judgeName }}</h4>
-          <p>鎬诲垎锛�<span class="total-score">{{ selectedRating.totalScore.toFixed(1) }}</span></p>
+      <div v-if="ratingDetail">
+        <div class="dialog-header">
+          <h4>{{ ratingDetail.judgeName }} 璇勫缁撴灉</h4>
+          <p class="project-info">
+            鎬诲垎锛�
+            <span class="total-score">
+              {{ ratingDetail.totalScore?.toFixed(1) ?? '鏆傛棤' }}
+            </span>
+          </p>
         </div>
-        
-        <el-table 
-          :data="ratingItems" 
-          v-loading="ratingDetailLoading"
-          style="width: 100%"
+
+        <el-table
+          :data="ratingDetail.items || []"
+          border
+          size="small"
         >
-          <el-table-column prop="itemName" label="璇勫垎椤圭洰" min-width="150" />
+          <el-table-column prop="itemName" label="璇勫垎椤�" min-width="160" />
           <el-table-column prop="score" label="寰楀垎" width="100" align="center">
             <template #default="scope">
-              <span class="item-score">{{ scope.row.score }}</span>
+              <span class="item-score">{{ scope.row.score?.toFixed(1) ?? '-' }}</span>
             </template>
           </el-table-column>
           <el-table-column prop="maxScore" label="婊″垎" width="100" align="center">
             <template #default="scope">
-              <span class="max-score">{{ scope.row.maxScore || 100 }}</span>
+              <span class="max-score">{{ scope.row.maxScore?.toFixed(1) ?? '-' }}</span>
             </template>
           </el-table-column>
         </el-table>
-        
-        <div v-if="selectedRating.remark" class="rating-comment">
-          <h5>璇勮锛�</h5>
-          <p>{{ selectedRating.remark }}</p>
+
+        <div class="rating-comment" v-if="ratingDetail.comment">
+          <h5>璇勮</h5>
+          <p>{{ ratingDetail.comment }}</p>
         </div>
       </div>
-      
+
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="handleRatingDetailClose">鍏抽棴</el-button>
@@ -229,40 +249,63 @@
   </div>
 </template>
 
-<script setup>
+<script setup lang="ts">
 import { ref, onMounted } from 'vue'
 import { useRouter } from 'vue-router'
 import { ElMessage } from 'element-plus'
 import { Search, Trophy, View } from '@element-plus/icons-vue'
 import { getActiveActivities, getRatingStats, getJudgeRatingDetail } from '@/api/projectReview'
 import { getProjectReviewApplications } from '@/api/projectReviewNew'
+import { getLeafRegions } from '@/api/region'
 import { userApi } from '@/api/user'
 import { getUserInfo } from '@/utils/auth'
 
 const router = useRouter()
 
-// 鍝嶅簲寮忔暟鎹�
-const activities = ref([])
-const selectedActivity = ref(null)
-const projects = ref([])
+interface ActivityItem {
+  id: number
+  name: string
+  pid: number
+  parent?: ActivityItem
+}
+
+interface RegionItem {
+  id: number
+  name: string
+}
+
+const activities = ref<ActivityItem[]>([])
+const regions = ref<RegionItem[]>([])
+const selectedActivity = ref<number | null>(null)
+const selectedRegion = ref<number | null>(null)
+const projects = ref<any[]>([])
 const searchName = ref('')
 const activitiesLoading = ref(false)
 const projectsLoading = ref(false)
 
-// 璇勫寮圭獥鐩稿叧鏁版嵁
+// 寮圭獥鐩稿叧鐘舵��
 const ratingListVisible = ref(false)
 const ratingDetailVisible = ref(false)
-const selectedProject = ref(null)
-const selectedRating = ref(null)
-const judgeRatings = ref([])
-const ratingItems = ref([])
+const selectedProject = ref<any | null>(null)
+const judgeRatings = ref<any[]>([])
+const ratingDetail = ref<any | null>(null)
 const ratingsLoading = ref(false)
 const ratingDetailLoading = ref(false)
 
-// 鍒嗛〉鏁版嵁
+// 鍒嗛〉
 const currentPage = ref(1)
 const pageSize = ref(10)
 const total = ref(0)
+
+const loadRegions = async () => {
+  try {
+    const data = await getLeafRegions()
+    regions.value = data || []
+  } catch (error: any) {
+    console.error('鍔犺浇鍖哄煙鍒楄〃澶辫触:', error)
+    ElMessage.error(error?.message || '鍔犺浇鍖哄煙鍒楄〃澶辫触')
+  }
+}
 
 // 鍔犺浇姣旇禌鍒楄〃
 const loadActivities = async () => {
@@ -271,16 +314,20 @@
   try {
     console.log('璋冪敤 getActiveActivities...')
     const data = await getActiveActivities()
-    console.log('getActiveActivities 杩斿洖鏁版嵁:', data)
-    activities.value = data
-    console.log('activities.value 璁剧疆涓�:', activities.value)
-    console.log('activities.value.length:', activities.value?.length)
-  } catch (error) {
+    console.log('姣旇禌鏁版嵁锛�', data)
+
+    activities.value = data || []
+
+    if (activities.value.length === 1) {
+      selectedActivity.value = activities.value[0].id
+      console.log('鑷姩閫変腑鍞竴姣旇禌:', selectedActivity.value)
+      loadProjects()
+    }
+  } catch (error: any) {
     console.error('鍔犺浇姣旇禌鍒楄〃澶辫触:', error)
-    ElMessage.error(error.message)
+    ElMessage.error(error?.message || '鍔犺浇姣旇禌鍒楄〃澶辫触')
   } finally {
     activitiesLoading.value = false
-    console.log('=== 姣旇禌鍒楄〃鍔犺浇瀹屾垚 ===')
   }
 }
 
@@ -290,51 +337,54 @@
     ElMessage.warning('璇峰厛閫夋嫨姣旇禌')
     return
   }
-  
+
   console.log('=== 寮�濮嬪姞杞介」鐩垪琛� ===')
   console.log('selectedActivity.value:', selectedActivity.value)
+  console.log('selectedRegion.value:', selectedRegion.value)
   console.log('currentPage.value:', currentPage.value)
   console.log('pageSize.value:', pageSize.value)
-  
+
   projectsLoading.value = true
   try {
-    const params = {
+    const params: Record<string, unknown> = {
       activityId: selectedActivity.value,
       page: currentPage.value,
       size: pageSize.value
     }
-    
-    // 濡傛灉鏈夋悳绱㈠悕绉帮紝娣诲姞鍒板弬鏁颁腑
+
+    if (selectedRegion.value) {
+      params.regionId = selectedRegion.value
+    }
+
     if (searchName.value && searchName.value.trim()) {
       params.name = searchName.value.trim()
     }
-    
-    console.log('API璋冪敤鍙傛暟:', params)
-    
+
+    console.log('璇锋眰鍙傛暟:', params)
     const response = await getProjectReviewApplications(params)
     console.log('API鍝嶅簲:', response)
-    
-    // 澶勭悊鍝嶅簲鏁版嵁 - 鏂扮殑鍒嗛〉缁撴瀯
+
     const pageData = response.projectReviewApplications
     projects.value = pageData?.content || []
     total.value = pageData?.totalElements || 0
-    
-    console.log('璁剧疆 projects.value:', projects.value)
-    console.log('projects.value.length:', projects.value.length)
-    console.log('璁剧疆 total.value:', total.value)
-  } catch (error) {
+  } catch (error: any) {
     console.error('鍔犺浇椤圭洰鍒楄〃澶辫触:', error)
-    ElMessage.error('鍔犺浇椤圭洰鍒楄〃澶辫触')
-    // 濡傛灉API璋冪敤澶辫触锛屾樉绀虹┖鏁版嵁
-    projects.value = []
-    total.value = 0
+    ElMessage.error(error?.message || '鍔犺浇椤圭洰鍒楄〃澶辫触')
   } finally {
     projectsLoading.value = false
   }
 }
 
+// 澶勭悊鍖哄煙閫夋嫨鍙樺寲
+const handleRegionChange = () => {
+  currentPage.value = 1
+  if (selectedActivity.value) {
+    loadProjects()
+  }
+}
+
 // 澶勭悊姣旇禌閫夋嫨鍙樺寲
-const handleActivityChange = (activityId) => {
+const handleActivityChange = () => {
   currentPage.value = 1
   loadProjects()
 }
@@ -342,217 +392,86 @@
 // 娓呯┖鎼滅储
 const handleClear = () => {
   searchName.value = ''
-  currentPage.value = 1
-  if (selectedActivity.value) {
-    loadProjects()
-  }
+  loadProjects()
 }
 
-// 閲嶇疆鎼滅储
-const resetSearch = () => {
-  searchName.value = ''
-  selectedActivity.value = null
-  currentPage.value = 1
-  projects.value = []
-  total.value = 0
-}
-
-// 鍒嗛〉澶勭悊
-const handleSizeChange = (size) => {
+// 鍒嗛〉澶у皬鏀瑰彉
+const handleSizeChange = (size: number) => {
   pageSize.value = size
   currentPage.value = 1
   loadProjects()
 }
 
-const handleCurrentChange = (page) => {
+// 褰撳墠椤垫敼鍙�
+const handleCurrentChange = (page: number) => {
   currentPage.value = page
   loadProjects()
 }
 
-// 鏌ョ湅璇︽儏
-const viewDetails = async (projectId) => {
-  // 浼犻�抯tageId鍙傛暟锛宻electedActivity.value灏辨槸褰撳墠閫変腑鐨剆tageId
-  const stageId = selectedActivity.value
-  if (!stageId) {
-    ElMessage.warning('璇峰厛閫夋嫨姣旇禌闃舵')
-    return
-  }
-
-  try {
-    // 鑾峰彇褰撳墠鐢ㄦ埛淇℃伅
-    const userInfo = getUserInfo()
-    if (!userInfo) {
-      ElMessage.error('鐢ㄦ埛鏈櫥褰曪紝璇烽噸鏂扮櫥褰�')
-      return
-    }
-
-    // 妫�鏌ョ敤鎴锋槸鍚︽湁employee韬唤
-    const hasEmployeeRole = !!userInfo.employee
-    
-    if (hasEmployeeRole) {
-      // 濡傛灉鐢ㄦ埛鏈塭mployee韬唤锛岀洿鎺ュ厑璁告煡鐪嬶紙涓嶉渶瑕佹潈闄愭鏌ワ級
-      console.log('鐢ㄦ埛鍏锋湁鍛樺伐韬唤锛屽厑璁告煡鐪嬫墍鏈夎瘎鍒嗚褰�')
-      router.push(`/project-review/${projectId}/detail?stageId=${stageId}`)
-      return
-    }
-
-    // 濡傛灉鐢ㄦ埛鍙湁judge韬唤锛堟病鏈塭mployee韬唤锛夛紝闇�瑕佹鏌ヨ瘎濮旀潈闄�
-    const judgeInfo = await userApi.getCurrentJudgeInfo()
-    
-    if (!judgeInfo) {
-      ElMessage.error('鎮ㄦ病鏈夎瘎濮旀潈闄愶紝鏃犳硶杩涜璇勫')
-      return
-    }
-
-    // 妫�鏌ヨ瘎濮旀槸鍚︽湁褰撳墠姣旇禌闃舵鐨勬潈闄�
-    const hasPermission = await userApi.checkJudgeInActivity(stageId, judgeInfo.judgeId)
-    
-    if (!hasPermission) {
-      ElMessage.error('鎮ㄦ病鏈夊綋鍓嶆瘮璧涢樁娈电殑璇勫鏉冮檺锛屾棤娉曡繘鍏ヨ瘎瀹¢〉闈�')
-      return
-    }
-
-    // 鏉冮檺妫�鏌ラ�氳繃锛岃烦杞埌璇勫椤甸潰
-    router.push(`/project-review/${projectId}/detail?stageId=${stageId}`)
-  } catch (error) {
-    console.error('鏉冮檺妫�鏌ュけ璐�:', error)
-    ElMessage.error('鏉冮檺楠岃瘉澶辫触锛岃閲嶆柊鐧诲綍')
-  }
-}
-
-// 鏄剧ず璇勫鍒楄〃
-const showRatingList = async (project) => {
-  if (project.ratingCount === 0) {
-    ElMessage.warning('璇ラ」鐩殏鏃犺瘎瀹¤褰�')
-    return
-  }
-  
+// 灞曠ず璇勫鍒楄〃
+const showRatingList = async (project: any) => {
   selectedProject.value = project
-  ratingListVisible.value = true
-  
-  // 鍔犺浇璇勫鍒楄〃
-  await loadJudgeRatings(project.id)
-}
-
-// 鍔犺浇璇勫璇勫垎鍒楄〃
-const loadJudgeRatings = async (activityPlayerId) => {
   ratingsLoading.value = true
+  ratingListVisible.value = true
   try {
-    const result = await getRatingStats(activityPlayerId)
-    console.log('getRatingStats 杩斿洖鏁版嵁:', result)
-    
-    // 浣跨敤姝g‘鐨勫瓧娈靛悕
-    judgeRatings.value = result.ratings || []
-    console.log('judgeRatings 璁剧疆涓�:', judgeRatings.value)
-  } catch (error) {
+    const data = await getRatingStats(project.id)
+    judgeRatings.value = data || []
+  } catch (error: any) {
     console.error('鍔犺浇璇勫鍒楄〃澶辫触:', error)
-    ElMessage.error('鍔犺浇璇勫鍒楄〃澶辫触')
-    judgeRatings.value = []
+    ElMessage.error(error?.message || '鍔犺浇璇勫鍒楄〃澶辫触')
   } finally {
     ratingsLoading.value = false
   }
 }
 
-// 鏌ョ湅璇勫垎璇︽儏
-const viewRatingDetail = async (rating) => {
-  if (!rating.hasRated) {
-    ElMessage.warning('璇ヨ瘎濮斿皻鏈瘎鍒�')
-    return
-  }
-  
-  selectedRating.value = rating
-  ratingDetailVisible.value = true
-  
-  // 鍔犺浇璇勫垎鏄庣粏
-  await loadRatingDetail(selectedProject.value.id, rating.judgeId)
+const handleRatingListClose = () => {
+  ratingListVisible.value = false
+  judgeRatings.value = []
 }
 
-// 鍔犺浇璇勫垎鏄庣粏
-const loadRatingDetail = async (activityPlayerId, judgeId) => {
+// 鏌ョ湅璇勫垎璇︽儏
+const viewRatingDetail = async (rating: any) => {
+  ratingDetailVisible.value = true
   ratingDetailLoading.value = true
   try {
-    console.log('鍔犺浇璇勫垎鏄庣粏锛宎ctivityPlayerId:', activityPlayerId, 'judgeId:', judgeId)
-    
-    const result = await getJudgeRatingDetail(activityPlayerId, judgeId)
-    console.log('璇勫垎鏄庣粏API杩斿洖:', result)
-    
-    if (result && result.items) {
-      ratingItems.value = result.items.map(item => ({
-        itemName: item.ratingItemName,
-        score: item.score,
-        maxScore: item.maxScore || 100
-      }))
-      
-      // 鏇存柊閫変腑璇勫垎鐨勫娉ㄤ俊鎭�
-      if (selectedRating.value) {
-        selectedRating.value.remark = result.remark
-      }
-    } else {
-      ratingItems.value = []
-    }
-  } catch (error) {
-    console.error('鍔犺浇璇勫垎鏄庣粏澶辫触:', error)
-    ElMessage.error('鍔犺浇璇勫垎鏄庣粏澶辫触: ' + (error.message || '鏈煡閿欒'))
-    ratingItems.value = []
+    const data = await getJudgeRatingDetail(rating.id)
+    ratingDetail.value = data
+  } catch (error: any) {
+    console.error('鍔犺浇璇勫垎璇︽儏澶辫触:', error)
+    ElMessage.error(error?.message || '鍔犺浇璇勫垎璇︽儏澶辫触')
   } finally {
     ratingDetailLoading.value = false
   }
 }
 
-// 鍏抽棴璇勫鍒楄〃寮圭獥
-const handleRatingListClose = () => {
-  ratingListVisible.value = false
-  selectedProject.value = null
-  judgeRatings.value = []
-}
-
-// 鍏抽棴璇勫垎璇︽儏寮圭獥
 const handleRatingDetailClose = () => {
   ratingDetailVisible.value = false
-  selectedRating.value = null
-  ratingItems.value = []
+  ratingDetail.value = null
 }
 
-// 鏍煎紡鍖栨棩鏈�
-const formatDate = (dateString) => {
-  if (!dateString) return '-'
-  return new Date(dateString).toLocaleString('zh-CN')
-}
-
-// 鑾峰彇鐘舵�佺被鍨�
-const getStateType = (state) => {
-  const stateMap = {
-    0: 'danger',  // 宸叉嫆缁�
-    1: 'warning', // 寰呭鏍�
-    2: 'success', // 宸查�氳繃
-    3: 'info'     // 宸茬粨鏉�
-  }
-  return stateMap[state] || 'info'
-}
-
-// 鑾峰彇鐘舵�佸悕绉�
-const getStateName = (state) => {
-  const stateMap = {
-    0: '宸叉嫆缁�',
-    1: '寰呰瘎瀹�',
-    2: '宸查�氳繃',
-    3: '宸茬粨鏉�'
-  }
-  return stateMap[state] || '鏈煡'
+// 鏍煎紡鍖栨椂闂�
+const formatDate = (value: string | number | Date) => {
+  if (!value) return '-'
+  const date = new Date(value)
+  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(
+    date.getDate()
+  ).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(
+    date.getMinutes()
+  ).padStart(2, '0')}`
 }
 
 // 鑾峰彇璇勫鐘舵�佺被鍨嬶紙鍩轰簬璇勫娆℃暟锛�
-const getReviewStatusType = (ratingCount) => {
+const getReviewStatusType = (ratingCount: number) => {
   return ratingCount > 0 ? 'success' : 'warning'
 }
 
 // 鑾峰彇璇勫鐘舵�佸悕绉帮紙鍩轰簬璇勫娆℃暟锛�
-const getReviewStatusName = (ratingCount) => {
+const getReviewStatusName = (ratingCount: number) => {
   return ratingCount > 0 ? '宸茶瘎瀹�' : '鏈瘎瀹�'
 }
 
 // 鑾峰彇姣旇禌鍚嶇О锛堝鏋滄槸闃舵锛岃繑鍥炵埗姣旇禌鍚嶇О锛涘鏋滄槸姣旇禌锛岃繑鍥炶嚜宸辩殑鍚嶇О锛�
-const getActivityName = (activity) => {
+const getActivityName = (activity: ActivityItem) => {
   if (activity.pid > 0 && activity.parent) {
     return activity.parent.name
   }
@@ -560,22 +479,30 @@
 }
 
 // 鑾峰彇娲诲姩鏄剧ず鍚嶇О锛堢敤浜庢悳绱㈠拰閫変腑鏃舵樉绀猴級
-const getActivityDisplayName = (activity) => {
+const getActivityDisplayName = (activity: ActivityItem) => {
   if (activity.pid > 0 && activity.parent) {
     return `${activity.parent.name} - ${activity.name}`
   }
   return activity.name
 }
 
-// 鑾峰彇闃舵鍚嶇О锛堝鏋滄槸闃舵锛岃繑鍥為樁娈靛悕绉帮紱濡傛灉鏄瘮璧涳紝杩斿洖姣旇禌鍚嶇О锛�
-const getStageName = (activity) => {
-  return activity.name
+// 鏉冮檺妫�鏌ワ細鍙湁绠$悊鍛樺彲浠ユ煡鐪嬮」鐩瘎瀹�
+const checkPermission = async () => {
+  const userInfo = getUserInfo()
+  if (!userInfo) {
+    router.push('/login')
+  }
 }
 
-
+// 鏌ョ湅椤圭洰璇︽儏
+const viewDetails = (projectId: number) => {
+  router.push(`/review/detail/${projectId}`)
+}
 
 // 缁勪欢鎸傝浇鏃跺姞杞芥暟鎹�
 onMounted(() => {
+  checkPermission()
+  loadRegions()
   loadActivities()
 })
 </script>
@@ -616,36 +543,23 @@
 /* 寮圭獥鏍峰紡 */
 .dialog-header {
   margin-bottom: 20px;
-  
+
   h4 {
     margin: 0 0 8px 0;
-    color: #303133;
-    font-size: 16px;
+    font-size: 18px;
     font-weight: 600;
+    color: #1f2937;
   }
-  
+
   .project-info {
     margin: 0;
-    color: #606266;
-    font-size: 14px;
+    color: #6b7280;
+    font-size: 13px;
   }
 }
 
-.rating-detail-header {
-  margin-bottom: 20px;
-  
-  h4 {
-    margin: 0 0 8px 0;
-    color: #303133;
-    font-size: 16px;
-    font-weight: 600;
-  }
-  
-  .total-score {
-    color: #409eff;
-    font-weight: 600;
-    font-size: 18px;
-  }
+.dialog-footer {
+  text-align: right;
 }
 
 .rating-comment {
@@ -653,14 +567,14 @@
   padding: 16px;
   background-color: #f5f7fa;
   border-radius: 8px;
-  
+
   h5 {
     margin: 0 0 8px 0;
     color: #303133;
     font-size: 14px;
     font-weight: 600;
   }
-  
+
   p {
     margin: 0;
     color: #606266;
@@ -699,12 +613,6 @@
   align-items: center;
 }
 
-.search-area {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-}
-
 /* 鎿嶄綔鎸夐挳鏍峰紡 */
 .action-btn {
   padding: 8px !important;
@@ -720,16 +628,6 @@
 .view-btn:hover {
   background-color: rgba(59, 130, 246, 0.1) !important;
   transform: scale(1.2);
-}
-
-.score {
-  color: #67c23a;
-  font-weight: 600;
-}
-
-.no-score {
-  color: #909399;
-  font-style: italic;
 }
 
 .pagination-container {
@@ -757,10 +655,10 @@
     gap: 12px;
     align-items: stretch;
   }
-  
-  .search-area {
+
+  .search-form {
     justify-content: center;
     flex-wrap: wrap;
   }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/web/vite.config.ts b/web/vite.config.ts
index 697b868..54dc648 100644
--- a/web/vite.config.ts
+++ b/web/vite.config.ts
@@ -10,7 +10,7 @@
     }
   },
   server: {
-    port: 5173,
+    port: 3000,
     open: true,
     proxy: {
       '/api': {
diff --git a/wx/lib/utils.wxs b/wx/lib/utils.wxs
index 07c55be..096a5ae 100644
--- a/wx/lib/utils.wxs
+++ b/wx/lib/utils.wxs
@@ -1,30 +1,30 @@
-var formatDate = function(dateStr, format) {
-  // 寮哄埗杞崲涓哄瓧绗︿覆锛屽吋瀹� null, undefined, number 绛夌被鍨�
-  var s = '' + dateStr;
-
-  if (s.length < 10) {
-    return '鈥�';
-  }
-  
-  // 浣跨敤 slice 鏇挎崲 substr
-  var y = s.slice(0, 4);
-  var m = s.slice(5, 7);
-  var d = s.slice(8, 10);
-
-  if (format === 'YYYY-MM-DD') {
-    return y + '-' + m + '-' + d;
-  }
-
-  // 妫�鏌ユ椂闂撮儴鍒嗘墍闇�闀垮害
-  if (format === 'YYYY-MM-DD HH:mm' && s.length >= 16) {
-    var h = s.slice(11, 13);
-    var min = s.slice(14, 16);
-    return y + '-' + m + '-' + d + ' ' + h + ':' + min;
-  }
-
-  return '鈥�'; // 榛樿杩斿洖鍗犱綅绗�
-};
-
-module.exports = {
-  formatDate: formatDate
+var formatDate = function(dateStr, format) {

+  // 寮哄埗杞崲涓哄瓧绗︿覆锛屽吋瀹� null, undefined, number 绛夌被鍨�

+  var s = '' + dateStr;

+

+  if (s.length < 10) {

+    return '鈥�';

+  }

+  

+  // 浣跨敤 slice 鏇挎崲 substr

+  var y = s.slice(0, 4);

+  var m = s.slice(5, 7);

+  var d = s.slice(8, 10);

+

+  if (format === 'YYYY-MM-DD') {

+    return y + '-' + m + '-' + d;

+  }

+

+  // 妫�鏌ユ椂闂撮儴鍒嗘墍闇�闀垮害

+  if (format === 'YYYY-MM-DD HH:mm' && s.length >= 16) {

+    var h = s.slice(11, 13);

+    var min = s.slice(14, 16);

+    return y + '-' + m + '-' + d + ' ' + h + ':' + min;

+  }

+

+  return '鈥�'; // 榛樿杩斿洖鍗犱綅绗�

+};

+

+module.exports = {

+  formatDate: formatDate

 };
\ No newline at end of file
diff --git a/wx/pages/judge/review.js b/wx/pages/judge/review.js
index e317235..1fd19b0 100644
--- a/wx/pages/judge/review.js
+++ b/wx/pages/judge/review.js
@@ -628,4 +628,4 @@
       path: '/pages/index/index'
     }
   }
-})
+})
\ No newline at end of file
diff --git a/wx/pages/profile/employee-review-detail.js b/wx/pages/profile/employee-review-detail.js
index 0b6dadc..1e8e9e7 100644
--- a/wx/pages/profile/employee-review-detail.js
+++ b/wx/pages/profile/employee-review-detail.js
@@ -20,6 +20,7 @@
         fullUrl
         fullThumbUrl
         fileExt
+        fileSize
         mediaType
       }
     }
@@ -43,8 +44,12 @@
     loading: false,
     submitting: false,
     activityPlayerId: null,
-    detail: null,
-    feedback: ''
+    detail: {
+      playerInfo: {},
+      submissionFiles: []
+    },
+    feedback: '',
+    mediaList: []
   },
 
   onLoad(options) {
@@ -76,16 +81,17 @@
         return
       }
 
+      const mediaList = (detail.submissionFiles || []).map(item => this.transformMediaFile(item))
+      const playerInfo = detail.playerInfo || {}
+
       this.setData({
         detail: {
           ...detail,
+          playerInfo,
           stateText: this.getStateText(detail.state),
-          submissionFiles: (detail.submissionFiles || []).map(file => ({
-            id: file.id,
-            name: file.name,
-            url: file.fullUrl || file.fullThumbUrl || ''
-          }))
+          submissionFiles: mediaList
         },
+        mediaList,
         feedback: detail.feedback || ''
       })
     } catch (error) {
@@ -93,6 +99,32 @@
       wx.showToast({ title: '鍔犺浇澶辫触', icon: 'none' })
     } finally {
       this.setData({ loading: false })
+    }
+  },
+
+  transformMediaFile(file = {}) {
+    const url = file.fullUrl || file.fullThumbUrl || file.url || ''
+    const thumbUrl = file.fullThumbUrl || file.fullUrl || url
+    const ext = (file.fileExt || '').toLowerCase()
+    let mediaType = 'file'
+
+    if (file.mediaType === 1 || ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic'].includes(ext)) {
+      mediaType = 'image'
+    } else if (file.mediaType === 2 || ['mp4', 'mov', 'avi', 'wmv', 'mkv', 'webm', 'flv'].includes(ext)) {
+      mediaType = 'video'
+    } else if (ext === 'pdf') {
+      mediaType = 'pdf'
+    } else if (['doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'wps', 'txt', 'rtf'].includes(ext)) {
+      mediaType = 'word'
+    }
+
+    return {
+      id: file.id,
+      name: file.name || '璧勬枡鏂囦欢',
+      url,
+      thumbUrl,
+      mediaType,
+      size: Number(file.fileSize) || 0
     }
   },
 
@@ -119,9 +151,10 @@
     this.setData({ submitting: true })
 
     try {
+      const trimmedFeedback = (this.data.feedback || '').trim()
       const variables = {
         id: Number(this.data.activityPlayerId),
-        feedback: this.data.feedback ? this.data.feedback.trim() : null
+        feedback: trimmedFeedback ? trimmedFeedback : null
       }
       const result = await graphqlRequest(mutation, variables)
       const success = result && (action === 'APPROVE' ? result.approveActivityPlayer : result.rejectActivityPlayer)
@@ -143,13 +176,74 @@
     }
   },
 
-  previewFile(e) {
-    const url = e.currentTarget.dataset.url
-    if (url) {
-      wx.navigateTo({ url: `/pages/webview/webview?url=${encodeURIComponent(url)}` })
+  onMediaTap(e) {
+    const index = Number(e.currentTarget.dataset.index)
+    const mediaList = this.data.mediaList || []
+    const media = mediaList[index]
+    if (!media) return
+
+    if (media.mediaType === 'image') {
+      const images = mediaList.filter(item => item.mediaType === 'image').map(item => item.url)
+      wx.previewImage({
+        current: media.url,
+        urls: images
+      })
+    } else if (media.mediaType === 'video') {
+      wx.navigateTo({
+        url: `/pages/video/video?url=${encodeURIComponent(media.url)}&title=${encodeURIComponent(media.name)}`
+      })
+    } else {
+      this.openDocumentMedia(media)
     }
   },
 
+  async openDocumentMedia(media) {
+    try {
+      wx.showLoading({ title: '鎵撳紑涓�...' })
+      const downloadRes = await new Promise((resolve, reject) => {
+        wx.downloadFile({
+          url: media.url,
+          success: resolve,
+          fail: reject
+        })
+      })
+
+      if (downloadRes.statusCode !== 200) {
+        throw new Error('鏂囦欢涓嬭浇澶辫触')
+      }
+
+      await new Promise((resolve, reject) => {
+        wx.openDocument({
+          filePath: downloadRes.tempFilePath,
+          showMenu: true,
+          success: resolve,
+          fail: reject
+        })
+      })
+    } catch (error) {
+      console.error('鎵撳紑鏂囦欢澶辫触:', error)
+      wx.showToast({
+        title: '鏃犳硶鎵撳紑鏂囦欢',
+        icon: 'error'
+      })
+    } finally {
+      wx.hideLoading()
+    }
+  },
+
+  getFileSizeText(size) {
+    if (!size || size <= 0) {
+      return '鏈煡澶у皬'
+    }
+    if (size < 1024) {
+      return `${size}B`
+    }
+    if (size < 1024 * 1024) {
+      return `${(size / 1024).toFixed(1)}KB`
+    }
+    return `${(size / (1024 * 1024)).toFixed(1)}MB`
+  },
+
   getStateText(state) {
     switch (state) {
       case 0:
diff --git a/wx/pages/profile/employee-review-detail.wxml b/wx/pages/profile/employee-review-detail.wxml
index b66c8a2..e5a34c1 100644
--- a/wx/pages/profile/employee-review-detail.wxml
+++ b/wx/pages/profile/employee-review-detail.wxml
@@ -5,11 +5,11 @@
       <text class="section-title">椤圭洰淇℃伅</text>
       <view class="info-row">
         <text class="label">姣旇禌鍚嶇О</text>
-        <text class="value">{{detail.activityName || '-'}} </text>
+        <text class="value">{{detail.activityName || '-'}}</text>
       </view>
       <view class="info-row">
         <text class="label">椤圭洰鍚嶇О</text>
-        <text class="value">{{detail.projectName || '-'}} </text>
+        <text class="value">{{detail.projectName || '-'}}</text>
       </view>
       <view class="info-row">
         <text class="label">褰撳墠鐘舵��</text>
@@ -33,13 +33,47 @@
       </view>
     </view>
 
-    <view class="info-card" wx:if="{{detail.submissionFiles && detail.submissionFiles.length}}">
-      <text class="section-title">鎻愪氦璧勬枡</text>
-      <view class="file-item" wx:for="{{detail.submissionFiles}}" wx:key="id" data-url="{{item.url}}" bindtap="previewFile">
-        <text class="file-name">{{item.name || '璧勬枡鏂囦欢'}}</text>
-        <text class="file-action">棰勮</text>
+    <block wx:if="{{detail.submissionFiles && detail.submissionFiles.length}}">
+      <view class="info-card">
+        <text class="section-title">鎻愪氦璧勬枡</text>
+        <view class="media-list">
+          <view
+            class="media-item"
+            wx:for="{{detail.submissionFiles}}"
+            wx:key="id"
+            data-index="{{index}}"
+            bindtap="onMediaTap"
+          >
+            <view class="media-thumb-wrapper">
+              <block wx:if="{{item.mediaType === 'image' || item.mediaType === 'video'}}">
+                <image
+                  wx:if="{{item.thumbUrl}}"
+                  class="media-thumb"
+                  src="{{item.thumbUrl}}"
+                  mode="aspectFill"
+                />
+                <text wx:else class="icon media-icon placeholder ic-picture"></text>
+              </block>
+              <text wx:elif="{{item.mediaType === 'pdf'}}" class="icon media-icon pdf ic-pdf"></text>
+              <text wx:elif="{{item.mediaType === 'word'}}" class="icon media-icon doc ic-word"></text>
+              <text wx:else class="icon media-icon file ic-file"></text>
+              <text wx:if="{{item.mediaType === 'video'}}" class="icon media-play ic-video-play"></text>
+            </view>
+            <view class="media-info">
+              <text class="media-name">{{item.name || '璧勬枡鏂囦欢'}}</text>
+              <text class="media-size">{{getFileSizeText(item.size)}}</text>
+            </view>
+          </view>
+        </view>
       </view>
-    </view>
+    </block>
+
+    <block wx:else>
+      <view class="info-card">
+        <text class="section-title">鎻愪氦璧勬枡</text>
+        <view class="empty-media">鏆傛棤涓婁紶鐨勮祫鏂欐枃浠�</view>
+      </view>
+    </block>
 
     <view class="info-card">
       <text class="section-title">瀹℃牳鎰忚</text>
diff --git a/wx/pages/profile/employee-review-detail.wxss b/wx/pages/profile/employee-review-detail.wxss
index dc136a0..2933f6b 100644
--- a/wx/pages/profile/employee-review-detail.wxss
+++ b/wx/pages/profile/employee-review-detail.wxss
@@ -1,3 +1,5 @@
+@import "../../style/icon.wxss";
+
 .container {
   min-height: 100vh;
   background: #f5f5f5;
@@ -58,30 +60,108 @@
   margin-top: 8rpx;
 }
 
-.file-item {
+.media-list {
   display: flex;
-  justify-content: space-between;
+  flex-direction: column;
+  gap: 20rpx;
+}
+
+.media-item {
+  display: flex;
   align-items: center;
-  padding: 20rpx 0;
-  border-bottom: 1rpx solid #f0f0f0;
-  font-size: 26rpx;
+  padding: 20rpx;
+  border-radius: 12rpx;
+  background: #f8f9fb;
+  gap: 20rpx;
 }
 
-.file-item:last-child {
-  border-bottom: none;
+.media-item:active {
+  background: #eef4ff;
 }
 
-.file-name {
-  color: #333333;
+.media-thumb-wrapper {
+  position: relative;
+  width: 120rpx;
+  height: 120rpx;
+  border-radius: 12rpx;
+  overflow: hidden;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: #ffffff;
+}
+
+.media-thumb {
+  width: 100%;
+  height: 100%;
+}
+
+.media-icon {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 48rpx;
+  color: #ffffff;
+}
+
+.media-icon.pdf {
+  background: linear-gradient(135deg, #ff5959 0%, #ff7b7b 100%);
+}
+
+.media-icon.doc {
+  background: linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%);
+}
+
+.media-icon.file {
+  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
+}
+
+.media-icon.placeholder {
+  background: #f1f3f9;
+  color: #9ca3af;
+}
+
+.media-play {
+  position: absolute;
+  right: 8rpx;
+  bottom: 8rpx;
+  background: rgba(0, 0, 0, 0.5);
+  color: #ffffff;
+  width: 36rpx;
+  height: 36rpx;
+  border-radius: 18rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 24rpx;
+}
+
+.media-info {
   flex: 1;
+  display: flex;
+  flex-direction: column;
+  gap: 8rpx;
+}
+
+.media-name {
+  font-size: 28rpx;
+  color: #1f2937;
+  font-weight: 600;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
-  padding-right: 20rpx;
 }
 
-.file-action {
-  color: #1677ff;
+.media-size {
+  font-size: 24rpx;
+  color: #9ca3af;
+}
+
+.empty-media {
+  font-size: 26rpx;
+  color: #999999;
 }
 
 .feedback-input {
diff --git a/wx/pages/profile/employee-review.js b/wx/pages/profile/employee-review.js
index 5c3ad4a..9247275 100644
--- a/wx/pages/profile/employee-review.js
+++ b/wx/pages/profile/employee-review.js
@@ -1,5 +1,16 @@
 const { graphqlRequest } = require('../../lib/utils')
 
+function formatTime(dateString) {
+  if (!dateString) return '-'
+  const date = new Date(dateString)
+  const year = date.getFullYear()
+  const month = String(date.getMonth() + 1).padStart(2, '0')
+  const day = String(date.getDate()).padStart(2, '0')
+  const hours = String(date.getHours()).padStart(2, '0')
+  const minutes = String(date.getMinutes()).padStart(2, '0')
+  return `${year}-${month}-${day} ${hours}:${minutes}`
+}
+
 const LIST_QUERY = `
   query EmployeeReviewApplications($keyword: String, $state: Int, $page: Int, $size: Int) {
     employeeReviewApplications(keyword: $keyword, state: $state, page: $page, size: $size) {
@@ -59,18 +70,18 @@
   },
 
   onLoad() {
-    this.initData()
+    this.initializeReviewData()
   },
 
   onShow() {
     if (this.data.needRefresh) {
-      this.initData()
+      this.initializeReviewData()
       this.setData({ needRefresh: false })
     }
   },
 
   onPullDownRefresh() {
-    this.initData().finally(() => {
+    this.initializeReviewData().finally(() => {
       wx.stopPullDownRefresh()
     })
   },
@@ -81,7 +92,7 @@
     }
   },
 
-  async initData() {
+  async initializeReviewData() {
     this.setData({ loading: true, page: 1, list: [], hasMore: true })
 
     try {
@@ -134,6 +145,9 @@
       const result = await graphqlRequest(LIST_QUERY, variables)
       const pageData = result && result.employeeReviewApplications
       const items = pageData && Array.isArray(pageData.content) ? pageData.content : []
+      items.forEach(item => {
+        item.applyTime = formatTime(item.applyTime)
+      })
       const list = reset ? items : this.data.list.concat(items)
       const total = pageData && typeof pageData.totalElements === 'number' ? pageData.totalElements : 0
       const hasMore = nextPage * this.data.pageSize < total
@@ -160,13 +174,13 @@
   },
 
   onSearch() {
-    this.initData()
+    this.initializeReviewData()
   },
 
   clearSearch() {
     if (!this.data.searchKeyword) return
     this.setData({ searchKeyword: '' })
-    this.initData()
+    this.initializeReviewData()
   },
 
   switchTab(e) {
@@ -182,7 +196,7 @@
       hasMore: true
     })
 
-    this.initData()
+    this.initializeReviewData()
   },
 
   getStateByTab(index) {
diff --git a/wx/pages/profile/employee-review.wxml b/wx/pages/profile/employee-review.wxml
index a1c4e8c..c7eb086 100644
--- a/wx/pages/profile/employee-review.wxml
+++ b/wx/pages/profile/employee-review.wxml
@@ -1,16 +1,18 @@
 <view class="container">
-  <view class="search-bar">
-    <input
-      class="search-input"
-      value="{{searchKeyword}}"
-      placeholder="鎼滅储姣旇禌 / 椤圭洰 / 瀛﹀憳"
-      bindinput="onSearchInput"
-      confirm-type="search"
-      bindconfirm="onSearch"
-    />
-    <view class="search-actions">
-      <button class="primary-btn" bindtap="onSearch">鎼滅储</button>
-      <button class="plain-btn" bindtap="clearSearch" wx:if="{{searchKeyword}}">娓呯┖</button>
+  <view class="search-container">
+    <view class="search-box">
+      <text class="icon search-icon ic-search"></text>
+      <input
+        class="search-input"
+        placeholder="鎼滅储姣旇禌 / 椤圭洰 / 瀛﹀憳"
+        value="{{searchKeyword}}"
+        bindinput="onSearchInput"
+        confirm-type="search"
+        bindconfirm="onSearch"
+      />
+      <view class="search-clear" wx:if="{{searchKeyword}}" bindtap="clearSearch">
+        <text class="icon clear-icon ic-close"></text>
+      </view>
     </view>
   </view>
 
@@ -32,7 +34,14 @@
   </view>
 
   <view class="list-container">
-    <view wx:if="{{list.length > 0}}">
+    <block wx:if="{{loading && list.length === 0}}">
+      <view class="loading-state">
+        <text class="icon loading-icon ic-refresh"></text>
+        <text class="loading-text">姝e湪鍔犺浇</text>
+      </view>
+    </block>
+
+    <block wx:elif="{{list.length > 0}}">
       <view class="audit-card" wx:for="{{list}}" wx:key="id">
         <view class="card-header">
           <text class="card-title">{{item.projectName || '鏈懡鍚嶉」鐩�'}}</text>
@@ -51,7 +60,6 @@
           <text class="card-value">{{item.applyTime || '-'}}</text>
         </view>
         <view class="card-footer">
-          <text class="card-tip">鏈�鏂扮姸鎬侊細{{item.stateText || '鏈煡'}}</text>
           <button class="action-btn" data-id="{{item.id}}" bindtap="goToDetail">瀹℃牳</button>
         </view>
       </view>
@@ -61,10 +69,10 @@
       <view class="load-more" wx:elif="{{!hasMore}}">
         <text>娌℃湁鏇村鏁版嵁</text>
       </view>
-    </view>
+    </block>
 
     <view wx:else class="empty-state">
-      <text class="empty-icon">馃搨</text>
+      <text class="icon empty-icon ic-list"></text>
       <text class="empty-text">鏆傛棤绗﹀悎鏉′欢鐨勯」鐩�</text>
       <text class="empty-desc">灏濊瘯璋冩暣绛涢�夋潯浠舵垨鎼滅储鍏朵粬鍏抽敭璇�</text>
     </view>
diff --git a/wx/pages/profile/employee-review.wxss b/wx/pages/profile/employee-review.wxss
index 9979dbd..bfe6a0f 100644
--- a/wx/pages/profile/employee-review.wxss
+++ b/wx/pages/profile/employee-review.wxss
@@ -1,50 +1,52 @@
+@import "../../style/icon.wxss";
+
 .container {
   min-height: 100vh;
   background: #f5f5f5;
   padding: 24rpx;
 }
 
-.search-bar {
+.search-container {
+  padding: 30rpx;
+  background: #ffffff;
+  border-bottom: 1rpx solid #e0e0e0;
+}
+
+.search-box {
   display: flex;
   align-items: center;
-  gap: 16rpx;
-  margin-bottom: 24rpx;
+  background: #f8f9fa;
+  border-radius: 24rpx;
+  padding: 0 30rpx;
+  height: 80rpx;
+}
+
+.search-icon {
+  font-size: 36rpx;
+  color: #999999;
+  margin-right: 20rpx;
 }
 
 .search-input {
   flex: 1;
-  height: 72rpx;
-  background: #ffffff;
-  border-radius: 36rpx;
-  padding: 0 24rpx;
   font-size: 28rpx;
-  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+  color: #333333;
 }
 
-.search-actions {
+.search-clear {
+  width: 40rpx;
+  height: 40rpx;
   display: flex;
-  gap: 12rpx;
+  align-items: center;
+  justify-content: center;
+  background: rgba(22, 119, 255, 0.15);
+  border-radius: 20rpx;
+  margin-left: 20rpx;
 }
 
-.primary-btn {
-  background: #1677ff;
-  color: #ffffff;
-  border-radius: 36rpx;
-  padding: 0 32rpx;
-  height: 72rpx;
-  line-height: 72rpx;
-  font-size: 28rpx;
-}
-
-.plain-btn {
-  background: #ffffff;
+.clear-icon {
+  font-size: 26rpx;
   color: #1677ff;
-  border-radius: 36rpx;
-  padding: 0 32rpx;
-  height: 72rpx;
-  line-height: 72rpx;
-  font-size: 28rpx;
-  border: 2rpx solid #1677ff;
 }
 
 .tab-bar {
@@ -85,6 +87,35 @@
   gap: 20rpx;
 }
 
+.loading-state {
+  padding: 120rpx 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 12rpx;
+  color: #999999;
+}
+
+.loading-icon {
+  font-size: 48rpx;
+  color: #1677ff;
+  animation: spin 1.2s linear infinite;
+}
+
+.loading-text {
+  font-size: 26rpx;
+  color: #666666;
+}
+
+@keyframes spin {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
 .audit-card {
   background: #ffffff;
   border-radius: 16rpx;
@@ -93,6 +124,7 @@
   display: flex;
   flex-direction: column;
   gap: 16rpx;
+  margin-bottom: 20rpx;
 }
 
 .card-header {
@@ -146,7 +178,7 @@
 
 .card-footer {
   display: flex;
-  justify-content: space-between;
+  justify-content: flex-end;
   align-items: center;
   margin-top: 8rpx;
 }
@@ -164,6 +196,8 @@
   line-height: 64rpx;
   border-radius: 32rpx;
   font-size: 26rpx;
+  margin: 0;
+  border: none;
 }
 
 .load-more {
@@ -184,6 +218,7 @@
 
 .empty-icon {
   font-size: 72rpx;
+  color: rgba(22, 119, 255, 0.8);
 }
 
 .empty-text {
diff --git a/wx/pages/project/detail.js b/wx/pages/project/detail.js
index 49a5ed8..5e56ab7 100644
--- a/wx/pages/project/detail.js
+++ b/wx/pages/project/detail.js
@@ -5,18 +5,12 @@
   data: {
     projectId: '',
     projectDetail: null,
-    timeline: [],
+    ratingStats: null,
     loading: true,
     error: '',
     statusText: '',
     genderText: '',
-    educationText: '',
-    timelineLoading: false,
-    timelineError: '',
-    showRatingDetail: false,
-    ratingDetail: null,
-    ratingDetailLoading: false,
-    ratingDetailError: ''
+    educationText: ''
   },
 
   onLoad(options) {
@@ -36,38 +30,53 @@
   // 鍔犺浇椤圭洰璇︽儏
   async loadProjectDetail() {
     try {
-      this.setData({
-        loading: true,
-        error: ''
+      this.setData({ 
+        loading: true, 
+        error: '' 
       })
 
+      // 璋冪敤API鑾峰彇椤圭洰璇︽儏
       const projectDetail = await this.getProjectDetailFromAPI(this.data.projectId)
+      
+      if (projectDetail) {
+        // 澶勭悊鏂囦欢澶у皬鏄剧ず
+        if (projectDetail.submissionFiles) {
+          projectDetail.submissionFiles.forEach(file => {
+            file.fileSizeText = this.formatFileSize(file.fileSize)
+            // 瀛楁宸茬粡鏄纭殑鍚嶇О锛屾棤闇�鏄犲皠
+            // fullUrl, fullThumbUrl, fileSize, fileExt 閮芥槸姝g‘鐨勫瓧娈靛悕
+          })
+        }
 
-      if (!projectDetail) {
+        // 鑾峰彇璇勫垎缁熻
+        const ratingStats = await this.getRatingStatsFromAPI(this.data.projectId)
+        
+        // 澶勭悊璇勫垎鏃堕棿鏄剧ず
+        if (ratingStats && ratingStats.judgeRatings) {
+          ratingStats.judgeRatings.forEach(rating => {
+            if (rating.ratingTime) {
+              rating.ratingTimeText = this.formatDateTime(rating.ratingTime)
+            }
+          })
+        }
+
+        this.setData({
+          projectDetail,
+          ratingStats,
+          statusText: this.getStatusText(projectDetail.state),
+          genderText: this.getGenderText(projectDetail.playerInfo?.gender),
+          educationText: this.getEducationText(projectDetail.playerInfo?.education),
+          loading: false
+        })
+      } else {
         throw new Error('椤圭洰璇︽儏鑾峰彇澶辫触')
       }
-
-      if (projectDetail.submissionFiles) {
-        projectDetail.submissionFiles.forEach(file => {
-          file.fileSizeText = this.formatFileSize(file.fileSize)
-        })
-      }
-
-      this.setData({
-        projectDetail,
-        statusText: this.getStatusText(projectDetail.state),
-        genderText: this.getGenderText(projectDetail.playerInfo?.gender),
-        educationText: this.getEducationText(projectDetail.playerInfo?.education)
-      })
-
-      await this.loadProjectTimeline(this.data.projectId)
     } catch (error) {
       console.error('鍔犺浇椤圭洰璇︽儏澶辫触:', error)
       this.setData({
-        error: error.message || '鍔犺浇澶辫触锛岃閲嶈瘯'
+        error: error.message || '鍔犺浇澶辫触锛岃閲嶈瘯',
+        loading: false
       })
-    } finally {
-      this.setData({ loading: false })
     }
   },
 
@@ -146,174 +155,37 @@
     }
   },
 
-  async loadProjectTimeline(activityPlayerId) {
-    if (!activityPlayerId) {
-      return
+  // 鑾峰彇璇勫垎缁熻
+  async getRatingStatsFromAPI(projectId) {
+    // 鏆傛椂杩斿洖绌虹殑璇勫垎鏁版嵁锛岄伩鍏岹raphQL鏌ヨ閿欒
+    // TODO: 闇�瑕佸悗绔彁渚涘悎閫傜殑璇勫垎缁熻鏌ヨ鎺ュ彛
+    try {
+      return {
+        averageScore: null,
+        ratingCount: 0,
+        judgeRatings: []
+      }
+    } catch (error) {
+      throw error
     }
+  },
 
-    const idNumber = Number(activityPlayerId)
-    const variables = {
-      activityPlayerId: Number.isNaN(idNumber) ? activityPlayerId : idNumber
-    }
-
-    this.setData({
-      timelineLoading: true,
-      timelineError: ''
-    })
-
+  // 鑾峰彇璇勫璇勫垎璇︽儏
+  async getJudgeRatingDetail(activityPlayerId, judgeId) {
     const query = `
-      query ProjectStageTimeline($activityPlayerId: ID!) {
-        projectStageTimeline(activityPlayerId: $activityPlayerId) {
-          activityId
-          activityName
-          stages {
-            stageId
-            stageName
-            matchTime
-            sortOrder
-            participated
-            activityPlayerId
-            averageScore
-            ratingCount
-            hasRating
-            latestRatingTime
-          }
+      query GetJudgeRatingDetail($activityPlayerId: ID!, $judgeId: ID!) {
+        judgeRatingDetail(activityPlayerId: $activityPlayerId, judgeId: $judgeId) {
+          remark
         }
       }
     `
 
     try {
-      const result = await app.graphqlRequest(query, variables)
-      const projectStageTimeline = result && result.projectStageTimeline ? result.projectStageTimeline : null
-      const stages = projectStageTimeline && projectStageTimeline.stages ? projectStageTimeline.stages : []
-
-      const timeline = stages.map(stage => {
-        const hasScore = stage.hasRating && stage.averageScore !== null && stage.averageScore !== undefined
-        let scoreText = '鏈弬璧�'
-        if (stage.participated) {
-          scoreText = hasScore ? `骞冲潎鍒嗭細${Number(stage.averageScore).toFixed(2)}` : '鏈瘎鍒�'
-        }
-
-        return {
-          ...stage,
-          matchTimeText: stage.matchTime ? this.formatDateTime(stage.matchTime) : '',
-          scoreText,
-          displayAverageScore: hasScore ? Number(stage.averageScore).toFixed(2) : null,
-          isClickable: stage.participated && hasScore && !!stage.activityPlayerId
-        }
-      })
-
-      this.setData({
-        timeline,
-        timelineLoading: false
-      })
+      const result = await app.graphqlRequest(query, { activityPlayerId, judgeId })
+      return result.judgeRatingDetail
     } catch (error) {
-      console.error('鍔犺浇闃舵鏃堕棿杞村け璐�:', error)
-      this.setData({
-        timelineError: error.message || '鏃堕棿杞村姞杞藉け璐�',
-        timelineLoading: false
-      })
+      throw error
     }
-  },
-
-  async fetchStageRatingDetail(activityPlayerId) {
-    const idNumber = Number(activityPlayerId)
-    const variables = {
-      activityPlayerId: Number.isNaN(idNumber) ? activityPlayerId : idNumber
-    }
-
-    const query = `
-      query StageJudgeRatings($activityPlayerId: ID!) {
-        stageJudgeRatings(activityPlayerId: $activityPlayerId) {
-          activityPlayerId
-          stageId
-          stageName
-          matchTime
-          ratingCount
-          averageScore
-          judgeRatings {
-            judgeId
-            judgeName
-            totalScore
-            feedback
-            ratingTime
-          }
-        }
-      }
-    `
-
-    const result = await app.graphqlRequest(query, variables)
-    const detail = result && result.stageJudgeRatings ? result.stageJudgeRatings : null
-
-    const sourceJudgeRatings = detail && detail.judgeRatings ? detail.judgeRatings : []
-    const judgeRatings = sourceJudgeRatings.map(item => ({
-      ...item,
-      totalScoreText: item.totalScore !== null && item.totalScore !== undefined ? `${Number(item.totalScore).toFixed(2)}鍒哷 : '鏈瘎鍒�',
-      ratingTimeText: item.ratingTime ? this.formatDateTime(item.ratingTime) : ''
-    }))
-
-    const averageScoreValue = detail && detail.averageScore !== undefined && detail.averageScore !== null
-      ? detail.averageScore
-      : null
-
-    return {
-      activityPlayerId: detail && detail.activityPlayerId ? detail.activityPlayerId : variables.activityPlayerId,
-      stageId: detail && detail.stageId ? detail.stageId : null,
-      stageName: detail && detail.stageName ? detail.stageName : '闃舵淇℃伅',
-      matchTime: detail && detail.matchTime ? detail.matchTime : null,
-      matchTimeText: detail && detail.matchTime ? this.formatDateTime(detail.matchTime) : '',
-      ratingCount: detail && detail.ratingCount ? detail.ratingCount : 0,
-      averageScore: averageScoreValue,
-      averageScoreText: averageScoreValue !== null ? Number(averageScoreValue).toFixed(2) : '鏆傛棤璇勫垎',
-      judgeRatings
-    }
-  },
-
-  async openStageDetail(e) {
-    const { playerId } = e.currentTarget.dataset
-    const clickable = e.currentTarget.dataset.clickable === true || e.currentTarget.dataset.clickable === 'true'
-    const participated = e.currentTarget.dataset.participated === true || e.currentTarget.dataset.participated === 'true'
-
-    if (!playerId || !participated) {
-      return
-    }
-
-    if (!clickable) {
-      wx.showToast({
-        title: '鏆傛棤璇勫垎',
-        icon: 'none'
-      })
-      return
-    }
-
-    this.setData({
-      showRatingDetail: true,
-      ratingDetailLoading: true,
-      ratingDetailError: '',
-      ratingDetail: { judgeRatings: [] }
-    })
-
-    try {
-      const detail = await this.fetchStageRatingDetail(playerId)
-      this.setData({
-        ratingDetail: detail,
-        ratingDetailLoading: false
-      })
-    } catch (error) {
-      console.error('鍔犺浇闃舵璇勫垎璇︽儏澶辫触:', error)
-      this.setData({
-        ratingDetailError: error.message || '鍔犺浇澶辫触',
-        ratingDetailLoading: false
-      })
-    }
-  },
-
-  closeStageDetail() {
-    this.setData({
-      showRatingDetail: false,
-      ratingDetail: null,
-      ratingDetailError: ''
-    })
   },
 
   // 棰勮鏂囦欢
diff --git a/wx/pages/review/index.js b/wx/pages/review/index.js
index 3a9dd0e..b469a0b 100644
--- a/wx/pages/review/index.js
+++ b/wx/pages/review/index.js
@@ -363,4 +363,4 @@
     ]
     return emptyDescs[currentTab] || ''
   }
-})
+})
\ No newline at end of file
diff --git "a/\345\206\205\345\256\271\347\224\261 AI \347\224\237\346\210\220.txt" "b/\345\206\205\345\256\271\347\224\261 AI \347\224\237\346\210\220.txt"
deleted file mode 100644
index 05976e7..0000000
--- "a/\345\206\205\345\256\271\347\224\261 AI \347\224\237\346\210\220.txt"
+++ /dev/null
@@ -1,990 +0,0 @@
-鍐呭鐢� AI 鐢熸垚
-#钃夋槗鍒涢」鐩�
-##  閲嶈鎻愮ず锛屼笉鍙繚鍙�
-1.	鍏ㄧ▼浣跨敤涓枃锛�
-2.	涓嶈淇敼鍜屼綘鏃犲叧鐨勪唬鐮侊紒
-3.	 淇敼浠讳綍鍔熻兘閮藉厛瀵瑰簲鏌ヨ瀵瑰簲妯″潡鐨勫姛鑳借姹傦紒
-4.	鎵�鏈夋柊澧炪�佺紪杈戦〉闈㈤兘鏄叕鐢ㄤ竴涓〉闈€��
-5.	姣忎釜瀹炰綋绫婚兘鏈夊搴旂殑Input绫诲瀷锛宨d=null 灏辫〃绀烘柊澧炪�俰d !=null 灏辫〃绀轰慨鏀广��
-6.	user 鍏宠仈鐨勫ご鍍忥紝 activity鍏宠仈鐨勮棰戝浘鐗囷紝carousel 鍏宠仈鐨勮棰戝拰鍥惧儚锛岄兘瀵瑰簲 t_media 鏁版嵁琛紝瀹炰綋绫籑edia . 閫氳繃Target
-## 闇�姹傛弿杩�
-	 瀛﹀憳鐧诲綍灏忕▼搴忥紝鐐瑰嚮鎶ュ悕锛屽綍鍏ユ姤鍚嶄俊鎭紝宸ヤ綔浜哄憳瀹℃牳鎶ュ悕璧勬枡銆� 璇勫璇勫鎶ュ悕璧勬枡锛屽畨瑁呮瘮璧涘搴旂殑璇勫垎琛ㄧ粰瀛﹀憳鎵撳垎锛屽苟缁欏嚭璇勮銆�
-## web闇�姹�
-	1. 鐧诲綍椤甸潰锛� 鎵嬫満鍙峰拰瀵嗙爜鐧诲綍銆傚悓鏃堕鐣� 寰俊鎵弿鐧诲綍銆�
-	2. 鐧诲綍鍚庨椤垫槸鏍囧噯鐨勫伐浣滃彴妯℃澘鏍峰紡
-		2.1 宸﹁竟鏄彍鍗曟爮鐩�  鍖呮嫭锛氭瘮璧涚鐞嗭紝璇勫绠$悊锛岃瘎鍒嗘ā鏉裤�傚弬璧涗汉鍛橈紝鏂伴椈涓庢帹骞裤�� 鍛樺伐绠$悊
-		2.2  鍙宠竟鏄伐浣滃尯锛岄椤垫樉绀�  褰撳墠杩涜姣旇禌锛� 鍙傝禌浜烘暟锛屾姤鍚嶅緟瀹℃牳浜烘暟銆�
-	3.   姣旇禌绠$悊
-		3.1 姣旇禌鍒楄〃
-			3.1.1 涓婇潰鏌ヨ妗嗭紝鍙互鎸夊悕绉版煡璇紝
-			 3.1.2  鍙宠竟鏄�愭柊澧炴瘮璧涖�� 
-			3.1.2   涓嬮潰鏄瘮璧涘垪琛ㄥ睍绀猴細鍚嶇О锛屾姤鍚嶄汉鏁帮紝姣旇禌鏃堕棿锛屾姤鍚嶆埅鑷虫椂闂淬��
-			3.1.2    鍚庨潰鍙� 鏌ョ湅閫夋墜锛岀紪杈戯紝鍒犻櫎绛夋搷浣溿��
-		3.2     姣旇禌璇︽儏锛�
-			 	3.2.1    鍚嶇О锛� 鏈�澶ч暱搴﹂檺鍒讹紝灏忎簬30涓瓧绗�
-				3.2.2    鎶ュ悕鎴嚦鏃ユ湡  锛岄�氳繃鏃ュ巻閫夋嫨 
-				3.2.3     姣旇禌寮�濮嬫椂闂达紝 骞存湀鏃� 鍜屾椂鍒嗐��
-				3.3.4     姣旇禌鍦板潃銆�
-				3.3.5    鏈�澶у弬璧涗汉鏁�
-				3.3.5     姣旇禌鎻忚堪
-				3.3.6    閫夋嫨璇勫垎妯℃澘
-				3.3.7.  閫夋嫨瑙嗛鍜屽浘鐗囥�� 鍙傝�冿細璁捐瑕佺偣 閮ㄥ垎
-				3.3.7    姣旇禌闃舵瀹氫箟
-					閲嶈锛� 姣旇禌闃舵鍜屾瘮璧涘叕鐢ㄤ竴涓疄浣撶被鍜屾暟鎹〃锛坱_activity) .閫氳繃pid 鍏宠仈銆�
-					3.3.7.1    鍚嶇О锛� 鏈�澶ч暱搴﹂檺鍒讹紝灏忎簬30涓瓧绗�
- 					3.3.7.2     姣旇禌寮�濮嬫椂闂达紝 骞存湀鏃� 鍜屾椂鍒嗐��
- 					3.3.7.3     姣旇禌鍦板潃銆�
- 					3.3.7.4    鏈�澶у弬璧涗汉鏁�
- 					3.3.7.5     姣旇禌鎻忚堪
- 					3.3.7.6    閫夋嫨璇勫垎妯℃澘锛岄粯璁ゆ槸姣旇禌鐨勬ā鏉匡紝涔熷彲浠ュ崟鐙�夋嫨銆�
-					3.3.7.7     鍒楄〃鍙宠竟鏄� 鏌ョ湅閫夋墜锛� 缂栬緫锛屽垹闄ょ瓑鎿嶄綔
-		4.璇勫绠$悊  瀵瑰簲 Judge 
-			4.1 璇勫鍒楄〃  鏄剧ず 澶村儚锛屽悕绉帮紝涓撲笟鏍囩锛� 鐢佃瘽锛�  鎬у埆
-			4.2  璇勫璇︽儏锛� 鍚嶇О锛屽ご鍍忥紝 鎬у埆锛屼笓涓氭爣绛撅紝鍙互澶氶�夛紝  涓撲笟鏍囩鍙傝�冦�愯璁¤鐐圭3鐐广�戯紝璇︽儏銆� 
-		4. 璇勫垎妯$鐞�  瀵瑰簲 RatingScheme 
-			4.1 璇勫垎妯℃澘鍒楄〃銆�  鍚嶇О锛� 鎬诲垎銆�		
-			4.2  璇勫垎妯℃澘璇︽儏锛�  鍚嶇О锛� 涓嬮潰鏄瘎鍒嗘潯鐩垪琛紝 鍙互鏂板鍜屽垹闄わ紝锛堝凡缁忕粦瀹氬埌姣旇禌鐨勪笉鑳藉垹闄わ級銆�  璇勫垎鏉$洰鍖呮嫭 鍚嶇О鍜屽垎鍊笺��
-		5. 瀛﹀憳绠$悊
-			5.1 瀛﹀憳鍒楄〃  鏄剧ず澶村儚锛屽悕绉帮紝鎶ュ悕鐨勬瘮璧涳紝  鐢宠鏃堕棿銆�
-			5.2	瀛﹀憳璇︽儏椤甸潰锛� 鍚嶇О锛屽ご鍍忥紝鐢佃瘽锛� 
-			5.2.1  瀛﹀憳璇︽儏椤甸潰鐨� 姣旇禌鍒楄〃锛�
-					1.涓嬮潰鍒楄〃鏄弬鍔犳姤鍚嶇殑姣旇禌锛堜竴涓弬璧涗汉鍙互鎶ュ悕鍙傚姞澶氫釜姣旇禌锛夛紝鍖呮嫭姣旇禌鍚嶇О鍜屽綋鍓嶇姸鎬併�� 鐘舵�佸寘鎷細甯﹀鏍革紝杩涜涓�� 寰楀垎锛屽悕娆$瓑淇℃伅銆�
-					2.  寰楀垎锛屽悕娆′俊鎭湪姣旇禌缁撴潫鍚庝笉鍙慨鏀癸紒
-					3. activityPlayer鐨勭姸鎬侊細0锛氬垹闄わ紝 1锛氬緟瀹℃牳锛�2锛氳繘琛屼腑锛堝鏍搁�氳繃锛夛紝 3锛氱粨鏉熴��
-		6.  鏂伴椈  瀵瑰簲carousel
-			6.1 鏂伴椈鍒楄〃   鍚嶇О锛屽垱寤烘椂闂淬�� 
-			6.2  鏂伴椈鎾斁    瀵瑰簲 t_carousel_sort ,  鏌ヨ鎸夐挳鍚庨潰鏈変竴涓� 鎸夐挳 銆愯缃挱鏀俱�戯紝 鐐瑰嚮鍒颁竴涓缃〉闈紝涓婇潰鏄釜鏌ヨ妗嗭紝涓嬮潰鏄釜鍒楄〃锛屽垪琛ㄥ悗闈㈡槸鎾斁椤哄簭璁剧疆.
-			6.2 鏂伴椈璇︽儏 锛氬悕绉帮紝鍐呭锛� 瑙嗛鍜屽浘鐗�  璁剧疆鎾斁椤哄簭
-		7.  鏉冮檺瑙掕壊銆�
-			閲囩敤鏍囧噯鐨凴BC妯″瀷
-			浣嗘槸瑙掕壊鍐呯疆锛屼笉鍙慨鏀瑰拰缂栬緫锛� 瀛﹀憳锛屽钩鍙帮紝璇勫锛岀鐞嗗憳銆�  
-		8.	涓撲笟鏍囩绠$悊
-			8.1 涓撲笟鏍囩鍒楄〃锛� 鍙互鏂板鏍囩锛�  鏂板鐨勬爣绛綾ode 榛樿閲囩敤鍚嶇О銆�  
-		9. 鍙戠敓娑堟伅绫诲埆
-			9.1 鏄剧ず鎵�鏈夊凡缁忓彂鐢熺殑娑堟伅銆�
-## 寰俊绔�
-	鏆傛椂鐣欑┖
- 
-
-## 璁捐瑕佺偣
-	1. 鍒楄〃閫氱敤璁捐锛�	閮芥湁鏌ヨ锛� 鏂板
-	2. user鐨勫ご鍍忥紝 player鐨勫ご鍍忥紝 activity 鐨勮棰戝拰鍥剧墖锛宎ctiviityPlayer 鍏宠仈鐨勬姤鍚嶄汉涓婁紶鐨勮祫鏂欙紝judge鐨勫ご鍍忥紝  employee鐨勫ご鍍忥紝锛宑arousel 鐨勮棰戝拰鍥惧儚鐨勮矾寰勯兘璁板綍鍦╰_media琛ㄥ唴銆�  閫氳繃TargetType鍖哄埆
-		瀹氫箟涓�涓灇涓剧被鍨嬭鍖呮嫭涓婅堪鐨勭被鍨嬨��	
-	3. 鏍囩Tag  瀵瑰簲t_tag 琛紝 閲岄潰閫氳繃category 鏉ュ尯鍒嗕笉鍚岀殑鏍囩绫诲瀷銆傛瘮濡傦細涓撲笟鏍囩鐨刢ategory = 'major'.
-
-## 鐜閰嶇疆
-	1. 寮�鍙戠幆澧冩槸windows 锛屽懡浠よ鏄痯owersehll. 涓嶈鎼為敊浜嗭紒
-	2.  java 25 鐨勭洰褰曞湪 : C:\Program Files\Java\jdk-25
-	  3.  
-##鎶�鏈鑼�
-	1. 鐩綍瑙勮寖锛�
-	鏍圭洰褰曚笅鍒哹ackend锛堝悗鍙帮級銆亀eb锛堥鐣欏墠绔級銆亀x锛堝皬绋嬪簭锛夛紝backend鍐呴儴鎸夋ā鍧楀垝鍒嗭紝缁撴瀯濡備笅锛氶噸鐐癸細姣忎釜涓氬姟妯″潡閮藉寘鎷琣pi,  dto锛� entity锛� service锛宺epository 绛夌洰褰曪紒锛侊紒
-
-	backend/src/main/java/com/rongyichuang/
-	鈹溾攢鈹� config/ # 閰嶇疆绫伙紙RabbitMQ銆丟raphQL銆佽法鍩熺瓑锛�
-	鈹溾攢鈹� judge/ # 璇勫妯″潡
-	鈹� 鈹溾攢鈹� api/ # GraphQL鎺ュ彛锛堝JudgeGraphqlApi锛�
-	鈹� 鈹溾攢鈹� entity/ # 瀹炰綋绫伙紙濡侸udge.java锛�
-	鈹� 鈹溾攢鈹� dto/ # 鏁版嵁浼犺緭瀵硅薄锛坮equest/response锛�
-	鈹� 鈹溾攢鈹� service/ # 涓氬姟绫伙紙濡侸udgeService.java锛屾棤鎺ュ彛锛�
-	鈹� 鈹斺攢鈹� util/ # 妯″潡鍐呭伐鍏风被
-	鈹溾攢鈹� rating/ # 璇勫垎妯℃澘妯″潡锛堢粨鏋勫悓judge锛�
-	鈹溾攢鈹� activity/ # 姣旇禌妯″潡锛堢粨鏋勫悓judge锛�
-	鈹溾攢鈹� player/ # 鍙傝禌浜哄憳妯″潡锛堢粨鏋勫悓judge锛�
-	鈹溾攢鈹� carousel/ # 杞挱鍥炬ā鍧楋紙缁撴瀯鍚宩udge锛�
-	鈹溾攢鈹� employee/ # 骞冲彴鍛樺伐妯″潡锛堢粨鏋勫悓judge锛�
-	鈹斺攢鈹� common/ # 鍏叡妯″潡
-	鈹溾攢鈹� entity/ # 鍩虹瀹炰綋锛堝BaseEntity.java锛屽惈id/state锛�
-	鈹溾攢鈹� dto/ # 鍏叡DTO锛堝PageRequest.java锛�
-	鈹溾攢鈹� exception/ # 鍏ㄥ眬寮傚父澶勭悊锛堝GlobalExceptionHandler.java锛�
-	鈹斺攢鈹� util/ # 鍏ㄥ眬宸ュ叿绫伙紙濡侱ateUtil.java锛�
-	2.  **Grpahqls鏂囦欢鏀惧湪 resources\graphqls鐩綍涓嬮潰锛�  姣忎釜妯″潡瀵瑰簲涓�涓猤raphqls鏂囦欢銆�
-	3. spring security +token
-	4. **浜嬪姟鎺у埗**锛歴ervice鏍稿績鏂规硶锛堝璇勫璇勫垎銆佸鍛樺鏍革級娣诲姞@Transactional锛岀‘淇濇暟鎹竴鑷存�с��
-	5. **閫昏緫鍒犻櫎**锛氭墍鏈夊疄浣撶被鍚玸tate瀛楁锛坕nt(1)锛岄粯璁�1锛夛紝JPA鐖舵帴鍙e姞@Where(clause = "state = 1")锛屾煡璇㈣嚜鍔ㄨ繃婊ゅ凡鍒犻櫎鏁版嵁銆�  娉ㄦ剰锛歛ctivityPlayer 鐨剆atate 鍚箟鍜屽叾瀹冪殑涓嶅悓
-	6.  瑙嗛锛屽浘鐗囩洿鎺ヤ笂浼犲埌鑵捐浜戝瓨鍌ㄦ《閲岄潰
-	7. 瀛樺偍妗剁洰褰曪紝鎸墆yyyMMyy 璁剧疆鐩綍锛屾瘡澶╃殑璧勬簮閮芥斁鍦ㄥ搴旂殑鐩綍涓嬨��
-##璧勬簮
-### 3.4 鏁版嵁搴撹繛鎺ヤ俊鎭�
-- 绫诲瀷锛歮ysql
-- IP锛�140.143.152.226
-- 绔彛锛�3308
-- 鏁版嵁搴擄細ryc
-- 鐢ㄦ埛鍚嶏細openai
-- 瀵嗙爜锛歑ml@uk2025
-
-### 3.5 RabbitMQ杩炴帴淇℃伅
-rabbitmq:
-host: 140.143.152.226
-port: 5672
-virtual-host: ryc_prod
-username: admin
-password: ycl@2020
-connection-timeout: 15000
-
-###  浜戝瓨鍌ㄤ俊鎭�
-    SecretId: 'AKIDZ4Pcj9zbgaZgl6yJf0HSXXHzfGl9PcdT',
-    SecretKey: 'qjYFnCas0xseEdsxrsobKuJiwzrZ7dzC',
-    Bucket: 'ryc-1256886520',
-    Region: 'ap-chengdu'
-
-- 绫诲瀷锛歮ysql
-- IP锛�140.143.152.226
-- 绔彛锛�3308
-- 鏁版嵁搴擄細ryc
-- 鐢ㄦ埛鍚嶏細openai
-- 瀵嗙爜锛歑ml@uk2025
-
-### 3.5 RabbitMQ杩炴帴淇℃伅
-rabbitmq:
-host: 140.143.152.226
-port: 5672
-virtual-host: ryc_prod
-username: admin
-password: ycl@2020
-connection-timeout: 15000
-
-###  浜戝瓨鍌ㄤ俊鎭�
-
-    SecretId: 'AKIDZ4Pcj9zbgaZgl6yJf0HSXXHzfGl9PcdT',
-    SecretKey: 'qjYFnCas0xseEdsxrsobKuJiwzrZ7dzC',
-    Bucket: 'ryc-1256886520',
-    Region: 'ap-chengdu'
-
-				鎶婅繖涓枃妗� 鏁寸悊瑙勮寖涓�涓嬶紝灏ゅ叾鏍囬鍜屽簭鍙凤紝杩樻湁缂╄繘銆�
-钃夋槗鍒涢」鐩�
-涓�銆侀噸瑕佹彁绀猴紙涓嶅彲杩濆弽锛�
-鍏ㄧ▼浣跨敤涓枃锛�
-涓嶈淇敼鍜屼綘鏃犲叧鐨勪唬鐮侊紒
-淇敼浠讳綍鍔熻兘閮藉厛瀵瑰簲鏌ヨ瀵瑰簲妯″潡鐨勫姛鑳借姹傦紒
-鎵�鏈夋柊澧炪�佺紪杈戦〉闈㈤兘鏄叕鐢ㄤ竴涓〉闈€��
-姣忎釜瀹炰綋绫婚兘鏈夊搴旂殑Input绫诲瀷锛宨d=null 灏辫〃绀烘柊澧烇紱id !=null 灏辫〃绀轰慨鏀广��
-user 鍏宠仈鐨勫ご鍍忋�乤ctivity鍏宠仈鐨勮棰戝浘鐗囥�乧arousel 鍏宠仈鐨勮棰戝拰鍥惧儚锛岄兘瀵瑰簲 t_media 鏁版嵁琛紙瀹炰綋绫籑edia锛夛紝閫氳繃Target鍏宠仈銆�
-浜屻�侀渶姹傛弿杩�
-瀛﹀憳鐧诲綍灏忕▼搴忥紝鐐瑰嚮鎶ュ悕锛屽綍鍏ユ姤鍚嶄俊鎭紝宸ヤ綔浜哄憳瀹℃牳鎶ュ悕璧勬枡锛涜瘎濮旇瘎瀹℃姤鍚嶈祫鏂欙紝鎸夌収姣旇禌瀵瑰簲鐨勮瘎鍒嗚〃缁欏鍛樻墦鍒嗭紝骞剁粰鍑鸿瘎璁恒��
-2.1 Web闇�姹�
-2.1.1 鐧诲綍椤甸潰
-鏀寔鎵嬫満鍙峰拰瀵嗙爜鐧诲綍锛�
-棰勭暀寰俊鎵弿鐧诲綍鍏ュ彛銆�
-2.1.2 鐧诲綍鍚庨椤碉紙鏍囧噯宸ヤ綔鍙版ā鏉挎牱寮忥級
-宸︿晶锛氳彍鍗曟爮鐩紝鍖呭惈銆屾瘮璧涚鐞嗐�嶃�岃瘎濮旂鐞嗐�嶃�岃瘎鍒嗘ā鏉裤�嶃�屽弬璧涗汉鍛樸�嶃�屾柊闂讳笌鎺ㄥ箍銆嶃�屽憳宸ョ鐞嗐�嶏紱
-鍙充晶锛氬伐浣滃尯锛岄椤垫樉绀恒�屽綋鍓嶈繘琛屾瘮璧涖�嶃�屽弬璧涗汉鏁般�嶃�屾姤鍚嶅緟瀹℃牳浜烘暟銆嶃��
-2.1.3 姣旇禌绠$悊锛堝搴斿疄浣撶被锛欰ctivity锛�
-2.1.3.1 姣旇禌鍒楄〃
-椤堕儴锛氭煡璇㈡锛堟敮鎸佹寜姣旇禌鍚嶇О鏌ヨ锛夛紱
-鍙充晶锛氥�愭柊澧炴瘮璧涖�戞寜閽紱
-鍒楄〃灞曠ず瀛楁锛氬悕绉般�佹姤鍚嶄汉鏁般�佹瘮璧涙椂闂淬�佹姤鍚嶆埅鑷虫椂闂达紱
-鍒楄〃鎿嶄綔椤癸細鏌ョ湅閫夋墜銆佺紪杈戙�佸垹闄ゃ��
-2.1.3.2 姣旇禌璇︽儏
-鍚嶇О锛堟渶澶ч暱搴﹂檺鍒讹細灏忎簬30涓瓧绗︼級锛�
-鎶ュ悕鎴嚦鏃ユ湡锛堥�氳繃鏃ュ巻閫夋嫨锛夛紱
-姣旇禌寮�濮嬫椂闂达紙绮剧‘鍒板勾鏈堟棩鏃跺垎锛夛紱
-姣旇禌鍦板潃锛�
-鏈�澶у弬璧涗汉鏁帮紱
-姣旇禌鎻忚堪锛�
-閫夋嫨璇勫垎妯℃澘锛�
-閫夋嫨瑙嗛鍜屽浘鐗囷紙鍙傝�冦�屽洓銆佽璁¤鐐广�嶉儴鍒嗭級锛�
-姣旇禌闃舵瀹氫箟锛堥噸瑕侊細姣旇禌闃舵鍜屾瘮璧涘叕鐢ㄤ竴涓疄浣撶被鍜屾暟鎹〃t_activity锛岄�氳繃pid鍏宠仈锛夛細
-闃舵鍚嶇О锛堟渶澶ч暱搴﹂檺鍒讹細灏忎簬30涓瓧绗︼級锛�
-闃舵姣旇禌寮�濮嬫椂闂达紙绮剧‘鍒板勾鏈堟棩鏃跺垎锛夛紱
-闃舵姣旇禌鍦板潃锛�
-闃舵鏈�澶у弬璧涗汉鏁帮紱
-闃舵姣旇禌鎻忚堪锛�
-閫夋嫨璇勫垎妯℃澘锛堥粯璁ょ户鎵挎瘮璧涙ā鏉匡紝鏀寔鍗曠嫭閫夋嫨锛夛紱
-闃舵鍒楄〃鎿嶄綔椤癸細鏌ョ湅閫夋墜銆佺紪杈戙�佸垹闄ゃ��
-2.1.4 璇勫绠$悊锛堝搴斿疄浣撶被锛欽udge锛�
-2.1.4.1 璇勫鍒楄〃
-鍒楄〃灞曠ず瀛楁锛氬ご鍍忋�佸悕绉般�佷笓涓氭爣绛俱�佺數璇濄�佹�у埆銆�
-2.1.4.2 璇勫璇︽儏
-鍩虹淇℃伅锛氬悕绉般�佸ご鍍忋�佹�у埆銆佷笓涓氭爣绛撅紙鏀寔澶氶�夛紝鍙傝�冦�屽洓銆佽璁¤鐐圭3鐐广�嶏級銆佽鎯呮弿杩般��
-2.1.5 璇勫垎妯℃澘绠$悊锛堝搴斿疄浣撶被锛歊atingScheme锛�
-2.1.5.1 璇勫垎妯℃澘鍒楄〃
-鍒楄〃灞曠ず瀛楁锛氬悕绉般�佹�诲垎銆�
-2.1.5.2 璇勫垎妯℃澘璇︽儏
-鍩虹淇℃伅锛氭ā鏉垮悕绉帮紱
-璇勫垎鏉$洰绠$悊锛氭敮鎸佹柊澧炪�佸垹闄よ瘎鍒嗘潯鐩紙宸茬粦瀹氬埌姣旇禌鐨勬ā鏉夸笉鍙垹闄わ級锛�
-璇勫垎鏉$洰瀛楁锛氬悕绉般�佸垎鍊笺��
-2.1.6 瀛﹀憳绠$悊锛堝搴斿疄浣撶被锛歅layer锛�
-2.1.6.1 瀛﹀憳鍒楄〃
-鍒楄〃灞曠ず瀛楁锛氬ご鍍忋�佸悕绉般�佹姤鍚嶇殑姣旇禌銆佺敵璇锋椂闂淬��
-2.1.6.2 瀛﹀憳璇︽儏椤甸潰
-鍩虹淇℃伅锛氬悕绉般�佸ご鍍忋�佺數璇濓紱
-鍙傝禌璁板綍鍒楄〃锛堜竴涓鍛樺彲鎶ュ悕澶氫釜姣旇禌锛夛細
-鍒楄〃灞曠ず瀛楁锛氭瘮璧涘悕绉般�佸綋鍓嶇姸鎬侊紱
-鐘舵�佽鏄庯細0-鍒犻櫎銆�1-寰呭鏍搞��2-杩涜涓紙瀹℃牳閫氳繃锛夈��3-缁撴潫锛�
-鐗规畩瑙勫垯锛氬緱鍒嗐�佸悕娆′俊鎭湪姣旇禌缁撴潫鍚庝笉鍙慨鏀广��
-2.1.7 鏂伴椈绠$悊锛堝搴斿疄浣撶被锛欳arousel锛�
-2.1.7.1 鏂伴椈鍒楄〃
-鍒楄〃灞曠ず瀛楁锛氬悕绉般�佸垱寤烘椂闂淬��
-2.1.7.2 鏂伴椈鎾斁璁剧疆锛堝搴旇〃锛歵_carousel_sort锛�
-鎿嶄綔鍏ュ彛锛氭煡璇㈡寜閽彸渚с�愯缃挱鏀俱�戞寜閽紱
-璁剧疆椤甸潰锛氶《閮ㄦ煡璇㈡锛屼笅鏂瑰垪琛紙鍒楄〃鎿嶄綔椤癸細璁剧疆鎾斁椤哄簭锛夈��
-2.1.7.3 鏂伴椈璇︽儏
-鍩虹淇℃伅锛氬悕绉般�佸唴瀹癸紱
-濯掍綋璁剧疆锛氳棰戝拰鍥剧墖閰嶇疆锛�
-鎾斁璁剧疆锛氬畾涔夋挱鏀鹃『搴忋��
-2.1.8 鏉冮檺瑙掕壊
-閲囩敤鏍囧噯RBC妯″瀷锛�
-瑙掕壊瑙勫垯锛氬唴缃鑹诧紙涓嶅彲淇敼/缂栬緫锛夛紝鍖呮嫭銆屽鍛樸�嶃�屽钩鍙般�嶃�岃瘎濮斻�嶃�岀鐞嗗憳銆嶃��
-2.1.9 涓撲笟鏍囩绠$悊锛堝搴旇〃锛歵_tag锛�
-2.1.9.1 涓撲笟鏍囩鍒楄〃
-鏀寔鍔熻兘锛氭柊澧炴爣绛撅紱
-榛樿瑙勫垯锛氭柊澧炴爣绛剧殑code榛樿涓庡悕绉颁竴鑷淬��
-2.1.10 鍙戦�佹秷鎭被鍒�
-鍔熻兘锛氬睍绀烘墍鏈夊凡鍙戦�佺殑娑堟伅銆�
-2.2 寰俊绔�
-鏆傛椂鐣欑┖
-涓夈�佺幆澧冮厤缃�
-寮�鍙戠幆澧冿細Windows锛屽懡浠よ宸ュ叿锛歅owerShell锛�
-JDK閰嶇疆锛欽ava 25锛屽畨瑁呯洰褰曪細C:\Program Files\Java\jdk-25銆�
-鍥涖�佽璁¤鐐�
-鍒楄〃閫氱敤璁捐锛氭墍鏈夊垪琛ㄩ〉闈㈠潎鍖呭惈銆屾煡璇€�嶃�屾柊澧炪�嶅姛鑳斤紱
-濯掍綋璧勬簮瀛樺偍瑙勫垯锛�
-鐢ㄦ埛澶村儚锛坲ser锛夈�侀�夋墜澶村儚锛坧layer锛夈�佹瘮璧涘獟浣擄紙activity鐨勮棰�/鍥剧墖锛夈�佸弬璧涙姤鍚嶈祫鏂欙紙activiityPlayer锛夈�佽瘎濮斿ご鍍忥紙judge锛夈�佸憳宸ュご鍍忥紙employee锛夈�佹柊闂诲獟浣擄紙carousel鐨勮棰�/鍥剧墖锛夛紝鍧囧瓨鍌ㄤ簬t_media琛紱
-閫氳繃銆孴argetType銆嶆灇涓惧尯鍒嗚祫婧愮被鍨嬶紙闇�瀹氫箟鍖呭惈涓婅堪鎵�鏈夌被鍨嬬殑鏋氫妇锛夛紱
-鏍囩绠$悊瑙勫垯锛�
-鏍囩瀛樺偍浜巘_tag琛紝閫氳繃銆宑ategory銆嶅瓧娈靛尯鍒嗘爣绛剧被鍨嬶紙渚嬶細涓撲笟鏍囩鐨刢ategory = 'major'锛夈��
-浜斻�佹妧鏈鑼�
-5.1 鐩綍瑙勮寖
-鏍圭洰褰曚笅鍒哹ackend锛堝悗鍙帮級銆亀eb锛堥鐣欏墠绔級銆亀x锛堝皬绋嬪簭锛夛紝backend鍐呴儴鎸夋ā鍧楀垝鍒嗭紝缁撴瀯濡備笅锛�
-plaintext
-backend/src/main/java/com/rongyichuang/
-鈹溾攢鈹� config/ # 閰嶇疆绫伙紙RabbitMQ銆丟raphQL銆佽法鍩熺瓑锛�
-鈹溾攢鈹� judge/ # 璇勫妯″潡
-鈹�   鈹溾攢鈹� api/ # GraphQL鎺ュ彛锛堝JudgeGraphqlApi锛�
-鈹�   鈹溾攢鈹� entity/ # 瀹炰綋绫伙紙濡侸udge.java锛�
-鈹�   鈹溾攢鈹� dto/ # 鏁版嵁浼犺緭瀵硅薄锛坮equest/response锛�
-鈹�   鈹溾攢鈹� service/ # 涓氬姟绫伙紙濡侸udgeService.java锛屾棤鎺ュ彛锛�
-鈹�   鈹斺攢鈹� util/ # 妯″潡鍐呭伐鍏风被
-鈹溾攢鈹� rating/ # 璇勫垎妯℃澘妯″潡锛堢粨鏋勫悓judge锛�
-鈹溾攢鈹� activity/ # 姣旇禌妯″潡锛堢粨鏋勫悓judge锛�
-鈹溾攢鈹� player/ # 鍙傝禌浜哄憳妯″潡锛堢粨鏋勫悓judge锛�
-鈹溾攢鈹� carousel/ # 杞挱鍥炬ā鍧楋紙缁撴瀯鍚宩udge锛�
-鈹溾攢鈹� employee/ # 骞冲彴鍛樺伐妯″潡锛堢粨鏋勫悓judge锛�
-鈹斺攢鈹� common/ # 鍏叡妯″潡
-    鈹溾攢鈹� entity/ # 鍩虹瀹炰綋锛堝BaseEntity.java锛屽惈id/state锛�
-    鈹溾攢鈹� dto/ # 鍏叡DTO锛堝PageRequest.java锛�
-    鈹溾攢鈹� exception/ # 鍏ㄥ眬寮傚父澶勭悊锛堝GlobalExceptionHandler.java锛�
-    鈹斺攢鈹� util/ # 鍏ㄥ眬宸ュ叿绫伙紙濡侱ateUtil.java锛�
-5.2 GraphQL閰嶇疆
-Graphqls鏂囦欢缁熶竴鏀惧湪 resources\graphqls 鐩綍锛屾瘡涓笟鍔℃ā鍧楀搴斾竴涓猤raphqls鏂囦欢銆�
-5.3 瀹夊叏妗嗘灦
-閲囩敤Spring Security + Token瀹炵幇韬唤璁よ瘉涓庢巿鏉冦��
-5.4 浜嬪姟鎺у埗
-service灞傛牳蹇冩柟娉曪紙濡傝瘎濮旇瘎鍒嗐�佸鍛樺鏍革級闇�娣诲姞@Transactional娉ㄨВ锛岀‘淇濇暟鎹竴鑷存�с��
-5.5 閫昏緫鍒犻櫎
-鎵�鏈夊疄浣撶被闇�鍖呭惈state瀛楁锛堢被鍨嬶細int(1)锛岄粯璁ゅ�硷細1锛夛紱
-JPA鐖舵帴鍙i渶娣诲姞@Where(clause = "state = 1")锛屾煡璇㈡椂鑷姩杩囨护宸插垹闄ゆ暟鎹紱
-鐗规畩璇存槑锛歛ctivityPlayer鐨剆tate鍚箟涓庡叾浠栧疄浣撲笉鍚岋紙鍙傝��2.1.6.2鐘舵�佽鏄庯級銆�
-5.6 浜戝瓨鍌ㄨ鑼�
-瀛樺偍鐩爣锛氳棰戙�佸浘鐗囩洿鎺ヤ笂浼犺嚦鑵捐浜戝瓨鍌ㄦ《锛�
-鐩綍瑙勫垯锛氭寜銆寉yyyMMdd銆嶆牸寮忓垱寤虹洰褰曪紝褰撴棩璧勬簮瀛樺叆瀵瑰簲鐩綍銆�
-鍏�佽祫婧愪俊鎭�
-6.1 鏁版嵁搴撹繛鎺ヤ俊鎭紙MySQL锛�
-绫诲瀷锛歁ySQL
-IP锛�140.143.152.226
-绔彛锛�3308
-鏁版嵁搴撳悕锛歳yc
-鐢ㄦ埛鍚嶏細openai
-瀵嗙爜锛歑ml@uk2025
-6.2 RabbitMQ杩炴帴淇℃伅
-yaml
-rabbitmq:
-  host: 140.143.152.226
-  port: 5672
-  virtual-host: ryc_prod
-  username: admin
-  password: ycl@2020
-  connection-timeout: 15000
-6.3 鑵捐浜戝瓨鍌ㄤ俊鎭�
-SecretId: AKIDZ4Pcj9zbgaZgl6yJf0HSXXHzfGl9PcdT
-SecretKey: qjYFnCas0xseEdsxrsobKuJiwzrZ7dzC
-Bucket: ryc-1256886520
-Region: ap-chengdu
-鑳芥敼鎴恗d鏍煎紡鍚�
-浠ヤ笅鏄畬鍏ㄩ伒寰� Markdown锛圡D锛夎鑼� 鏁寸悊鐨勩�岃搲鏄撳垱椤圭洰銆嶆枃妗o紝鍖呭惈鏍囧噯鏍囬灞傜骇銆佸垪琛ㄧ缉杩涖�佷唬鐮佸潡銆侀噸鐐瑰姞绮楃瓑鏍煎紡锛屽彲鐩存帴澶嶅埗浣跨敤锛�
-钃夋槗鍒涢」鐩�
-涓�銆侀噸瑕佹彁绀猴紙涓嶅彲杩濆弽锛�
-鍏ㄧ▼浣跨敤涓枃锛�
-涓嶈淇敼鍜屼綘鏃犲叧鐨勪唬鐮侊紒
-淇敼浠讳綍鍔熻兘閮藉厛瀵瑰簲鏌ヨ瀵瑰簲妯″潡鐨勫姛鑳借姹傦紒
-鎵�鏈夋柊澧炪�佺紪杈戦〉闈㈤兘鏄叕鐢ㄤ竴涓〉闈€��
-姣忎釜瀹炰綋绫婚兘鏈夊搴旂殑 Input 绫诲瀷锛歩d=null 琛ㄧず鏂板锛宨d鈮爊ull 琛ㄧず淇敼銆�
-鍏宠仈濯掍綋璧勬簮瑙勫垯锛�
-user 鍏宠仈鐨勫ご鍍忋�乤ctivity 鍏宠仈鐨勮棰� / 鍥剧墖銆乧arousel 鍏宠仈鐨勮棰� / 鍥惧儚锛屽潎瀵瑰簲 t_media 鏁版嵁琛紙瀹炰綋绫伙細Media锛夛紱
-閫氳繃 Target 瀛楁鍏宠仈鍖哄垎銆�
-浜屻�侀渶姹傛弿杩�
-瀛﹀憳鐧诲綍灏忕▼搴� 鈫� 鐐瑰嚮鎶ュ悕骞跺綍鍏ユ姤鍚嶄俊鎭� 鈫� 宸ヤ綔浜哄憳瀹℃牳鎶ュ悕璧勬枡 鈫� 璇勫璇勫璧勬枡锛屾寜姣旇禌瀵瑰簲璇勫垎琛ㄧ粰瀛﹀憳鎵撳垎骞跺~鍐欒瘎璁恒��
-涓夈�乄eb 绔渶姹�
-3.1 鐧诲綍椤甸潰
-鏍稿績鍔熻兘锛氭敮鎸併�屾墜鏈哄彿 + 瀵嗙爜銆嶇櫥褰曪紱
-棰勭暀鍔熻兘锛氬井淇℃壂鐮佺櫥褰曞叆鍙c��
-3.2 鐧诲綍鍚庨椤碉紙鏍囧噯宸ヤ綔鍙版ā鏉匡級
-鍖哄煙	鍐呭鎻忚堪
-宸︿晶鑿滃崟	鍖呭惈銆屾瘮璧涚鐞嗐�嶃�岃瘎濮旂鐞嗐�嶃�岃瘎鍒嗘ā鏉裤�嶃�屽弬璧涗汉鍛樸�嶃�屾柊闂讳笌鎺ㄥ箍銆嶃�屽憳宸ョ鐞嗐��
-鍙充晶宸ヤ綔鍖�	灞曠ず鏍稿績鏁版嵁锛氬綋鍓嶈繘琛屾瘮璧涖�佸弬璧涙�讳汉鏁般�佹姤鍚嶅緟瀹℃牳浜烘暟
-3.3 姣旇禌绠$悊锛堝疄浣撶被锛欰ctivity锛�
-3.3.1 姣旇禌鍒楄〃
-椤堕儴鍔熻兘锛氭煡璇㈡锛堟敮鎸佹寜銆屾瘮璧涘悕绉般�嶆ā绯婃煡璇級銆併�愭柊澧炴瘮璧涖�戞寜閽紱
-鍒楄〃瀛楁锛氭瘮璧涘悕绉般�佹姤鍚嶄汉鏁般�佹瘮璧涙椂闂淬�佹姤鍚嶆埅鑷虫椂闂达紱
-鎿嶄綔鍒楋細鏌ョ湅閫夋墜銆佺紪杈戙�佸垹闄ゃ��
-3.3.2 姣旇禌璇︽儏
-鍩虹淇℃伅閰嶇疆锛�
-姣旇禌鍚嶇О锛堟渶澶ч暱搴︹墹30 瀛楃锛夛紱
-鎶ュ悕鎴嚦鏃ユ湡锛堟棩鍘嗙粍浠堕�夋嫨锛夛紱
-姣旇禌寮�濮嬫椂闂达紙绮剧‘鍒般�屽勾鏈堟棩 鏃跺垎銆嶏級锛�
-姣旇禌鍦板潃銆佹渶澶у弬璧涗汉鏁般�佹瘮璧涙弿杩帮紱
-鍏宠仈閰嶇疆锛氶�夋嫨璇勫垎妯℃澘銆佷笂浼犺棰� / 鍥剧墖锛堝弬鑰冦�屼簲銆佽璁¤鐐广�嶏級銆�
-姣旇禌闃舵瀹氫箟锛堥噸瑕侊級锛�
-鏁版嵁鍏宠仈锛氭瘮璧涢樁娈典笌姣旇禌鍏辩敤 t_activity 琛紝閫氳繃 pid 鍏宠仈锛堢埗 ID 涓烘瘮璧� ID锛夛紱
-闃舵閰嶇疆椤癸細
-闃舵鍚嶇О锛堟渶澶ч暱搴︹墹30 瀛楃锛夛紱
-闃舵寮�濮嬫椂闂达紙绮剧‘鍒般�屽勾鏈堟棩 鏃跺垎銆嶏級锛�
-闃舵鍦板潃銆侀樁娈垫渶澶у弬璧涗汉鏁般�侀樁娈垫弿杩帮紱
-璇勫垎妯℃澘锛堥粯璁ょ户鎵挎瘮璧涙ā鏉匡紝鏀寔鍗曠嫭閫夋嫨锛夛紱
-闃舵鍒楄〃鎿嶄綔锛氭煡鐪嬮�夋墜銆佺紪杈戙�佸垹闄ゃ��
-3.4 璇勫绠$悊锛堝疄浣撶被锛欽udge锛�
-3.4.1 璇勫鍒楄〃
-灞曠ず瀛楁锛氬ご鍍忋�佽瘎濮斿悕绉般�佷笓涓氭爣绛俱�佽仈绯荤數璇濄�佹�у埆銆�
-3.4.2 璇勫璇︽儏
-鍩虹淇℃伅锛氳瘎濮斿悕绉般�佸ご鍍忋�佹�у埆銆佽仈绯荤數璇濓紱
-鏍囩閰嶇疆锛氫笓涓氭爣绛撅紙鏀寔澶氶�夛紝鍙傝�冦�屼簲銆佽璁¤鐐� 3銆嶏級锛�
-琛ュ厖淇℃伅锛氳瘎濮旇鎯呮弿杩般��
-3.5 璇勫垎妯℃澘绠$悊锛堝疄浣撶被锛歊atingScheme锛�
-3.5.1 璇勫垎妯℃澘鍒楄〃
-灞曠ず瀛楁锛氭ā鏉垮悕绉般�佹ā鏉挎�诲垎銆�
-3.5.2 璇勫垎妯℃澘璇︽儏
-鍩虹閰嶇疆锛氭ā鏉垮悕绉帮紱
-鏉$洰绠$悊锛�
-鏀寔銆屾柊澧� / 鍒犻櫎銆嶈瘎鍒嗘潯鐩紙宸茬粦瀹氭瘮璧涚殑妯℃澘涓嶅彲鍒犻櫎锛夛紱
-鏉$洰瀛楁锛氭潯鐩悕绉般�佹潯鐩垎鍊笺��
-3.6 瀛﹀憳绠$悊锛堝疄浣撶被锛歅layer锛�
-3.6.1 瀛﹀憳鍒楄〃
-灞曠ず瀛楁锛氬鍛樺ご鍍忋�佸鍛樺悕绉般�佹姤鍚嶇殑姣旇禌鍚嶇О銆佺敵璇锋椂闂淬��
-3.6.2 瀛﹀憳璇︽儏
-鍩虹淇℃伅锛氬鍛樺悕绉般�佸ご鍍忋�佽仈绯荤數璇濓紱
-鍙傝禌璁板綍鍒楄〃锛�1 涓鍛樺彲鎶ュ涓瘮璧涳級锛�
-灞曠ず瀛楁锛氭瘮璧涘悕绉般�佸綋鍓嶇姸鎬侊紱
-鐘舵�佽鏄庯細0-鍒犻櫎銆�1-寰呭鏍搞��2-杩涜涓紙瀹℃牳閫氳繃锛夈��3-缁撴潫锛�
-鐗规畩瑙勫垯锛氭瘮璧涚粨鏉熷悗锛屻�屽緱鍒嗐�嶃�屽悕娆°�嶄笉鍙慨鏀广��
-3.7 鏂伴椈绠$悊锛堝疄浣撶被锛欳arousel锛�
-3.7.1 鏂伴椈鍒楄〃
-灞曠ず瀛楁锛氭柊闂诲悕绉般�佸垱寤烘椂闂淬��
-3.7.2 鏂伴椈鎾斁璁剧疆锛堝叧鑱旇〃锛歵_carousel_sort锛�
-鎿嶄綔鍏ュ彛锛氬垪琛ㄦ煡璇㈡寜閽彸渚� 鈫� 銆愯缃挱鏀俱�戞寜閽紱
-璁剧疆椤甸潰锛�
-椤堕儴锛氭柊闂绘煡璇㈡锛�
-涓嬫柟锛氭柊闂诲垪琛紝鎿嶄綔鍒楁敮鎸併�岃缃挱鏀鹃『搴忋�嶃��
-3.7.3 鏂伴椈璇︽儏
-鍐呭閰嶇疆锛氭柊闂诲悕绉般�佹柊闂绘鏂囷紱
-濯掍綋閰嶇疆锛氫笂浼犳柊闂诲叧鑱旂殑瑙嗛 / 鍥剧墖锛�
-鎾斁閰嶇疆锛氳缃棰� / 鍥剧墖鐨勬挱鏀鹃『搴忋��
-3.8 鏉冮檺瑙掕壊
-妯″瀷锛氶噰鐢ㄦ爣鍑� RBC锛堝熀浜庤鑹茬殑璁块棶鎺у埗锛夋ā鍨嬶紱
-瑙掕壊瑙勫垯锛氬唴缃� 4 绫昏鑹诧紙涓嶅彲淇敼 / 缂栬緫锛夛細瀛﹀憳銆佸钩鍙颁汉鍛樸�佽瘎濮斻�佺鐞嗗憳銆�
-3.9 涓撲笟鏍囩绠$悊锛堝叧鑱旇〃锛歵_tag锛�
-3.9.1 涓撲笟鏍囩鍒楄〃
-鏀寔鍔熻兘锛氭柊澧炰笓涓氭爣绛撅紱
-榛樿瑙勫垯锛氭柊澧炴爣绛剧殑 code 涓庢爣绛惧悕绉颁竴鑷淬��
-3.10 娑堟伅绠$悊
-鍔熻兘锛氬睍绀烘墍鏈夊凡鍙戦�佺殑绯荤粺娑堟伅锛堝惈娑堟伅鍐呭銆佸彂閫佹椂闂淬�佹帴鏀跺璞$瓑锛夈��
-鍥涖�佸井淇$闇�姹�
-鏆傛椂鐣欑┖
-浜斻�佽璁¤鐐�
-鍒楄〃閫氱敤瑙勫垯锛氭墍鏈夋ā鍧楃殑鍒楄〃椤甸潰锛屽潎闇�鍖呭惈銆屾煡璇€�嶃�屾柊澧炪�嶅姛鑳斤紱
-濯掍綋璧勬簮瀛樺偍锛堝叧鑱旇〃锛歵_media锛夛細
-瑕嗙洊鍦烘櫙锛歶ser 澶村儚銆乸layer 澶村儚銆乤ctivity 瑙嗛 / 鍥剧墖銆乤ctivityPlayer 鎶ュ悕璧勬枡銆乯udge 澶村儚銆乪mployee 澶村儚銆乧arousel 瑙嗛 / 鍥剧墖锛�
-鍖哄垎瑙勫垯锛氶�氳繃 TargetType 鏋氫妇鍖哄垎璧勬簮绫诲瀷锛堥渶瀹氫箟鍖呭惈涓婅堪鎵�鏈夊満鏅殑鏋氫妇锛夛紱
-鏍囩绠$悊锛堣〃锛歵_tag锛夛細
-鍖哄垎瑙勫垯锛氶�氳繃 category 瀛楁鍖哄垎鏍囩绫诲瀷锛堜緥锛氫笓涓氭爣绛剧殑 category = 'major'锛夈��
-鍏�佺幆澧冮厤缃�
-寮�鍙戠幆澧冿細Windows 绯荤粺锛屽懡浠よ宸ュ叿锛歅owerShell锛�
-JDK 閰嶇疆锛欽ava 25锛屽畨瑁呰矾寰勶細C:\Program Files\Java\jdk-25銆�
-涓冦�佹妧鏈鑼�
-7.1 鐩綍瑙勮寖
-鏍圭洰褰曟寜銆岀 / 妯″潡銆嶆媶鍒嗭紝backend 鍐呴儴鎸夈�屼笟鍔℃ā鍧椼�嶅垝鍒嗭紝缁撴瀯濡備笅锛�
-plaintext
-backend/src/main/java/com/rongyichuang/
-鈹溾攢鈹� config/          # 鍏ㄥ眬閰嶇疆锛圧abbitMQ銆丟raphQL銆佽法鍩熺瓑锛�
-鈹溾攢鈹� judge/           # 璇勫妯″潡
-鈹�   鈹溾攢鈹� api/         # GraphQL鎺ュ彛锛堝JudgeGraphqlApi.java锛�
-鈹�   鈹溾攢鈹� entity/      # 瀹炰綋绫伙紙濡侸udge.java锛�
-鈹�   鈹溾攢鈹� dto/         # 鏁版嵁浼犺緭瀵硅薄锛堝垎request/response瀛愮洰褰曪級
-鈹�   鈹溾攢鈹� service/     # 涓氬姟閫昏緫锛堝JudgeService.java锛屾棤鍗曠嫭鎺ュ彛灞傦級
-鈹�   鈹斺攢鈹� util/        # 妯″潡鍐呭伐鍏风被
-鈹溾攢鈹� rating/          # 璇勫垎妯℃澘妯″潡锛堢粨鏋勫悓judge锛�
-鈹溾攢鈹� activity/        # 姣旇禌妯″潡锛堢粨鏋勫悓judge锛�
-鈹溾攢鈹� player/          # 鍙傝禌浜哄憳妯″潡锛堢粨鏋勫悓judge锛�
-鈹溾攢鈹� carousel/        # 杞挱鍥撅紙鏂伴椈锛夋ā鍧楋紙缁撴瀯鍚宩udge锛�
-鈹溾攢鈹� employee/        # 骞冲彴鍛樺伐妯″潡锛堢粨鏋勫悓judge锛�
-鈹斺攢鈹� common/          # 鍏叡妯″潡
-    鈹溾攢鈹� entity/      # 鍩虹瀹炰綋锛堝BaseEntity.java锛屽惈id/state锛�
-    鈹溾攢鈹� dto/         # 鍏叡DTO锛堝PageRequest.java锛�
-    鈹溾攢鈹� exception/   # 鍏ㄥ眬寮傚父澶勭悊锛堝GlobalExceptionHandler.java锛�
-    鈹斺攢鈹� util/        # 鍏ㄥ眬宸ュ叿绫伙紙濡侱ateUtil.java锛�
-7.2 GraphQL 閰嶇疆
-鏂囦欢璺緞锛歳esources/graphqls/锛�
-鍛藉悕瑙勫垯锛氭瘡涓笟鍔℃ā鍧楀搴� 1 涓� graphqls 鏂囦欢锛堜緥锛氳瘎濮旀ā鍧� 鈫� judge.graphqls锛夈��
-7.3 瀹夊叏妗嗘灦
-閲囩敤 Spring Security + Token 瀹炵幇韬唤璁よ瘉涓庢帴鍙f巿鏉冦��
-7.4 浜嬪姟鎺у埗
-浣滅敤鑼冨洿锛歴ervice 灞傛牳蹇冧笟鍔℃柟娉曪紙濡傝瘎濮旇瘎鍒嗐�佸鍛樺鏍革級锛�
-瀹炵幇鏂瑰紡锛氭坊鍔� @Transactional 娉ㄨВ锛岀‘淇濇暟鎹竴鑷存�с��
-7.5 閫昏緫鍒犻櫎
-閫氱敤瑙勫垯锛氭墍鏈夊疄浣撶被鍚� state 瀛楁锛堢被鍨嬶細int (1)锛岄粯璁ゅ�硷細1锛夛紱
-鏌ヨ杩囨护锛欽PA 鐖舵帴鍙f坊鍔� @Where(clause = "state = 1")锛岃嚜鍔ㄨ繃婊ゅ凡鍒犻櫎鏁版嵁锛�
-鐗规畩璇存槑锛歛ctivityPlayer 鐨� state 鍚箟涓庡叾浠栧疄浣撲笉鍚岋紙鍙傝�� 3.6.2 鐘舵�佽鏄庯級銆�
-7.6 浜戝瓨鍌ㄨ鑼�
-瀛樺偍鐩爣锛氳棰戙�佸浘鐗囦笂浼犺嚦銆岃吘璁簯瀛樺偍妗躲�嶏紱
-鐩綍瑙勫垯锛氭寜銆寉yyyMMdd銆嶏紙骞存湀鏃ワ級鍒涘缓鐩綍锛屽綋鏃ヨ祫婧愬瓨鍏ュ搴旂洰褰曘��
-鍏�佽祫婧愪俊鎭�
-8.1 鏁版嵁搴撹繛鎺ワ紙MySQL锛�
-鍙傛暟	閰嶇疆鍊�
-绫诲瀷	MySQL
-IP	140.143.152.226
-绔彛	3308
-鏁版嵁搴撳悕	ryc
-鐢ㄦ埛鍚�	openai
-瀵嗙爜	Xml@uk2025
-8.2 RabbitMQ 杩炴帴
-yaml
-rabbitmq:
-  host: 140.143.152.226
-  port: 5672
-  virtual-host: ryc_prod
-  username: admin
-  password: ycl@2020
-  connection-timeout: 15000
-8.3 鑵捐浜戝瓨鍌紙COS锛�
-SecretId锛欰KIDZ4Pcj9zbgaZgl6yJf0HSXXHzfGl9PcdT
-SecretKey锛歲jYFnCas0xseEdsxrsobKuJiwzrZ7dzC
-Bucket锛歳yc-1256886520
-Region锛歛p-chengdu
-濡傛灉瑕佺敤杩欎釜鏉ュ仛寮�鍙戯紝灏ゅ叾鏄AI 鏉ュ仛寮�鍙戯紝杩橀渶瑕佺粏鍖栭偅浜涘唴瀹�
-瑕佽 AI 鍩轰簬杩欎唤鏂囨。楂樻晥寮�鍙戯紝闇�瑕佷粠銆屽疄浣撳畾涔夈�佷笟鍔℃祦绋嬨�佹妧鏈粏鑺傘�佹帴鍙h鑼冦�嶇瓑缁村害杩涜缁嗗寲锛岃ˉ鍏� AI 寮�鍙戞墍闇�鐨勩�岀‘瀹氭�т俊鎭�嶏紙閬垮厤姝т箟锛夊拰銆岀害鏉熻鍒欍�嶏紙鏄庣‘杈圭晫锛夈�備互涓嬫槸闇�瑕侀噸鐐圭粏鍖栫殑鍐呭锛�
-涓�銆佹牳蹇冨疄浣撶被涓庢暟鎹簱琛ㄧ粨鏋勶紙蹇呴』缁嗗寲锛�
-褰撳墠鏂囨。浠呮彁鍒板疄浣撶被鍚嶇О锛堝 Judge銆丄ctivity锛夛紝浣嗙己灏戝瓧娈靛畾涔夈�佹暟鎹被鍨嬨�佺害鏉熷叧绯伙紝AI 鏃犳硶鐢熸垚瀹炰綋绫诲拰琛ㄧ粨鏋勩�傞渶琛ュ厖锛�
-1. 瀹炰綋绫诲瓧娈垫槑缁嗭紙鍚暟鎹簱鏄犲皠锛�
-绀轰緥锛堜互Activity涓轰緥锛夛細
-瀹炰綋瀛楁鍚�	鏁版嵁搴撳瓧娈靛悕	鏁版嵁绫诲瀷	绾︽潫瑙勫垯锛堥潪绌� / 闀垮害 / 榛樿鍊硷級	璇存槑
-id	id	bigint	涓婚敭锛岃嚜澧烇紝闈炵┖	姣旇禌 ID
-name	name	varchar(30)	闈炵┖锛岄暱搴︹墹30	姣旇禌鍚嶇О
-enrollEndTime	enroll_end_time	datetime	闈炵┖	鎶ュ悕鎴鏃堕棿
-startTime	start_time	datetime	闈炵┖	姣旇禌寮�濮嬫椂闂达紙绮剧‘鍒版椂鍒嗭級
-address	address	varchar(255)	鍙负绌�	姣旇禌鍦板潃
-maxPlayerCount	max_player_count	int	闈炵┖锛岄粯璁� 0锛屸墺0	鏈�澶у弬璧涗汉鏁�
-description	description	text	鍙负绌�	姣旇禌鎻忚堪
-ratingSchemeId	rating_scheme_id	bigint	鍙负绌猴紙鍏宠仈 RatingScheme.id锛�	鍏宠仈鐨勮瘎鍒嗘ā鏉� ID
-pid	pid	bigint	榛樿涓� 0锛�0 琛ㄧず姣旇禌锛屸墵0 琛ㄧず闃舵锛�	鐖� ID锛堝叧鑱旇嚜韬� id锛岀敤浜庨樁娈碉級
-state	state	tinyint	闈炵┖锛岄粯璁� 1锛�1 - 姝e父锛�0 - 鍒犻櫎锛�	閫昏緫鍒犻櫎鏍囪瘑
-闇�鎸夋鏍煎紡琛ュ厖鎵�鏈夊疄浣撶被锛圝udge銆丷atingScheme銆丳layer銆丮edia銆乀ag 绛夛級銆�
-2. 琛ㄥ叧绯讳笌澶栭敭绾︽潫
-鏄庣‘琛ㄤ箣闂寸殑鍏宠仈瑙勫垯锛屼緥濡傦細
-t_activity锛堟瘮璧� / 闃舵锛夌殑pid澶栭敭鍏宠仈鑷韩鐨刬d锛堣嚜鍏宠仈锛夛紱
-t_rating_scheme_item锛堣瘎鍒嗘潯鐩級鐨剆cheme_id澶栭敭鍏宠仈t_rating_scheme鐨刬d锛�
-t_media鐨則arget_id鍏宠仈瀵瑰簲瀹炰綋鐨刬d锛堝 user 鐨� id銆乤ctivity 鐨� id锛夛紝target_type鏋氫妇鍊奸渶鏄庣‘锛堝USER_AVATAR=1銆丄CTIVITY_VIDEO=2锛夈��
-浜屻�佷笟鍔℃祦绋嬩笌鐘舵�佹祦杞紙鍏抽敭缁嗗寲锛�
-褰撳墠鏂囨。浠呮弿杩板姛鑳界偣锛岀己灏戞祦绋嬫楠ゃ�佺姸鎬佽Е鍙戞潯浠讹紝AI 鏃犳硶瀹炵幇涓氬姟閫昏緫銆傞渶琛ュ厖锛�
-1. 鏍稿績娴佺▼姝ラ
-绀轰緥锛堝鍛樻姤鍚� - 瀹℃牳 - 璇勫垎娴佺▼锛夛細
-瀛﹀憳鎶ュ悕锛�
-杈撳叆锛氬鍛� ID銆佹瘮璧� ID銆佹姤鍚嶈祫鏂欙紙鍏宠仈 t_media 鐨勮祫婧� ID锛夛紱
-鏍¢獙锛氭瘮璧涙槸鍚﹀湪鎶ュ悕鏈熷唴锛堝綋鍓嶆椂闂粹墹enrollEndTime锛夈�佹槸鍚﹀凡杈炬渶澶т汉鏁帮紱
-杈撳嚭锛氱敓鎴� ActivityPlayer 璁板綍锛坰tate=1 - 寰呭鏍革級銆�
-宸ヤ綔浜哄憳瀹℃牳锛�
-鎿嶄綔锛氬鏍搁�氳繃 / 鎷掔粷锛�
-瑙﹀彂鏉′欢锛氫粎绠$悊鍛� / 骞冲彴瑙掕壊鍙搷浣滐紱
-鐘舵�佸彉鏇达細閫氳繃鈫抯tate=2锛堣繘琛屼腑锛夛紱鎷掔粷鈫抯tate=0锛堝垹闄わ紝鎴栨柊澧� reject_reason 瀛楁锛夛紱
-閫氱煡锛氬鏍哥粨鏋滈�氳繃 RabbitMQ 鍙戦�佹秷鎭粰瀛﹀憳锛堥渶鏄庣‘娑堟伅鏍煎紡锛夈��
-璇勫璇勫垎锛�
-鏉冮檺锛氳瘎濮斿彧鑳界粰宸插叧鑱旂殑姣旇禌 / 闃舵鎵撳垎锛堥渶鏂板judge_activity鍏宠仈琛級锛�
-杈撳叆锛氶�夋墜 ID銆佽瘎鍒嗘潯鐩緱鍒嗭紙姣忎釜鏉$洰寰楀垎鈮ゆ潯鐩垎鍊硷級銆佽瘎璁猴紱
-鏍¢獙锛氭�诲垎鈮ゆā鏉挎�诲垎锛�
-缁撴灉锛氭瘮璧涚粨鏉熷悗鑷姩璁$畻骞冲潎鍒嗭紙鎴栧彇鍘绘帀鏈�楂� / 鏈�浣庡垎鐨勮鍒欙級锛屾洿鏂伴�夋墜鐨剆core鍜宺ank锛堝悕娆★級銆�
-2. 鐘舵�佹祦杞鍒�
-鏄庣‘鎵�鏈夌姸鎬佸瓧娈电殑鍙樻洿鏉′欢锛屼緥濡傦細
-ActivityPlayer.state娴佽浆锛�1-寰呭鏍糕啋2-杩涜涓紙瀹℃牳閫氳繃锛夆啋3-缁撴潫锛堟瘮璧涚粨鏉燂級锛�
-Activity.state娴佽浆锛�1-姝e父鈫�0-鍒犻櫎锛堜粎绠$悊鍛樺彲鎿嶄綔锛屼笖宸茬粨鏉熺殑姣旇禌鎵嶈兘鍒犻櫎锛夈��
-涓夈�佹帴鍙h鑼冿紙GraphQL 缁嗗寲锛�
-褰撳墠浠呮彁鍒� 鈥滄瘡涓ā鍧楀搴斾竴涓� graphqls 鏂囦欢鈥濓紝浣嗙己灏戞煡璇� / 鍙樻洿鎿嶄綔鐨勫弬鏁般�佽繑鍥炲�笺�佹潈闄愭帶鍒讹紝AI 鏃犳硶鐢熸垚鎺ュ彛銆傞渶琛ュ厖锛�
-1. 鏌ヨ鎿嶄綔锛圦uery锛�
-绀轰緥锛堟瘮璧涘垪琛ㄦ煡璇級锛�
-graphql
-type Query {
-  # 姣旇禌鍒楄〃鏌ヨ
-  activityList(
-    name: String  # 鍚嶇О妯$硦鏌ヨ
-    page: Int!    # 椤电爜锛岄粯璁�1
-    size: Int!    # 姣忛〉鏉℃暟锛岄粯璁�10
-  ): PageResult!  # 杩斿洖鍒嗛〉缁撴灉锛堝惈鎬绘潯鏁般�佸垪琛ㄦ暟鎹級
-}
-
-# 鍒嗛〉缁撴灉缁撴瀯
-type PageResult {
-  total: Int!       # 鎬绘潯鏁�
-  list: [Activity!]! # 鏁版嵁鍒楄〃
-}
-
-# 姣旇禌鏁版嵁缁撴瀯
-type Activity {
-  id: ID!
-  name: String!
-  enrollEndTime: String! # 鏍煎紡锛歽yyy-MM-dd HH:mm:ss
-  # 鍏朵粬瀛楁...
-}
-2. 鍙樻洿鎿嶄綔锛圡utation锛�
-绀轰緥锛堟柊澧炴瘮璧涳級锛�
-graphql
-type Mutation {
-  # 鏂板姣旇禌
-  createActivity(input: ActivityInput!): Activity!
-}
-
-# 杈撳叆鍙傛暟缁撴瀯
-input ActivityInput {
-  name: String!        # 鏍¢獙锛氶潪绌猴紝闀垮害鈮�30
-  enrollEndTime: String! # 鏍¢獙锛氭牸寮忔纭紝涓斺墺褰撳墠鏃堕棿
-  startTime: String!   # 鏍¢獙锛氭牸寮忔纭紝涓斺墺enrollEndTime
-  # 鍏朵粬瀛楁...
-}
-3. 鏉冮檺鎺у埗
-鏄庣‘姣忎釜鎺ュ彛鐨勮闂鑹诧紝渚嬪锛�
-createActivity锛氫粎绠$悊鍛� / 骞冲彴瑙掕壊鍙闂紱
-queryPlayerList锛氱鐞嗗憳鍙煡鐪嬫墍鏈夛紝璇勫浠呰兘鏌ョ湅鑷繁璐熻矗鐨勬瘮璧涢�夋墜銆�
-鍥涖�佹妧鏈粏鑺備笌绾︽潫锛堝紑鍙戝繀澶囷級
-褰撳墠鏂囨。鎻愬埌鎶�鏈鑼冧絾涓嶅叿浣擄紝闇�琛ュ厖锛�
-1. 鏁版嵁鏍¢獙瑙勫垯
-鎵嬫満鍙凤細鏍煎紡蹇呴』涓�1[3-9]\d{9}锛�
-鏃ユ湡鏃堕棿锛氱粺涓�鏍煎紡yyyy-MM-dd HH:mm:ss锛屽悗绔渶鏍¢獙鍚堟硶鎬э紙濡傛湀浠解墹12锛夛紱
-璇勫垎鏉$洰锛氬崟涓潯鐩緱鍒嗏墺0 涓斺墹鏉$洰鍒嗗�硷紝鎵�鏈夋潯鐩�诲垎鈮ゆā鏉挎�诲垎銆�
-2. 濯掍綋璧勬簮澶勭悊
-涓婁紶闄愬埗锛氬浘鐗団墹5MB锛堟牸寮忥細jpg/png锛夛紝瑙嗛鈮�100MB锛堟牸寮忥細mp4锛夛紱
-鑵捐浜戝瓨鍌ㄨ矾寰勶細{targetType}/{yyyyMMdd}/{uuid}.ext锛堝activity_video/20250921/123e456-e89b-12d3-a456-426614174000.mp4锛夛紱
-璁块棶 URL锛歨ttps://{Bucket}.cos.{Region}.myqcloud.com/{璺緞}銆�
-3. 寮傚父澶勭悊
-鏄庣‘甯歌寮傚父鍦烘櫙鍙婂鐞嗘柟寮忥紝渚嬪锛�
-鎶ュ悕浜烘暟瓒呴檺锛氭姏鍑築usinessException("鎶ュ悕浜烘暟宸茶揪涓婇檺")锛孒TTP 鐘舵�佺爜 400锛�
-璇勫垎妯℃澘宸插叧鑱旀瘮璧涳細鍒犻櫎鏃舵姏鍑築usinessException("妯℃澘宸插叧鑱旀瘮璧涳紝涓嶅彲鍒犻櫎")銆�
-4. 娑堟伅闃熷垪锛圧abbitMQ锛�
-浜ゆ崲鏈� / 闃熷垪瀹氫箟锛氫緥濡傚鏍搁�氱煡闃熷垪ryc.exchange.notice锛岃矾鐢遍敭notice.audit锛�
-娑堟伅鏍煎紡锛�
-json
-{
-  "type": "AUDIT_PASS", // 娑堟伅绫诲瀷
-  "userId": 123,        // 鎺ユ敹鐢ㄦ埛ID
-  "content": "鎮ㄦ姤鍚嶇殑XX姣旇禌宸查�氳繃瀹℃牳",
-  "createTime": "2025-09-21 10:00:00"
-}
-浜斻�佸墠绔氦浜掑叧鑱旓紙褰卞搷鍚庣璁捐锛�
-鍗充娇閲嶇偣鏄悗绔紑鍙戯紝涔熼渶鏄庣‘鍓嶇浜や簰瀵瑰悗绔殑瑕佹眰锛�
-鍒嗛〉鍙傛暟锛氶粯璁ら〉鐮� 1锛屾瘡椤� 10 鏉★紝鏀寔鍓嶇鑷畾涔夛紱
-鍒楄〃鎺掑簭锛氶粯璁ゆ寜鍒涘缓鏃堕棿闄嶅簭锛屾敮鎸佹寜鎸囧畾瀛楁鎺掑簭锛堝姣旇禌鏃堕棿锛夛紱
-鎵归噺鎿嶄綔锛氭槸鍚︽敮鎸佹壒閲忓垹闄ゆ瘮璧涖�佹壒閲忓鏍搁�夋墜锛堥渶鍚庣鎻愪緵鎵归噺鎺ュ彛锛夈��
-鍏�佽竟鐣屽満鏅笌鐗规畩瑙勫垯
-琛ュ厖 鈥滀緥澶栨儏鍐碘�� 澶勭悊锛屼緥濡傦細
-姣旇禌鏃堕棿淇敼锛氳嫢宸叉湁浜烘姤鍚嶏紝闇�鍒ゆ柇鏂版椂闂存槸鍚︹墺褰撳墠鏃堕棿锛屼笖閫氱煡宸叉姤鍚嶅鍛橈紱
-璇勫鏇存崲锛氬凡鎵撳垎鐨勮瘎濮旇绉婚櫎鍚庯紝鍏惰瘎鍒嗘槸鍚︿繚鐣欙紙瑙勫垯锛氫繚鐣欎絾涓嶈鍏ユ渶缁堝緱鍒嗭級锛�
-鏁版嵁瀵煎嚭锛氭槸鍚﹂渶瑕佹敮鎸佸鍑烘瘮璧涙姤鍚嶅悕鍗曘�佽瘎鍒嗙粨鏋滐紙闇�鏄庣‘瀵煎嚭瀛楁鍜屾牸寮忥級銆�
-閫氳繃浠ヤ笂缁嗗寲锛孉I 鍙幏寰楄冻澶熺殑銆岀‘瀹氭�т俊鎭�嶏紝鐢熸垚鐨勪唬鐮佸皢鏇磋创鍚堥渶姹傦紝鍑忓皯鍚庢湡淇敼鎴愭湰銆傛牳蹇冨師鍒欐槸锛氳姣忎釜鍔熻兘鐐归兘鏈夋槑纭殑 鈥滆緭鍏� - 澶勭悊 - 杈撳嚭鈥� 瑙勫垯锛屾瘡涓瓧娈甸兘鏈夋竻鏅扮殑绾︽潫銆�
-钃夋槗鍒涢」鐩�
-涓�銆侀噸瑕佹彁绀猴紙涓嶅彲杩濆弽锛�
-鍏ㄧ▼浣跨敤涓枃锛�
-涓嶈淇敼鍜屼綘鏃犲叧鐨勪唬鐮侊紒
-鏁版嵁搴撹〃缁撴瀯宸茬粡璁捐濂戒簡锛屼綘鍙互閫氳繃jdbc 鍒涘缓瑕佺粰test 鍑芥暟鏉ヨ闂暟鎹簱鑾峰彇琛ㄧ粨鏋�!, 涓嶈鍦ㄥ懡浠よ閲岄潰鎵цmysql 锛屾垜娌℃湁瀹夎锛侊紒锛�
-淇敼浠讳綍鍔熻兘閮藉厛瀵瑰簲鏌ヨ瀵瑰簲妯″潡鐨勫姛鑳借姹傦紒
-鎵�鏈夋柊澧炪�佺紪杈戦〉闈㈤兘鏄叕鐢ㄤ竴涓〉闈€��
-姣忎釜瀹炰綋绫婚兘鏈夊搴旂殑 Input 绫诲瀷锛歩d=null 琛ㄧず鏂板锛宨d鈮爊ull 琛ㄧず淇敼銆�
-鍏宠仈濯掍綋璧勬簮瑙勫垯锛�
-user 鍏宠仈鐨勫ご鍍忋�乤ctivity 鍏宠仈鐨勮棰� / 鍥剧墖銆乧arousel 鍏宠仈鐨勮棰� / 鍥惧儚锛屽潎瀵瑰簲 t_media 鏁版嵁琛紙瀹炰綋绫伙細Media锛夛紱
-閫氳繃 Target 瀛楁鍏宠仈鍖哄垎銆�
-浜屻�侀渶姹傛弿杩�
-瀛﹀憳鐧诲綍灏忕▼搴� 鈫� 鐐瑰嚮鎶ュ悕骞跺綍鍏ユ姤鍚嶄俊鎭� 鈫� 宸ヤ綔浜哄憳瀹℃牳鎶ュ悕璧勬枡 鈫� 璇勫璇勫璧勬枡锛屾寜姣旇禌瀵瑰簲璇勫垎琛ㄧ粰瀛﹀憳鎵撳垎骞跺~鍐欒瘎璁恒��
-涓夈�乄eb 绔渶姹�
-3.1 鐧诲綍椤甸潰
-鏍稿績鍔熻兘锛氭敮鎸併�屾墜鏈哄彿 + 瀵嗙爜銆嶇櫥褰曪紱
-棰勭暀鍔熻兘锛氬井淇℃壂鐮佺櫥褰曞叆鍙c��
-3.2 鐧诲綍鍚庨椤碉紙鏍囧噯宸ヤ綔鍙版ā鏉匡級
-鍖哄煙	鍐呭鎻忚堪
-宸︿晶鑿滃崟	鍖呭惈銆屾瘮璧涚鐞嗐�嶃�岃瘎濮旂鐞嗐�嶃�岃瘎鍒嗘ā鏉裤�嶃�屽弬璧涗汉鍛樸�嶃�屾柊闂讳笌鎺ㄥ箍銆嶃�屽憳宸ョ鐞嗐��
-鍙充晶宸ヤ綔鍖�	灞曠ず鏍稿績鏁版嵁锛氬綋鍓嶈繘琛屾瘮璧涖�佸弬璧涙�讳汉鏁般�佹姤鍚嶅緟瀹℃牳浜烘暟
-3.3 姣旇禌绠$悊锛堝疄浣撶被锛欰ctivity锛�
-3.3.1 姣旇禌鍒楄〃
-椤堕儴鍔熻兘锛氭煡璇㈡锛堟敮鎸佹寜銆屾瘮璧涘悕绉般�嶆ā绯婃煡璇級銆併�愭柊澧炴瘮璧涖�戞寜閽紱
-鍒楄〃瀛楁锛氭瘮璧涘悕绉般�佹姤鍚嶄汉鏁般�佹瘮璧涙椂闂淬�佹姤鍚嶆埅鑷虫椂闂达紱
-鎿嶄綔鍒楋細鏌ョ湅閫夋墜銆佺紪杈戙�佸垹闄ゃ��
-3.3.2 姣旇禌璇︽儏
-鍩虹淇℃伅閰嶇疆锛�
-姣旇禌鍚嶇О锛堟渶澶ч暱搴︹墹30 瀛楃锛夛紱
-鎶ュ悕鎴嚦鏃ユ湡锛堟棩鍘嗙粍浠堕�夋嫨锛夛紱
-姣旇禌寮�濮嬫椂闂达紙绮剧‘鍒般�屽勾鏈堟棩 鏃跺垎銆嶏級锛�
-姣旇禌鍦板潃銆佹渶澶у弬璧涗汉鏁般�佹瘮璧涙弿杩帮紱
-鍏宠仈閰嶇疆锛氶�夋嫨璇勫垎妯℃澘銆佷笂浼犺棰� / 鍥剧墖锛堝弬鑰冦�屼簲銆佽璁¤鐐广�嶏級銆�
-姣旇禌闃舵瀹氫箟锛堥噸瑕侊級锛�
-鏁版嵁鍏宠仈锛氭瘮璧涢樁娈典笌姣旇禌鍏辩敤 t_activity 琛紝閫氳繃 pid 鍏宠仈锛堢埗 ID 涓烘瘮璧� ID锛夛紱
-闃舵閰嶇疆椤癸細
-闃舵鍚嶇О锛堟渶澶ч暱搴︹墹30 瀛楃锛夛紱
-闃舵寮�濮嬫椂闂达紙绮剧‘鍒般�屽勾鏈堟棩 鏃跺垎銆嶏級锛�
-闃舵鍦板潃銆侀樁娈垫渶澶у弬璧涗汉鏁般�侀樁娈垫弿杩帮紱
-璇勫垎妯℃澘锛堥粯璁ょ户鎵挎瘮璧涙ā鏉匡紝鏀寔鍗曠嫭閫夋嫨锛夛紱
-闃舵鍒楄〃鎿嶄綔锛氭煡鐪嬮�夋墜銆佺紪杈戙�佸垹闄ゃ��
-3.4 璇勫绠$悊锛堝疄浣撶被锛欽udge锛�
-3.4.1 璇勫鍒楄〃
-灞曠ず瀛楁锛氬ご鍍忋�佽瘎濮斿悕绉般�佷笓涓氭爣绛俱�佽仈绯荤數璇濄�佹�у埆銆�
-3.4.2 璇勫璇︽儏
-鍩虹淇℃伅锛氳瘎濮斿悕绉般�佸ご鍍忋�佹�у埆銆佽仈绯荤數璇濓紱
-鏍囩閰嶇疆锛氫笓涓氭爣绛撅紙鏀寔澶氶�夛紝鍙傝�冦�屼簲銆佽璁¤鐐� 3銆嶏級锛�
-琛ュ厖淇℃伅锛氳瘎濮旇鎯呮弿杩般��
-3.5 璇勫垎妯℃澘绠$悊锛堝疄浣撶被锛歊atingScheme锛�
-3.5.1 璇勫垎妯℃澘鍒楄〃
-灞曠ず瀛楁锛氭ā鏉垮悕绉般�佹ā鏉挎�诲垎銆�
-3.5.2 璇勫垎妯℃澘璇︽儏
-鍩虹閰嶇疆锛氭ā鏉垮悕绉帮紱
-鏉$洰绠$悊锛�
-鏀寔銆屾柊澧� / 鍒犻櫎銆嶈瘎鍒嗘潯鐩紙宸茬粦瀹氭瘮璧涚殑妯℃澘涓嶅彲鍒犻櫎锛夛紱
-鏉$洰瀛楁锛氭潯鐩悕绉般�佹潯鐩垎鍊笺��
-3.6 瀛﹀憳绠$悊锛堝疄浣撶被锛歅layer锛�
-3.6.1 瀛﹀憳鍒楄〃
-灞曠ず瀛楁锛氬鍛樺ご鍍忋�佸鍛樺悕绉般�佹姤鍚嶇殑姣旇禌鍚嶇О銆佺敵璇锋椂闂淬��
-3.6.2 瀛﹀憳璇︽儏
-鍩虹淇℃伅锛氬鍛樺悕绉般�佸ご鍍忋�佽仈绯荤數璇濓紱
-鍙傝禌璁板綍鍒楄〃锛�1 涓鍛樺彲鎶ュ涓瘮璧涳級锛�
-灞曠ず瀛楁锛氭瘮璧涘悕绉般�佸綋鍓嶇姸鎬侊紱
-鐘舵�佽鏄庯細0-鍒犻櫎銆�1-寰呭鏍搞��2-杩涜涓紙瀹℃牳閫氳繃锛夈��3-缁撴潫锛�
-鐗规畩瑙勫垯锛氭瘮璧涚粨鏉熷悗锛屻�屽緱鍒嗐�嶃�屽悕娆°�嶄笉鍙慨鏀广��
-3.7 鏂伴椈绠$悊锛堝疄浣撶被锛欳arousel锛�
-3.7.1 鏂伴椈鍒楄〃
-灞曠ず瀛楁锛氭柊闂诲悕绉般�佸垱寤烘椂闂淬��
-3.7.2 鏂伴椈鎾斁璁剧疆锛堝叧鑱旇〃锛歵_carousel_sort锛�
-鎿嶄綔鍏ュ彛锛氬垪琛ㄦ煡璇㈡寜閽彸渚� 鈫� 銆愯缃挱鏀俱�戞寜閽紱
-璁剧疆椤甸潰锛�
-椤堕儴锛氭柊闂绘煡璇㈡锛�
-涓嬫柟锛氭柊闂诲垪琛紝鎿嶄綔鍒楁敮鎸併�岃缃挱鏀鹃『搴忋�嶃��
-3.7.3 鏂伴椈璇︽儏
-鍐呭閰嶇疆锛氭柊闂诲悕绉般�佹柊闂绘鏂囷紱
-濯掍綋閰嶇疆锛氫笂浼犳柊闂诲叧鑱旂殑瑙嗛 / 鍥剧墖锛�
-鎾斁閰嶇疆锛氳缃棰� / 鍥剧墖鐨勬挱鏀鹃『搴忋��
-3.8 鏉冮檺瑙掕壊
-妯″瀷锛氶噰鐢ㄦ爣鍑� RBC锛堝熀浜庤鑹茬殑璁块棶鎺у埗锛夋ā鍨嬶紱
-瑙掕壊瑙勫垯锛氬唴缃� 4 绫昏鑹诧紙涓嶅彲淇敼 / 缂栬緫锛夛細瀛﹀憳銆佸钩鍙颁汉鍛樸�佽瘎濮斻�佺鐞嗗憳銆�
-3.9 涓撲笟鏍囩绠$悊锛堝叧鑱旇〃锛歵_tag锛�
-3.9.1 涓撲笟鏍囩鍒楄〃
-鏀寔鍔熻兘锛氭柊澧炰笓涓氭爣绛撅紱
-榛樿瑙勫垯锛氭柊澧炴爣绛剧殑 code 涓庢爣绛惧悕绉颁竴鑷淬��
-3.10 娑堟伅绠$悊
-鍔熻兘锛氬睍绀烘墍鏈夊凡鍙戦�佺殑绯荤粺娑堟伅锛堝惈娑堟伅鍐呭銆佸彂閫佹椂闂淬�佹帴鏀跺璞$瓑锛夈��
-鍥涖�佸井淇$闇�姹�
-鏆傛椂鐣欑┖
-浜斻�佽璁¤鐐�
-鍒楄〃閫氱敤瑙勫垯锛氭墍鏈夋ā鍧楃殑鍒楄〃椤甸潰锛屽潎闇�鍖呭惈銆屾煡璇€�嶃�屾柊澧炪�嶅姛鑳斤紱
-濯掍綋璧勬簮瀛樺偍锛堝叧鑱旇〃锛歵_media锛夛細
-瑕嗙洊鍦烘櫙锛歶ser 澶村儚銆乸layer 澶村儚銆乤ctivity 瑙嗛 / 鍥剧墖銆乤ctivityPlayer 鎶ュ悕璧勬枡銆乯udge 澶村儚銆乪mployee 澶村儚銆乧arousel 瑙嗛 / 鍥剧墖锛�
-鍖哄垎瑙勫垯锛氶�氳繃 TargetType 鏋氫妇鍖哄垎璧勬簮绫诲瀷锛堥渶瀹氫箟鍖呭惈涓婅堪鎵�鏈夊満鏅殑鏋氫妇锛夛紱
-鏍囩绠$悊锛堣〃锛歵_tag锛夛細
-鍖哄垎瑙勫垯锛氶�氳繃 category 瀛楁鍖哄垎鏍囩绫诲瀷锛堜緥锛氫笓涓氭爣绛剧殑 category = 'major'锛夈��
-鍏�佺幆澧冮厤缃�
-寮�鍙戠幆澧冿細Windows 绯荤粺锛屽懡浠よ宸ュ叿锛歅owerShell锛�
-JDK 閰嶇疆锛欽ava 25锛屽畨瑁呰矾寰勶細C:\Program Files\Java\jdk-25銆�
-浣跨敤鐜鍙橀噺閲岄潰鐨刴ave銆�
-涓冦�佹妧鏈鑼�
-7.1 鐩綍瑙勮寖
-鏍圭洰褰曟寜銆岀 / 妯″潡銆嶆媶鍒嗭紝backend 鍐呴儴鎸夈�屼笟鍔℃ā鍧椼�嶅垝鍒嗭紝缁撴瀯濡備笅锛�
-plaintext
-backend/src/main/java/com/rongyichuang/
-鈹溾攢鈹� config/          # 鍏ㄥ眬閰嶇疆锛圧abbitMQ銆丟raphQL銆佽法鍩熺瓑锛�
-鈹溾攢鈹� judge/           # 璇勫妯″潡
-鈹�   鈹溾攢鈹� api/         # GraphQL鎺ュ彛锛堝JudgeGraphqlApi.java锛�
-鈹�   鈹溾攢鈹� entity/      # 瀹炰綋绫伙紙濡侸udge.java锛�
-鈹�   鈹溾攢鈹� dto/         # 鏁版嵁浼犺緭瀵硅薄锛堝垎request/response瀛愮洰褰曪級
-鈹�   鈹溾攢鈹� service/     # 涓氬姟閫昏緫锛堝JudgeService.java锛屾棤鍗曠嫭鎺ュ彛灞傦級
-鈹�   鈹斺攢鈹� util/        # 妯″潡鍐呭伐鍏风被
-鈹溾攢鈹� rating/          # 璇勫垎妯℃澘妯″潡锛堢粨鏋勫悓judge锛�
-鈹溾攢鈹� activity/        # 姣旇禌妯″潡锛堢粨鏋勫悓judge锛�
-鈹溾攢鈹� player/          # 鍙傝禌浜哄憳妯″潡锛堢粨鏋勫悓judge锛�
-鈹溾攢鈹� carousel/        # 杞挱鍥撅紙鏂伴椈锛夋ā鍧楋紙缁撴瀯鍚宩udge锛�
-鈹溾攢鈹� employee/        # 骞冲彴鍛樺伐妯″潡锛堢粨鏋勫悓judge锛�
-鈹斺攢鈹� common/          # 鍏叡妯″潡
-    鈹溾攢鈹� entity/      # 鍩虹瀹炰綋锛堝BaseEntity.java锛屽惈id/state锛�
-    鈹溾攢鈹� dto/         # 鍏叡DTO锛堝PageRequest.java锛�
-    鈹溾攢鈹� exception/   # 鍏ㄥ眬寮傚父澶勭悊锛堝GlobalExceptionHandler.java锛�
-    鈹斺攢鈹� util/        # 鍏ㄥ眬宸ュ叿绫伙紙濡侱ateUtil.java锛�
-7.2 GraphQL 閰嶇疆
-鏂囦欢璺緞锛歳esources/graphqls/锛�
-鍛藉悕瑙勫垯锛氭瘡涓笟鍔℃ā鍧楀搴� 1 涓� graphqls 鏂囦欢锛堜緥锛氳瘎濮旀ā鍧� 鈫� judge.graphqls锛夈�� 涓氬姟妯″潡鐨刧raphql閲岄潰query锛孧utation閮借extend scheme.graphqls 锛屼笉鐒朵細鍖呴噸澶嶇殑閿欒锛�
-7.3 瀹夊叏妗嗘灦
-閲囩敤 Spring Security + Token 瀹炵幇韬唤璁よ瘉涓庢帴鍙f巿鏉冦��
-7.4 浜嬪姟鎺у埗
-浣滅敤鑼冨洿锛歴ervice 灞傛牳蹇冧笟鍔℃柟娉曪紙濡傝瘎濮旇瘎鍒嗐�佸鍛樺鏍革級锛�
-瀹炵幇鏂瑰紡锛氭坊鍔� @Transactional 娉ㄨВ锛岀‘淇濇暟鎹竴鑷存�с��
-7.5 閫昏緫鍒犻櫎
-閫氱敤瑙勫垯锛氭墍鏈夊疄浣撶被鍚� state 瀛楁锛堢被鍨嬶細int (1)锛岄粯璁ゅ�硷細1锛夛紱
-鏌ヨ杩囨护锛欽PA 鐖舵帴鍙f坊鍔� @Where(clause = "state = 1")锛岃嚜鍔ㄨ繃婊ゅ凡鍒犻櫎鏁版嵁锛�
-鐗规畩璇存槑锛歛ctivityPlayer 鐨� state 鍚箟涓庡叾浠栧疄浣撲笉鍚岋紙鍙傝�� 3.6.2 鐘舵�佽鏄庯級銆�
-7.6 浜戝瓨鍌ㄨ鑼�
-瀛樺偍鐩爣锛氳棰戙�佸浘鐗囦笂浼犺嚦銆岃吘璁簯瀛樺偍妗躲�嶏紱
-鐩綍瑙勫垯锛氭寜銆寉yyyMMdd銆嶏紙骞存湀鏃ワ級鍒涘缓鐩綍锛屽綋鏃ヨ祫婧愬瓨鍏ュ搴旂洰褰曘��
-鍏�佽祫婧愪俊鎭�
-8.1 鏁版嵁搴撹繛鎺ワ紙MySQL锛�
-鍙傛暟	閰嶇疆鍊�
-绫诲瀷	MySQL
-IP	140.143.152.226
-绔彛	3308
-鏁版嵁搴撳悕	ryc
-鐢ㄦ埛鍚�	openai
-瀵嗙爜	Xml@uk2025
-8.2 RabbitMQ 杩炴帴
-yaml
-rabbitmq:
-  host: 140.143.152.226
-  port: 5672
-  virtual-host: ryc_prod
-  username: admin
-  password: ycl@2020
-  connection-timeout: 15000
-8.3 鑵捐浜戝瓨鍌紙COS锛�
-SecretId锛欰KIDZ4Pcj9zbgaZgl6yJf0HSXXHzfGl9PcdT
-SecretKey锛歲jYFnCas0xseEdsxrsobKuJiwzrZ7dzC
-Bucket锛歳yc-1256886520
-Region锛歛p-chengdu  浣犲熀浜庤繖涓啀瀹屽杽涓�涓嬶紝缁嗚妭
-钃夋槗鍒涢」鐩紙瀹屽杽鐗堬級
-涓�銆侀噸瑕佹彁绀猴紙涓嶅彲杩濆弽锛�
-鍏ㄧ▼浣跨敤涓枃锛�
-涓嶈淇敼鍜屼綘鏃犲叧鐨勪唬鐮侊紒
-鏁版嵁搴撹〃缁撴瀯鑾峰彇鏂瑰紡锛�
-鍥犳湭瀹夎 MySQL 鍛戒护琛岋紝闇�閫氳繃 JDBC 缂栧啓TestTableStructure鍑芥暟鏌ヨ琛ㄧ粨鏋勶紝鍏蜂綋瀹炵幇濡備笅锛堝惈渚濊禆銆佷唬鐮佺ず渚嬨�佹墽琛屾楠わ級锛�
-渚濊禆锛氬湪pom.xml涓坊鍔� MySQL JDBC 渚濊禆锛堥�傞厤 MySQL 8.0+锛夛細
-xml
-<dependency>
-    <groupId>mysql</groupId>
-    <artifactId>mysql-connector-java</artifactId>
-    <version>8.0.33</version>
-    <scope>test</scope>
-</dependency>
-浠g爜绀轰緥锛堜綅浜巘est/java/com/rongyichuang/common/util/TestTableStructure.java锛夛細
-java
-import java.sql.*;
-import java.util.ArrayList;
-import java.util.List;
-
-public class TestTableStructure {
-    // 鏁版嵁搴撹繛鎺ヤ俊鎭紙涓庢枃妗�8.1涓�鑷达級
-    private static final String URL = "jdbc:mysql://140.143.152.226:3308/ryc?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true";
-    private static final String USER = "openai";
-    private static final String PASSWORD = "Xml@uk2025";
-
-    // 闇�鏌ヨ鐨勮〃鍚嶏紙鏍规嵁妯″潡琛ュ厖锛屽t_activity銆乼_judge锛�
-    private static final List<String> TABLE_NAMES = List.of("t_activity", "t_judge", "t_rating_scheme", "t_media");
-
-    public static void main(String[] args) {
-        try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD)) {
-            for (String tableName : TABLE_NAMES) {
-                System.out.println("=== 琛ㄥ悕锛�" + tableName + " ===");
-                // 鏌ヨ琛ㄥ瓧娈电粨鏋�
-                String sql = "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE, COLUMN_DEFAULT " +
-                            "FROM INFORMATION_SCHEMA.COLUMNS " +
-                            "WHERE TABLE_SCHEMA = 'ryc' AND TABLE_NAME = ?";
-                try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
-                    pstmt.setString(1, tableName);
-                    ResultSet rs = pstmt.executeQuery();
-                    while (rs.next()) {
-                        System.out.printf("瀛楁鍚嶏細%s | 鏁版嵁绫诲瀷锛�%s | 娉ㄩ噴锛�%s | 鏄惁鍏佽绌猴細%s | 榛樿鍊硷細%s%n",
-                                rs.getString("COLUMN_NAME"),
-                                rs.getString("DATA_TYPE"),
-                                rs.getString("COLUMN_COMMENT"),
-                                rs.getString("IS_NULLABLE"),
-                                rs.getString("COLUMN_DEFAULT"));
-                    }
-                }
-                System.out.println(); // 鎹㈣鍒嗛殧
-            }
-        } catch (SQLException e) {
-            e.printStackTrace();
-            System.err.println("鏌ヨ琛ㄧ粨鏋勫け璐ワ細" + e.getMessage());
-        }
-    }
-}
-鎵ц姝ラ锛氬湪 PowerShell 涓繘鍏ラ」鐩牴鐩綍锛屾墽琛宮vn test -Dtest=TestTableStructure锛屾帶鍒跺彴杈撳嚭琛ㄧ粨鏋勮鎯呫��
-淇敼浠讳綍鍔熻兘閮藉厛瀵瑰簲鏌ヨ瀵瑰簲妯″潡鐨勫姛鑳借姹傦紒
-鎵�鏈夋柊澧炪�佺紪杈戦〉闈㈤兘鏄叕鐢ㄤ竴涓〉闈€��
-姣忎釜瀹炰綋绫婚兘鏈夊搴旂殑Input绫诲瀷锛歩d=null琛ㄧず鏂板锛宨d鈮爊ull琛ㄧず淇敼銆�
-鍏宠仈濯掍綋璧勬簮瑙勫垯锛�
-user 鍏宠仈鐨勫ご鍍忋�乤ctivity 鍏宠仈鐨勮棰� / 鍥剧墖銆乧arousel 鍏宠仈鐨勮棰� / 鍥惧儚锛屽潎瀵瑰簲t_media鏁版嵁琛紙瀹炰綋绫伙細Media锛夛紱
-閫氳繃target_type锛堣祫婧愮被鍨嬶級鍜宼arget_id锛堝叧鑱斿疄浣� ID锛夊弻瀛楁鍖哄垎锛宼arget_type鏋氫妇鍊艰銆屼簲銆佽璁¤鐐� 2銆嶃��
-浜屻�侀渶姹傛弿杩�
-瀛﹀憳鐧诲綍灏忕▼搴� 鈫� 閫夋嫨鐩爣姣旇禌 鈫� 濉啓鎶ュ悕淇℃伅 + 涓婁紶鎶ュ悕璧勬枡锛堝叧鑱攖_media锛� 鈫� 宸ヤ綔浜哄憳锛堝钩鍙� / 绠$悊鍛樿鑹诧級瀹℃牳鎶ュ悕璧勬枡 鈫� 瀹℃牳閫氳繃鍚庯紝璇勫锛堜粎鎸囧畾姣旇禌鐨勮瘎濮旓級鎸夊叧鑱旇瘎鍒嗘ā鏉挎墦鍒� + 濉啓璇勮 鈫� 姣旇禌缁撴潫鍚庯紝绯荤粺鑷姩璁$畻閫夋墜鏈�缁堝緱鍒嗗拰鍚嶆锛堜笉鍙慨鏀癸級銆�
-涓夈�乄eb 绔渶姹傦紙缁嗗寲鐗堬級
-3.1 鐧诲綍椤甸潰
-鏍稿績鍔熻兘锛�
-杈撳叆椤癸細鎵嬫満鍙凤紙鏍¢獙瑙勫垯锛�11 浣嶆暟瀛楋紝鍖归厤姝e垯^1[3-9]\\d{9}$锛夈�佸瘑鐮侊紙鏍¢獙瑙勫垯锛�6-20 浣嶏紝鍚暟瀛� + 瀛楁瘝锛夛紱
-鎸夐挳锛氥�愮櫥褰曘�戯紙鐐瑰嚮鍚庤皟鐢╨ogin鎺ュ彛锛岃繑鍥� Token 瀛樺叆 LocalStorage锛夈�併�愬繕璁板瘑鐮併�戯紙棰勭暀锛屾殏涓嶅疄鐜帮級锛�
-棰勭暀鍔熻兘锛�
-寰俊鎵爜鐧诲綍鍏ュ彛锛氭樉绀哄井淇℃壂鐮佷簩缁寸爜锛堝悗绔�氳繃寰俊寮�鏀惧钩鍙版帴鍙g敓鎴愶級锛屾壂鐮佸悗鍥炶皟wechatLoginCallback鎺ュ彛瀹屾垚璁よ瘉銆�
-3.2 鐧诲綍鍚庨椤碉紙鏍囧噯宸ヤ綔鍙版ā鏉匡級
-鍖哄煙	鍐呭鎻忚堪	琛ュ厖缁嗚妭
-宸︿晶鑿滃崟	鍖呭惈銆屾瘮璧涚鐞嗐�嶃�岃瘎濮旂鐞嗐�嶃�岃瘎鍒嗘ā鏉裤�嶃�屽弬璧涗汉鍛樸�嶃�屾柊闂讳笌鎺ㄥ箍銆嶃�屽憳宸ョ鐞嗐��	鑿滃崟鏉冮檺鎺у埗锛氭牴鎹鑹查殣钘忔棤鏉冮檺椤癸紙濡傚鍛樿鑹蹭粎鏄剧ず銆屾柊闂讳笌鎺ㄥ箍銆嶏級
-鍙充晶宸ヤ綔鍖�	灞曠ず鏍稿績鏁版嵁锛氬綋鍓嶈繘琛屾瘮璧涖�佸弬璧涙�讳汉鏁般�佹姤鍚嶅緟瀹℃牳浜烘暟	鏁版嵁瀹炴椂鎬э細姣� 5 鍒嗛挓鍒锋柊涓�娆★紱鐐瑰嚮鏁版嵁鍗$墖璺宠浆鑷冲搴斿垪琛ㄩ〉锛堝鐐瑰嚮銆屽緟瀹℃牳浜烘暟銆嶈烦杞嚦鍙傝禌浜哄憳鍒楄〃鐨勩�屽緟瀹℃牳銆嶆爣绛鹃〉锛�
-3.3 姣旇禌绠$悊锛堝疄浣撶被锛欰ctivity锛屽搴旇〃锛歵_activity锛�
-3.3.1 姣旇禌鍒楄〃
-椤堕儴鍔熻兘锛�
-鏌ヨ妗嗭細鏀寔鎸夈�屾瘮璧涘悕绉般�嶆ā绯婃煡璇紙杈撳叆鍚庡疄鏃惰繃婊わ紝鎴栫偣鍑汇�愭煡璇€�戞寜閽Е鍙戯級锛�
-銆愭柊澧炴瘮璧涖�戞寜閽細鐐瑰嚮璺宠浆鑷炽�屾瘮璧涚紪杈戦〉銆嶏紙id=null锛夛紱
-鍒楄〃瀛楁锛�
-瀛楁鍚�	灞曠ず鏍煎紡 / 瑙勫垯
-姣旇禌鍚嶇О	瓒呭嚭 15 瀛楃鏃舵樉绀虹渷鐣ュ彿锛坔over 鏄剧ず瀹屾暣鍚嶇О锛�
-鎶ュ悕浜烘暟	鏍煎紡锛氬凡鎶ュ悕X浜�/涓婇檺Y浜猴紙濡� 鈥滃凡鎶ュ悕 23 浜� / 涓婇檺 50 浜衡�濓級
-姣旇禌鏃堕棿	鏍煎紡锛歽yyy-MM-dd HH:mm锛堝 鈥�2024-10-01 09:00鈥濓級
-鎶ュ悕鎴嚦鏃堕棿	杩囨湡鏃舵爣绾紙濡傚綋鍓嶆椂闂� > 鎶ュ悕鎴嚦鏃堕棿锛屾枃瀛楅鑹� #F53F3F锛�
-鎿嶄綔鍒楋細
-鏌ョ湅閫夋墜锛氳烦杞嚦銆屽弬璧涗汉鍛樺垪琛ㄣ�嶅苟绛涢�夊綋鍓嶆瘮璧涚殑閫夋墜锛�
-缂栬緫锛氳烦杞嚦銆屾瘮璧涚紪杈戦〉銆嶏紙id=褰撳墠姣旇禌ID锛夛紱
-鍒犻櫎锛氬脊鍑虹‘璁ゅ脊绐楋紙鎻愮ず 鈥滃垹闄ゅ悗涓嶅彲鎭㈠锛屾槸鍚︾‘璁わ紵鈥濓級锛岀偣鍑荤‘璁よ皟鐢╠eleteActivity鎺ュ彛锛堜粎绠$悊鍛樺彲鎿嶄綔锛夈��
-3.3.2 姣旇禌璇︽儏锛堢紪杈戦〉锛�
-鍩虹淇℃伅閰嶇疆锛堣〃鍗曢」锛夛細
-琛ㄥ崟椤�	鎺т欢绫诲瀷	鏍¢獙瑙勫垯	
-姣旇禌鍚嶇О	鍗曡杈撳叆妗�	闈炵┖锛岄暱搴︹墹30 瀛楃锛屼笉鍏佽鍖呭惈鐗规畩瀛楃锛坄~!@#$%^&*()_+{}	:"<>?`锛�
-鎶ュ悕鎴嚦鏃ユ湡	鏃ユ湡鏃堕棿閫夋嫨鍣�	闈炵┖锛屼笖鈮ュ綋鍓嶆椂闂达紝涓斺墹姣旇禌寮�濮嬫椂闂�	
-姣旇禌寮�濮嬫椂闂�	鏃ユ湡鏃堕棿閫夋嫨鍣�	闈炵┖锛岀簿纭埌銆屾椂鍒嗐�嶏紙濡� 鈥�2024-10-01 09:00鈥濓級	
-姣旇禌鍦板潃	鍗曡杈撳叆妗�	闈炵┖锛岄暱搴︹墹255 瀛楃	
-鏈�澶у弬璧涗汉鏁�	鏁板瓧杈撳叆妗�	闈炵┖锛屸墺1锛屸墹9999	
-姣旇禌鎻忚堪	瀵屾枃鏈紪杈戝櫒	鍙负绌猴紝鏀寔鍥剧墖鎻掑叆锛堟彃鍏ョ殑鍥剧墖鑷姩涓婁紶鑷� COS锛岃矾寰剅ich_text/yyyyMMdd/锛�	
-璇勫垎妯℃澘	涓嬫媺閫夋嫨妗�	闈炵┖锛岄�夐」涓烘墍鏈夌姸鎬佹甯哥殑璇勫垎妯℃澘锛坱_rating_scheme.state=1锛�	
-濯掍綋閰嶇疆锛堜笂浼犲尯鍩燂級锛�
-瑙嗛涓婁紶锛氭敮鎸佸崟涓� MP4 鏍煎紡瑙嗛锛堝ぇ灏忊墹100MB锛夛紝涓婁紶鍚庢樉绀洪瑙堝浘 + 鍒犻櫎鎸夐挳锛�
-鍥剧墖涓婁紶锛氭敮鎸佸鍥句笂浼狅紙鏈�澶� 5 寮狅級锛屾牸寮� JPG/PNG锛堝崟寮犫墹5MB锛夛紝鏀寔鎷栨嫿鎺掑簭锛�
-瀛樺偍瑙勫垯锛氫笂浼犲悗璋冪敤uploadMedia鎺ュ彛锛屽獟浣撹祫婧愬瓨鍏� COS 璺緞activity_media/yyyyMMdd/锛屽苟鍦╰_media鐢熸垚璁板綍锛坱arget_type=ACTIVITY_MEDIA锛宼arget_id=姣旇禌ID锛夈��
-姣旇禌闃舵瀹氫箟锛堝瓙琛ㄦ牸锛夛細
-鏄剧ず褰撳墠姣旇禌鐨勬墍鏈夐樁娈碉紙t_activity.pid=褰撳墠姣旇禌ID锛夛紱
-銆愭柊澧為樁娈点�戞寜閽細鐐瑰嚮寮瑰嚭闃舵缂栬緫寮圭獥锛堥粯璁ょ户鎵挎瘮璧涚殑璇勫垎妯℃澘銆佸湴鍧�锛屽彲淇敼锛夛紱
-闃舵琛ㄥ崟椤癸細涓庢瘮璧涘熀纭�淇℃伅涓�鑷达紙闄� 鈥滄姤鍚嶆埅鑷虫棩鏈熲�濓紝闃舵鏃犻渶鎶ュ悕锛夛紱
-闃舵鎿嶄綔鍒楋細鏌ョ湅閫夋墜锛堢瓫閫夊綋鍓嶉樁娈电殑閫夋墜锛夈�佺紪杈戯紙寮圭獥淇敼锛夈�佸垹闄わ紙浠呴樁娈垫湭寮�濮嬪彲鍒犻櫎锛夈��
-3.4 璇勫绠$悊锛堝疄浣撶被锛欽udge锛屽搴旇〃锛歵_judge锛�
-3.4.1 璇勫鍒楄〃
-鍒楄〃瀛楁锛�
-瀛楁鍚�	灞曠ず瑙勫垯
-澶村儚	鏄剧ず鍦嗗舰澶村儚锛堝昂瀵� 40脳40px锛夛紝鏃犲ご鍍忔椂鏄剧ず榛樿鍥炬爣锛堣矾寰�/static/default-avatar.png锛�
-璇勫鍚嶇О	闈炵┖锛岃秴鍑� 10 瀛楃鏄剧ず鐪佺暐鍙�
-涓撲笟鏍囩	澶氭爣绛惧睍绀猴紙濡� 鈥滈挗鐞淬兓澹颁箰銉讳綔鏇测�濓級锛屾爣绛捐儗鏅壊 #E8F3FF锛屾枃瀛楄壊 #1890FF
-鑱旂郴鐢佃瘽	鑴辨晱鏄剧ず锛堝 鈥�138****5678鈥濓級
-鎬у埆	鏄剧ず鍥炬爣锛堢敺锛氣檪锛屽コ锛氣檧锛�
-鎿嶄綔鍒楋細缂栬緫锛堣烦杞嚦璇勫缂栬緫椤碉級銆佸垹闄わ紙浠呯鐞嗗憳鍙搷浣滐紝闇�纭锛夈��
-3.4.2 璇勫璇︽儏锛堢紪杈戦〉锛�
-鍩虹淇℃伅琛ㄥ崟锛�
-琛ㄥ崟椤�	鎺т欢绫诲瀷	鏍¢獙瑙勫垯
-璇勫鍚嶇О	鍗曡杈撳叆妗�	闈炵┖锛岄暱搴︹墹20 瀛楃锛屼笉鍖呭惈鐗规畩瀛楃
-澶村儚	鍥剧墖涓婁紶鎺т欢	鏀寔 JPG/PNG锛屽ぇ灏忊墹2MB锛屼笂浼犲悗棰勮锛堣矾寰刯udge_avatar/yyyyMMdd/锛�
-鎬у埆	鍗曢�夋	闈炵┖锛岄�夐」 鈥滅敺鈥濃�滃コ鈥濃�滀繚瀵嗏��
-鑱旂郴鐢佃瘽	鍗曡杈撳叆妗�	闈炵┖锛屽尮閰嶆墜鏈哄彿姝e垯^1[3-9]\\d{9}$
-涓撲笟鏍囩	鏍囩閫夋嫨鍣�	闈炵┖锛屾敮鎸佸閫夛紙閫夐」鏉ヨ嚜t_tag琛╟ategory='major'鐨勬爣绛撅級
-璇勫璇︽儏	澶氳杈撳叆妗�	鍙负绌猴紝闀垮害鈮�500 瀛楃
-鍏宠仈姣旇禌閰嶇疆锛堝閫夋缁勶級锛�
-閫夐」锛氭墍鏈夌姸鎬佹甯哥殑姣旇禌锛坱_activity.state=1涓攑id=0锛夛紱
-浣滅敤锛氭寚瀹氳瘎濮斿彲璇勫鐨勬瘮璧涳紙浠呭叧鑱旂殑姣旇禌鍙樉绀鸿璇勫骞跺垎閰嶈瘎鍒嗕换鍔★級銆�
-3.5 璇勫垎妯℃澘绠$悊锛堝疄浣撶被锛歊atingScheme锛屽搴旇〃锛歵_rating_scheme锛涜瘎鍒嗘潯鐩細RatingSchemeItem锛屽搴旇〃锛歵_rating_scheme_item锛�
-3.5.1 璇勫垎妯℃澘鍒楄〃
-鍒楄〃瀛楁锛�
-妯℃澘鍚嶇О锛氶潪绌猴紝瓒呭嚭 15 瀛楃鏄剧ず鐪佺暐鍙凤紱
-妯℃澘鎬诲垎锛氭牸寮� 鈥淴X 鍒嗏�濓紙濡� 鈥�100 鍒嗏�濓級锛岀敱鎵�鏈夎瘎鍒嗘潯鐩垎鍊兼眰鍜岃嚜鍔ㄨ绠楋紱
-鏉$洰鏁伴噺锛氭牸寮� 鈥淴X 椤光�濓紙濡� 鈥�5 椤光�濓級锛�
-鍏宠仈姣旇禌鏁帮細鏄剧ず褰撳墠妯℃澘宸插叧鑱旂殑姣旇禌鏁伴噺锛堝 鈥滃凡鍏宠仈 3 涓瘮璧涒�濓紝hover 鏄剧ず姣旇禌鍚嶇О锛夈��
-鎿嶄綔鍒楋細
-缂栬緫锛氳烦杞嚦妯℃澘缂栬緫椤碉紙鑻ュ凡鍏宠仈姣旇禌锛屸�滄ā鏉垮悕绉扳�� 涓嶅彲淇敼锛夛紱
-鍒犻櫎锛氫粎鏈叧鑱旀瘮璧涚殑妯℃澘鍙垹闄わ紙寮圭獥鎻愮ず 鈥滃凡鍏宠仈姣旇禌锛屼笉鍙垹闄も�濓級銆�
-3.5.2 璇勫垎妯℃澘璇︽儏锛堢紪杈戦〉锛�
-鍩虹閰嶇疆锛�
-妯℃澘鍚嶇О锛氬崟琛岃緭鍏ユ锛堝凡鍏宠仈姣旇禌鏃剁鐢級锛屾牎楠岃鍒� 鈥滈潪绌猴紝闀垮害鈮�20 瀛楃鈥濓紱
-璇勫垎鏉$洰瀛愯〃鏍硷細
-鍒楀悕	鎺т欢 / 瑙勫垯
-鏉$洰鍚嶇О	鍗曡杈撳叆妗嗭紙鏂板 / 缂栬緫鏃讹級锛岄潪绌猴紝闀垮害鈮�30 瀛楃
-鏉$洰鍒嗗��	鏁板瓧杈撳叆妗嗭紙鏂板 / 缂栬緫鏃讹級锛岄潪绌猴紝鈮�1锛屸墹100锛屼笖鎵�鏈夋潯鐩�诲垎鈮�1000
-鎿嶄綔	鏂板锛堢偣鍑汇�愭柊澧炴潯鐩�戞坊鍔犺锛夈�佸垹闄わ紙浠呮湭鍏宠仈姣旇禌鐨勬ā鏉垮彲鍒犻櫎鏉$洰锛�
-鎬诲垎璁$畻锛氬疄鏃惰绠楁墍鏈夋潯鐩垎鍊间箣鍜岋紝鏄剧ず鍦ㄨ〃鏍间笅鏂癸紙濡� 鈥滃綋鍓嶆�诲垎锛�100 鍒嗏�濓級銆�
-3.6 瀛﹀憳绠$悊锛堝疄浣撶被锛歅layer锛屽搴旇〃锛歵_player锛涘弬璧涜褰曪細ActivityPlayer锛屽搴旇〃锛歵_activity_player锛�
-3.6.1 瀛﹀憳鍒楄〃
-绛涢�夋潯浠讹紙椤堕儴鏍囩椤� + 鏌ヨ妗嗭級锛�
-鏍囩椤碉細鍏ㄩ儴銆佸緟瀹℃牳锛坰tate=1锛夈�佽繘琛屼腑锛坰tate=2锛夈�佸凡缁撴潫锛坰tate=3锛夛紱
-鏌ヨ妗嗭細鏀寔鎸� 鈥滃鍛樺悕绉扳�濃�滄姤鍚嶆瘮璧涘悕绉扳�� 妯$硦鏌ヨ锛�
-鍒楄〃瀛楁锛�
-瀛楁鍚�	灞曠ず瑙勫垯
-瀛﹀憳澶村儚	鍦嗗舰鏄剧ず锛�40脳40px锛夛紝鏃犲ご鍍忔樉绀洪粯璁ゅ浘鏍�
-瀛﹀憳鍚嶇О	瓒呭嚭 10 瀛楃鏄剧ず鐪佺暐鍙�
-鎶ュ悕鐨勬瘮璧�	鏄剧ず姣旇禌鍚嶇О锛堣秴鍑� 15 瀛楃鐪佺暐锛夛紝鑻ヤ负闃舵姣旇禌锛屽悗缂� 鈥�-XX 闃舵鈥濓紙濡� 鈥滄牎鍥瓕鎵嬭禌 - 鍒濊禌鈥濓級
-鐢宠鏃堕棿	鏍煎紡锛歽yyy-MM-dd HH:mm锛堝 鈥�2024-09-15 14:30鈥濓級
-褰撳墠鐘舵��	鏍囩鏄剧ず锛堝緟瀹℃牳锛�#FF7D00锛涜繘琛屼腑锛�#00B42A锛涘凡缁撴潫锛�#86909C锛�
-鎿嶄綔鍒楋細
-鏌ョ湅璇︽儏锛氳烦杞嚦瀛﹀憳璇︽儏椤碉紱
-瀹℃牳锛氫粎 鈥滃緟瀹℃牳鈥� 鐘舵�佹樉绀猴紙鐐瑰嚮寮瑰嚭瀹℃牳寮圭獥锛岄�夋嫨 鈥滈�氳繃 / 鎷掔粷鈥� 骞跺~鍐欐嫆缁濆師鍥狅級銆�
-3.6.2 瀛﹀憳璇︽儏椤�
-鍩虹淇℃伅鍗$墖锛�
-鏄剧ず瀛﹀憳鍚嶇О銆佸ご鍍忋�佽仈绯荤數璇濓紙鑴辨晱锛夈�佹姤鍚嶆�绘鏁帮紱
-鍙傝禌璁板綍鍒楄〃锛堟寜鎶ュ悕鏃堕棿鍊掑簭锛夛細
-瀛楁鍚�	灞曠ず瑙勫垯
-姣旇禌鍚嶇О	鐐瑰嚮鍙烦杞嚦姣旇禌璇︽儏椤�
-鎶ュ悕鏃堕棿	鏍煎紡锛歽yyy-MM-dd HH:mm
-褰撳墠鐘舵��	鍚屽垪琛ㄩ〉鏍囩
-鎶ュ悕璧勬枡	鐐瑰嚮銆愭煡鐪嬨�戝脊鍑哄脊绐楋紝鏄剧ず涓婁紶鐨勮祫鏂欙紙鍥剧墖 / 鏂囨。锛屽叧鑱攖_media锛�
-寰楀垎 / 鍚嶆	宸茬粨鏉熺姸鎬佹樉绀猴紙寰楀垎锛歑X 鍒嗭紱鍚嶆锛氱 X 鍚嶏紝鏃犲悕娆℃樉绀� 鈥滄湭鎺掑悕鈥濓級
-璇勫璇勮	宸茬粨鏉熺姸鎬佹樉绀猴紙hover 鏄剧ず鎵�鏈夎瘎濮旂殑璇勮锛屾牸寮� 鈥滆瘎濮� A锛歑XX锛涜瘎濮� B锛歑XX鈥濓級
-3.7 鏂伴椈绠$悊锛堝疄浣撶被锛欳arousel锛屽搴旇〃锛歵_carousel锛涙挱鏀炬帓搴忥細CarouselSort锛屽搴旇〃锛歵_carousel_sort锛�
-3.7.1 鏂伴椈鍒楄〃
-鍒楄〃瀛楁锛�
-鏂伴椈鍚嶇О锛氳秴鍑� 20 瀛楃鏄剧ず鐪佺暐鍙凤紝鐐瑰嚮璺宠浆鑷虫柊闂昏鎯呴〉锛�
-灏侀潰鍥撅細鏄剧ず缂╃暐鍥撅紙灏哄 120脳80px锛夛紝鏃犲皝闈㈠浘鏄剧ず榛樿鍥撅紱
-鍒涘缓鏃堕棿锛氭牸寮弝yyy-MM-dd锛堝 鈥�2024-09-01鈥濓級锛�
-鎾斁鐘舵�侊細鏍囩鏄剧ず锛堝凡鍔犲叆鎾斁锛�#00B42A锛涙湭鍔犲叆锛�#86909C锛夈��
-鎿嶄綔鍒楋細缂栬緫銆佸垹闄わ紙浠呯鐞嗗憳鍙搷浣滐級銆併�愬姞鍏ユ挱鏀俱��/銆愮Щ闄ゆ挱鏀俱�戯紙鍒囨崲鎾斁鐘舵�侊級銆�
-3.7.2 鏂伴椈鎾斁璁剧疆
-椤甸潰鍏ュ彛锛氭柊闂诲垪琛ㄩ〉鏌ヨ妗嗗彸渚с�愯缃挱鏀俱�戞寜閽紱
-鍔熻兘锛�
-椤堕儴鏌ヨ妗嗭細鎸� 鈥滄柊闂诲悕绉扳�� 妯$硦鏌ヨ宸插姞鍏ユ挱鏀剧殑鏂伴椈锛�
-鎺掑簭鍒楄〃锛氭樉绀哄凡鍔犲叆鎾斁鐨勬柊闂伙紙鎸夋挱鏀鹃『搴忓崌搴忥級锛屾瘡涓�琛屽彸渚ф湁銆愪笂绉汇�戙�愪笅绉汇�戞寜閽紙璋冩暣椤哄簭锛夈�併�愮Щ闄ゃ�戞寜閽紙浠庢挱鏀惧垪琛ㄥ垹闄わ級锛�
-鎺掑簭瑙勫垯锛氭挱鏀鹃『搴忓瓧娈祍ort锛坕nt 绫诲瀷锛屽�艰秺灏忚秺闈犲墠锛岄粯璁ゆ寜鍔犲叆鏃堕棿鎺掑簭锛夈��
-3.7.3 鏂伴椈璇︽儏锛堢紪杈戦〉锛�
-琛ㄥ崟閰嶇疆锛�
-琛ㄥ崟椤�	鎺т欢绫诲瀷	鏍¢獙瑙勫垯
-鏂伴椈鍚嶇О	鍗曡杈撳叆妗�	闈炵┖锛岄暱搴︹墹50 瀛楃
-灏侀潰鍥�	鍥剧墖涓婁紶鎺т欢	闈炵┖锛孞PG/PNG锛屽ぇ灏忊墹5MB锛岃矾寰刢arousel_cover/yyyyMMdd/
-鏂伴椈鍐呭	瀵屾枃鏈紪杈戝櫒	闈炵┖锛屾敮鎸佽棰� / 鍥剧墖鎻掑叆锛堣棰戔墹200MB锛岃矾寰刢arousel_content/yyyyMMdd/锛�
-濯掍綋璧勬簮	瑙嗛 / 鍥剧墖涓婁紶鍖�	鍙�夛紝鏀寔澶氭枃浠朵笂浼狅紙瑙嗛鈮�500MB锛屽浘鐗団墹10MB锛夛紝鐢ㄤ簬璇︽儏椤甸澶栧睍绀�
-鎾斁椤哄簭	鏁板瓧杈撳叆妗�	鍙负绌猴紙鍔犲叆鎾斁鏃惰嚜鍔ㄨ祴鍊硷紝榛樿鏈�澶ч『搴� + 1锛�
-3.8 鏉冮檺瑙掕壊锛堢粏鍖栨潈闄愯寖鍥达級
-閲囩敤鏍囧噯 RBAC 妯″瀷锛屽唴缃� 4 绫讳笉鍙慨鏀圭殑瑙掕壊锛屾潈闄愯寖鍥村涓嬭〃锛�
-瑙掕壊	鍙闂彍鍗�	鏍稿績鎿嶄綔鏉冮檺
-瀛﹀憳	鏂伴椈涓庢帹骞�	鏌ョ湅鏂伴椈銆佸皬绋嬪簭绔姤鍚嶆瘮璧涖�佹煡鐪嬩釜浜哄弬璧涜褰�
-骞冲彴浜哄憳	姣旇禌绠$悊銆佸弬璧涗汉鍛樸�佹柊闂讳笌鎺ㄥ箍	缂栬緫姣旇禌 / 闃舵銆佸鏍稿鍛樻姤鍚嶃�佺鐞嗘柊闂伙紙涓嶅惈鍒犻櫎锛夈�佹煡鐪嬭瘎濮� / 璇勫垎妯℃澘
-璇勫	鍙傝禌浜哄憳锛堜粎鎸囧畾姣旇禌锛�	鏌ョ湅鎸囧畾姣旇禌鐨勫鍛樿祫鏂欍�佹寜璇勫垎妯℃澘鎵撳垎 + 濉啓璇勮銆佹煡鐪嬭瘎鍒嗙粨鏋�
-绠$悊鍛�	鎵�鏈夎彍鍗�	鎵�鏈夋搷浣滐紙鍚垹闄ゆ瘮璧� / 璇勫 / 妯℃澘銆侀厤缃挱鏀鹃『搴忋�佺鐞嗗憳宸ヨ鑹诧級
-3.9 涓撲笟鏍囩绠$悊锛堝搴旇〃锛歵_tag锛�
-3.9.1 涓撲笟鏍囩鍒楄〃
-鍒楄〃瀛楁锛�
-鏍囩鍚嶇О锛氶潪绌猴紝闀垮害鈮�10 瀛楃锛�
-鏍囩 Code锛氶粯璁や笌鏍囩鍚嶇О涓�鑷达紙濡傚悕绉� 鈥滈挗鐞粹�濓紝Code 鈥滈挗鐞粹�濓級锛屽敮涓�涓嶅彲淇敼锛�
-鍏宠仈璇勫鏁帮細鏄剧ず褰撳墠鏍囩宸插叧鑱旂殑璇勫鏁伴噺锛堝 鈥滃叧鑱� 5 浣嶈瘎濮斺�濓級銆�
-鎿嶄綔鍒楋細
-鏂板锛氱偣鍑汇�愭柊澧炴爣绛俱�戝脊鍑鸿〃鍗曪紙杈撳叆鏍囩鍚嶇О锛孋ode 鑷姩鐢熸垚锛宑ategory='major'锛夛紱
-鍒犻櫎锛氫粎鏈叧鑱旇瘎濮旂殑鏍囩鍙垹闄わ紙寮圭獥鎻愮ず 鈥滃凡鍏宠仈璇勫锛屼笉鍙垹闄も�濓級銆�
-鏍¢獙瑙勫垯锛氭柊澧炴爣绛炬椂锛屾牎楠� 鈥滄爣绛惧悕绉� / Code 鏄惁宸插瓨鍦ㄢ�濓紙閬垮厤閲嶅锛夈��
-3.10 娑堟伅绠$悊锛堝疄浣撶被锛歁essage锛屽搴旇〃锛歵_message锛�
-鍒楄〃瀛楁锛堟寜鍙戦�佹椂闂村�掑簭锛夛細
-瀛楁鍚�	灞曠ず瑙勫垯
-娑堟伅绫诲瀷	鏍囩鏄剧ず锛堝鏍搁�氱煡锛�#FF7D00锛涜瘎鍒嗛�氱煡锛�#00B42A锛涚郴缁熼�氱煡锛�#86909C锛�
-娑堟伅鍐呭	瓒呭嚭 30 瀛楃鏄剧ず鐪佺暐鍙凤紙hover 鏄剧ず瀹屾暣鍐呭锛�
-鎺ユ敹瀵硅薄	鏍煎紡锛氣�滃鍛橈細XXX鈥濃�滆瘎濮旓細XXX鈥濃�滄墍鏈変汉鈥�
-鍙戦�佹椂闂�	鏍煎紡锛歽yyy-MM-dd HH:mm
-宸茶鐘舵��	鏈鏍囩孩锛堟枃瀛楄壊 #F53F3F锛夛紝宸茶姝e父锛�#1D2129锛�
-鎿嶄綔鍒楋細
-鏍囪宸茶锛氱偣鍑诲皢鏈娑堟伅鏀逛负宸茶锛�
-鏌ョ湅璇︽儏锛氬脊鍑哄脊绐楁樉绀哄畬鏁存秷鎭唴瀹癸紙鍚烦杞摼鎺ワ紝濡傚鏍搁�氱煡璺宠浆鑷冲鍛樿鎯呴〉锛夈��
-娑堟伅鍙戦�佽鍒欙細
-瀹℃牳閫氱煡锛氬鍛樻姤鍚嶅鏍搁�氳繃 / 鎷掔粷鏃讹紝鑷姩鍙戦�佺粰瀵瑰簲瀛﹀憳锛�
-璇勫垎閫氱煡锛氳瘎濮斿畬鎴愯瘎鍒嗗悗锛岃嚜鍔ㄥ彂閫佺粰瀵瑰簲瀛﹀憳锛�
-绯荤粺閫氱煡锛氱鐞嗗憳鎵嬪姩鍙戦�侊紙鏂板 鈥滃彂閫佺郴缁熸秷鎭�� 琛ㄥ崟锛岄�夋嫨鎺ユ敹瀵硅薄鍜屽唴瀹癸級銆�
-鍥涖�佸井淇$闇�姹傦紙琛ュ厖鍩虹妗嗘灦锛�
-鏆傛湭瀹炵幇锛屼絾闇�棰勭暀浠ヤ笅鎺ュ彛閫傞厤灏忕▼搴忥細
-瀛﹀憳鐧诲綍鎺ュ彛锛坵echatMiniLogin锛夛細閫氳繃寰俊灏忕▼搴� Code 鑾峰彇 OpenID锛屽叧鑱攖_player琛紱
-姣旇禌鍒楄〃鎺ュ彛锛坢iniActivityList锛夛細杩斿洖褰撳墠鍙姤鍚嶇殑姣旇禌锛堣繃婊ゅ凡杩囨湡銆佸凡杈句汉鏁颁笂闄愮殑姣旇禌锛夛紱
-鎶ュ悕鎺ュ彛锛坢iniPlayerEnroll锛夛細鎺ユ敹瀛﹀憳 ID銆佹瘮璧� ID銆佹姤鍚嶈祫鏂欙紙濯掍綋 ID锛夛紝鐢熸垚鍙傝禌璁板綍锛�
-涓汉涓績鎺ュ彛锛坢iniPlayerCenter锛夛細杩斿洖瀛﹀憳鍩烘湰淇℃伅銆佸弬璧涜褰曘�佹秷鎭垪琛ㄣ��
-浜斻�佽璁¤鐐癸紙缁嗗寲鐗堬級
-鍒楄〃閫氱敤瑙勫垯锛�
-鍒嗛〉锛氭墍鏈夊垪琛ㄩ粯璁ゅ垎椤碉紙椤电爜榛樿 1锛屾瘡椤� 10 鏉★紝鏀寔鍒囨崲 鈥�10/20/50 鏉� / 椤碘�濓級锛�
-鎺掑簭锛氶粯璁ゆ寜 鈥滃垱寤烘椂闂�
-鍙戞秷鎭垨杈撳叆 / 閫夋嫨鎶�鑳�

--
Gitblit v1.8.0