diff --git a/.env b/.env
index c62dd40..f405e84 100644
--- a/.env
+++ b/.env
@@ -3,6 +3,7 @@
# ============================================
# MySQL 配置
+INSTALL_MODE=service
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USERNAME=root
@@ -10,11 +11,14 @@ MYSQL_PASSWORD=zjf@123456
MYSQL_AUTH_PLUGIN=caching_sha2_password
# Redis 配置
+INSTALL_MODE=service
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=zjf@123456
# Nacos 配置
+INSTALL_PATH=/home/along/MyApp/nacos
+INSTALL_MODE=local
NACOS_HOST=localhost
NACOS_PORT=8848
NACOS_USERNAME=nacos
diff --git a/docker/app/docker-compose.yml b/docker/app/docker-compose.yml
new file mode 100644
index 0000000..598bdf8
--- /dev/null
+++ b/docker/app/docker-compose.yml
@@ -0,0 +1,183 @@
+version: '3.8'
+
+services:
+ # MySQL数据库
+ mysql:
+ image: mysql:8.0
+ container_name: fund-mysql
+ environment:
+ MYSQL_ROOT_PASSWORD: root123456
+ MYSQL_DATABASE: fundplatform
+ MYSQL_USER: funduser
+ MYSQL_PASSWORD: fund123456
+ TZ: Asia/Shanghai
+ ports:
+ - "3306:3306"
+ volumes:
+ - mysql-data:/var/lib/mysql
+ - ./init:/docker-entrypoint-initdb.d
+ command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
+ networks:
+ - fund-network
+ healthcheck:
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ # Redis缓存
+ redis:
+ image: redis:7-alpine
+ container_name: fund-redis
+ ports:
+ - "6379:6379"
+ volumes:
+ - redis-data:/data
+ - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
+ command: redis-server /usr/local/etc/redis/redis.conf --requirepass zjf@123456
+ networks:
+ - fund-network
+ healthcheck:
+ test: ["CMD", "redis-cli", "ping"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ # Nacos服务注册中心
+ nacos:
+ image: nacos/nacos-server:v2.3.0
+ container_name: fund-nacos
+ environment:
+ MODE: standalone
+ SPRING_DATASOURCE_PLATFORM: mysql
+ MYSQL_SERVICE_HOST: mysql
+ MYSQL_SERVICE_DB_NAME: nacos
+ MYSQL_SERVICE_PORT: 3306
+ MYSQL_SERVICE_USER: root
+ MYSQL_SERVICE_PASSWORD: root123456
+ NACOS_AUTH_ENABLE: true
+ NACOS_CORE_AUTH_SERVER_IDENTITY_KEY: fundplatform
+ NACOS_CORE_AUTH_SERVER_IDENTITY_VALUE: fundplatform123
+ NACOS_CORE_AUTH_DEFAULT_TOKEN_SECRET_KEY: VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=
+ JVM_XMS: 512m
+ JVM_XMX: 512m
+ ports:
+ - "8848:8848"
+ - "9848:9848"
+ volumes:
+ - nacos-data:/home/nacos/data
+ networks:
+ - fund-network
+ depends_on:
+ mysql:
+ condition: service_healthy
+
+ # fund-sys服务
+ fund-sys:
+ build:
+ context: ../../fundplatform
+ dockerfile: fund-sys/Dockerfile
+ container_name: fund-sys
+ environment:
+ SPRING_PROFILES_ACTIVE: docker
+ NACOS_SERVER_ADDR: nacos:8848
+ MYSQL_HOST: mysql
+ REDIS_HOST: redis
+ REDIS_PASSWORD: zjf@123456
+ JVM_OPTS: -Xms512m -Xmx1024m
+ ports:
+ - "8080:8080"
+ volumes:
+ - ./logs/fund-sys:/app/logs
+ networks:
+ - fund-network
+ depends_on:
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ nacos:
+ condition: service_started
+ restart: unless-stopped
+
+ # fund-cust服务
+ fund-cust:
+ build:
+ context: ../../fundplatform
+ dockerfile: fund-cust/Dockerfile
+ container_name: fund-cust
+ environment:
+ SPRING_PROFILES_ACTIVE: docker
+ NACOS_SERVER_ADDR: nacos:8848
+ MYSQL_HOST: mysql
+ REDIS_HOST: redis
+ REDIS_PASSWORD: zjf@123456
+ JVM_OPTS: -Xms512m -Xmx1024m
+ ports:
+ - "8082:8082"
+ volumes:
+ - ./logs/fund-cust:/app/logs
+ networks:
+ - fund-network
+ depends_on:
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ nacos:
+ condition: service_started
+ restart: unless-stopped
+
+ # fund-proj服务
+ fund-proj:
+ build:
+ context: ../../fundplatform
+ dockerfile: fund-proj/Dockerfile
+ container_name: fund-proj
+ environment:
+ SPRING_PROFILES_ACTIVE: docker
+ NACOS_SERVER_ADDR: nacos:8848
+ MYSQL_HOST: mysql
+ REDIS_HOST: redis
+ REDIS_PASSWORD: zjf@123456
+ JVM_OPTS: -Xms512m -Xmx1024m
+ ports:
+ - "8081:8081"
+ volumes:
+ - ./logs/fund-proj:/app/logs
+ networks:
+ - fund-network
+ depends_on:
+ mysql:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+ nacos:
+ condition: service_started
+ restart: unless-stopped
+
+ # Nginx网关
+ nginx:
+ image: nginx:alpine
+ container_name: fund-nginx
+ ports:
+ - "80:80"
+ - "443:443"
+ volumes:
+ - ./nginx/nginx.conf:/etc/nginx/nginx.conf
+ - ./nginx/html:/usr/share/nginx/html
+ networks:
+ - fund-network
+ depends_on:
+ - fund-sys
+ - fund-cust
+ - fund-proj
+
+volumes:
+ mysql-data:
+ redis-data:
+ nacos-data:
+
+networks:
+ fund-network:
+ driver: bridge
diff --git a/docker/app/nginx/nginx.conf b/docker/app/nginx/nginx.conf
new file mode 100644
index 0000000..694361c
--- /dev/null
+++ b/docker/app/nginx/nginx.conf
@@ -0,0 +1,119 @@
+user nginx;
+worker_processes auto;
+error_log /var/log/nginx/error.log warn;
+pid /var/run/nginx.pid;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ # 日志格式
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for" '
+ 'rt=$request_time uct="$upstream_connect_time" '
+ 'uht="$upstream_header_time" urt="$upstream_response_time"';
+
+ access_log /var/log/nginx/access.log main;
+
+ # 性能优化
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+
+ # Gzip压缩
+ gzip on;
+ gzip_vary on;
+ gzip_proxied any;
+ gzip_comp_level 6;
+ gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
+
+ # 上游服务器
+ upstream fund_sys {
+ server fund-sys:8080;
+ }
+
+ upstream fund_cust {
+ server fund-cust:8082;
+ }
+
+ upstream fund_proj {
+ server fund-proj:8081;
+ }
+
+ # HTTP服务器
+ server {
+ listen 80;
+ server_name localhost;
+
+ # 前端静态资源
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ try_files $uri $uri/ /index.html;
+ }
+
+ # API代理 - 系统服务
+ location /api/v1/sys/ {
+ proxy_pass http://fund_sys/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_connect_timeout 60s;
+ proxy_send_timeout 60s;
+ proxy_read_timeout 60s;
+ }
+
+ # API代理 - 客户中心
+ location /api/v1/cust/ {
+ proxy_pass http://fund_cust/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+ # API代理 - 项目管理
+ location /api/v1/proj/ {
+ proxy_pass http://fund_proj/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+ # API代理 - 通用接口(转发到sys服务)
+ location /api/v1/ {
+ proxy_pass http://fund_sys/;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+
+ # Swagger文档
+ location /swagger-ui/ {
+ proxy_pass http://fund_sys/swagger-ui/;
+ proxy_set_header Host $host;
+ }
+
+ # Actuator健康检查
+ location /actuator/ {
+ proxy_pass http://fund_sys/actuator/;
+ proxy_set_header Host $host;
+ }
+
+ # 错误页面
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+ }
+}
diff --git a/docker/elk/docker-compose.yml b/docker/elk/docker-compose.yml
new file mode 100644
index 0000000..37f466d
--- /dev/null
+++ b/docker/elk/docker-compose.yml
@@ -0,0 +1,81 @@
+version: '3.8'
+
+services:
+ # Elasticsearch
+ elasticsearch:
+ image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
+ container_name: fund-elasticsearch
+ environment:
+ - discovery.type=single-node
+ - xpack.security.enabled=false
+ - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
+ - cluster.routing.allocation.disk.threshold_enabled=false
+ ports:
+ - "9200:9200"
+ volumes:
+ - elasticsearch-data:/usr/share/elasticsearch/data
+ networks:
+ - elk-network
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:9200/_cluster/health"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+
+ # Logstash
+ logstash:
+ image: docker.elastic.co/logstash/logstash:8.11.0
+ container_name: fund-logstash
+ environment:
+ - "LS_JAVA_OPTS=-Xms256m -Xmx256m"
+ ports:
+ - "5044:5044"
+ - "9600:9600"
+ volumes:
+ - ./logstash/config:/usr/share/logstash/config
+ - ./logstash/pipeline:/usr/share/logstash/pipeline
+ - /home/along/MyCode/wanjiabuluo/fundplatform/logs:/var/log/fundplatform:ro
+ networks:
+ - elk-network
+ depends_on:
+ elasticsearch:
+ condition: service_healthy
+
+ # Kibana
+ kibana:
+ image: docker.elastic.co/kibana/kibana:8.11.0
+ container_name: fund-kibana
+ environment:
+ - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
+ - xpack.security.enabled=false
+ ports:
+ - "5601:5601"
+ networks:
+ - elk-network
+ depends_on:
+ elasticsearch:
+ condition: service_healthy
+
+ # Filebeat
+ filebeat:
+ image: docker.elastic.co/beats/filebeat:8.11.0
+ container_name: fund-filebeat
+ user: root
+ volumes:
+ - ./filebeat/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
+ - /home/along/MyCode/wanjiabuluo/fundplatform/logs:/var/log/fundplatform:ro
+ - /var/lib/docker/containers:/var/lib/docker/containers:ro
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ networks:
+ - elk-network
+ depends_on:
+ logstash:
+ condition: service_started
+
+volumes:
+ elasticsearch-data:
+ driver: local
+
+networks:
+ elk-network:
+ driver: bridge
diff --git a/docker/elk/filebeat/filebeat.yml b/docker/elk/filebeat/filebeat.yml
new file mode 100644
index 0000000..01ad18e
--- /dev/null
+++ b/docker/elk/filebeat/filebeat.yml
@@ -0,0 +1,54 @@
+filebeat.inputs:
+- type: log
+ enabled: true
+ paths:
+ - /var/log/fundplatform/fund-sys-json.log
+ - /var/log/fundplatform/fund-proj-json.log
+ fields:
+ log_source: fundplatform
+ service_type: application
+ fields_under_root: true
+ multiline.pattern: '^\{'
+ multiline.negate: true
+ multiline.match: after
+ json.keys_under_root: true
+ json.add_error_key: true
+
+- type: log
+ enabled: true
+ paths:
+ - /var/log/fundplatform/fund-sys-error.log
+ fields:
+ log_source: fundplatform
+ service_type: error
+ log_level: error
+ fields_under_root: true
+
+# 容器日志收集
+- type: container
+ paths:
+ - '/var/lib/docker/containers/*/*.log'
+ processors:
+ - add_docker_metadata:
+ host: "unix:///var/run/docker.sock"
+
+# 输出到Logstash
+output.logstash:
+ hosts: ["logstash:5044"]
+ enabled: true
+
+# 处理器
+processors:
+ - add_host_metadata:
+ when.not.contains.tags: forwarded
+ - add_cloud_metadata: ~
+ - add_docker_metadata: ~
+
+# 日志级别
+logging.level: info
+logging.to_files: true
+logging.files:
+ path: /var/log/filebeat
+ name: filebeat
+ keepfiles: 7
+ permissions: 0644
diff --git a/docker/elk/logstash/pipeline/logstash.conf b/docker/elk/logstash/pipeline/logstash.conf
new file mode 100644
index 0000000..30dc5c6
--- /dev/null
+++ b/docker/elk/logstash/pipeline/logstash.conf
@@ -0,0 +1,68 @@
+input {
+ beats {
+ port => 5044
+ }
+
+ file {
+ path => "/var/log/fundplatform/fund-sys-json.log"
+ start_position => "beginning"
+ sincedb_path => "/dev/null"
+ codec => "json"
+ type => "application"
+ }
+}
+
+filter {
+ if [type] == "application" {
+ # 解析时间戳
+ date {
+ match => [ "timestamp", "yyyy-MM-dd HH:mm:ss.SSS" ]
+ target => "@timestamp"
+ }
+
+ # 添加环境标签
+ mutate {
+ add_field => {
+ "environment" => "production"
+ "log_source" => "fundplatform"
+ }
+ }
+
+ # 提取日志级别
+ if [level] {
+ mutate {
+ add_field => { "log_level" => "%{level}" }
+ }
+ }
+
+ # 错误日志特殊处理
+ if [level] == "ERROR" {
+ mutate {
+ add_tag => [ "error_log" ]
+ }
+ }
+ }
+}
+
+output {
+ if [type] == "application" {
+ elasticsearch {
+ hosts => ["elasticsearch:9200"]
+ index => "fundplatform-logs-%{+YYYY.MM.dd}"
+ template_name => "fundplatform"
+ }
+ }
+
+ # 错误日志单独索引
+ if "error_log" in [tags] {
+ elasticsearch {
+ hosts => ["elasticsearch:9200"]
+ index => "fundplatform-errors-%{+YYYY.MM.dd}"
+ }
+ }
+
+ # 同时输出到控制台(调试)
+ stdout {
+ codec => rubydebug
+ }
+}
diff --git a/fund-admin/src/api/auth.js b/fund-admin/src/api/auth.js
index 94723c3..e465661 100644
--- a/fund-admin/src/api/auth.js
+++ b/fund-admin/src/api/auth.js
@@ -2,7 +2,7 @@ import request from '../utils/request'
export const login = (data) => {
return request({
- url: '/auth/login',
+ url: '/sys/api/v1/auth/login',
method: 'post',
data
})
@@ -10,7 +10,7 @@ export const login = (data) => {
export const refreshToken = (refreshToken) => {
return request({
- url: '/auth/refresh',
+ url: '/sys/api/v1/auth/refresh',
method: 'post',
data: { refreshToken }
})
@@ -18,7 +18,7 @@ export const refreshToken = (refreshToken) => {
export const logout = () => {
return request({
- url: '/auth/logout',
+ url: '/sys/api/v1/auth/logout',
method: 'post'
})
}
diff --git a/fund-admin/src/api/sysConfig.js b/fund-admin/src/api/sysConfig.js
new file mode 100644
index 0000000..5547e6e
--- /dev/null
+++ b/fund-admin/src/api/sysConfig.js
@@ -0,0 +1,63 @@
+import request from '@/utils/request'
+
+// 系统配置管理API
+
+export function getConfigPage(params) {
+ return request({
+ url: '/sys/config/page',
+ method: 'get',
+ params
+ })
+}
+
+export function getConfigById(configId) {
+ return request({
+ url: `/sys/config/${configId}`,
+ method: 'get'
+ })
+}
+
+export function getConfigValue(configKey) {
+ return request({
+ url: `/sys/config/value/${configKey}`,
+ method: 'get'
+ })
+}
+
+export function getConfigByGroup(configGroup) {
+ return request({
+ url: `/sys/config/group/${configGroup}`,
+ method: 'get'
+ })
+}
+
+export function createConfig(data) {
+ return request({
+ url: '/sys/config',
+ method: 'post',
+ data
+ })
+}
+
+export function updateConfig(configId, data) {
+ return request({
+ url: `/sys/config/${configId}`,
+ method: 'put',
+ data
+ })
+}
+
+export function deleteConfig(configId) {
+ return request({
+ url: `/sys/config/${configId}`,
+ method: 'delete'
+ })
+}
+
+export function batchDeleteConfig(configIds) {
+ return request({
+ url: '/sys/config/batch',
+ method: 'delete',
+ params: { configIds }
+ })
+}
diff --git a/fund-admin/src/api/sysDict.js b/fund-admin/src/api/sysDict.js
new file mode 100644
index 0000000..53be994
--- /dev/null
+++ b/fund-admin/src/api/sysDict.js
@@ -0,0 +1,78 @@
+import request from '@/utils/request'
+
+// 数据字典管理API
+
+export function getDictPage(params) {
+ return request({
+ url: '/sys/dict/page',
+ method: 'get',
+ params
+ })
+}
+
+export function getDictById(dictId) {
+ return request({
+ url: `/sys/dict/${dictId}`,
+ method: 'get'
+ })
+}
+
+export function getDictByType(dictType) {
+ return request({
+ url: `/sys/dict/type/${dictType}`,
+ method: 'get'
+ })
+}
+
+export function getAllDictTypes() {
+ return request({
+ url: '/sys/dict/types',
+ method: 'get'
+ })
+}
+
+export function getDictMap(dictType) {
+ return request({
+ url: `/sys/dict/map/${dictType}`,
+ method: 'get'
+ })
+}
+
+export function getDictLabel(dictType, dictCode) {
+ return request({
+ url: '/sys/dict/label',
+ method: 'get',
+ params: { dictType, dictCode }
+ })
+}
+
+export function createDict(data) {
+ return request({
+ url: '/sys/dict',
+ method: 'post',
+ data
+ })
+}
+
+export function updateDict(dictId, data) {
+ return request({
+ url: `/sys/dict/${dictId}`,
+ method: 'put',
+ data
+ })
+}
+
+export function deleteDict(dictId) {
+ return request({
+ url: `/sys/dict/${dictId}`,
+ method: 'delete'
+ })
+}
+
+export function batchDeleteDict(dictIds) {
+ return request({
+ url: '/sys/dict/batch',
+ method: 'delete',
+ params: { dictIds }
+ })
+}
diff --git a/fund-admin/src/views/finance/receipt.vue b/fund-admin/src/views/finance/receipt.vue
new file mode 100644
index 0000000..7ada1eb
--- /dev/null
+++ b/fund-admin/src/views/finance/receipt.vue
@@ -0,0 +1,391 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增收款记录
+
+
+
+
+
+
+
+ {{ getReceivableName(row.receivableId) }}
+
+
+
+
+ ¥{{ row.receiptAmount }}
+
+
+
+
+
+ {{ getReceiptMethodText(row.receiptMethod) }}
+
+
+
+
+
+
+
+
+
+ 编辑
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 确定
+
+
+
+
+
+
+
+
diff --git a/fund-admin/src/views/system/sysConfig.vue b/fund-admin/src/views/system/sysConfig.vue
new file mode 100644
index 0000000..78d0b6c
--- /dev/null
+++ b/fund-admin/src/views/system/sysConfig.vue
@@ -0,0 +1,404 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getTypeLabel(row.configType) }}
+
+
+
+
+
+ {{ getGroupLabel(row.configGroup) }}
+
+
+
+
+
+ {{ row.isSystem === 1 ? '是' : '否' }}
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 否
+ 是
+
+
+
+
+
+
+
+ 取消
+ 确定
+
+
+
+
+
+
+
+
diff --git a/fund-admin/src/views/system/sysDict.vue b/fund-admin/src/views/system/sysDict.vue
new file mode 100644
index 0000000..503eead
--- /dev/null
+++ b/fund-admin/src/views/system/sysDict.vue
@@ -0,0 +1,393 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+
+
+ 重置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.isDefault === 1 ? '是' : '否' }}
+
+
+
+
+
+
+
+
+
+
+
+ 编辑
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 否
+ 是
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 确定
+
+
+
+
+
+
+
+
diff --git a/fundplatform b/fundplatform
index c4dcf33..85528d3 160000
--- a/fundplatform
+++ b/fundplatform
@@ -1 +1 @@
-Subproject commit c4dcf332ba4422a31de94bef9ecd7b72168df888
+Subproject commit 85528d3f3bcefbcea1a237175ee93b18f7b8feb2
diff --git a/jmeter/fundplatform-test-plan.jmx b/jmeter/fundplatform-test-plan.jmx
new file mode 100644
index 0000000..6c2d2a6
--- /dev/null
+++ b/jmeter/fundplatform-test-plan.jmx
@@ -0,0 +1,349 @@
+
+
+
+
+
+ false
+ true
+ false
+
+
+
+ BASE_URL
+ localhost
+ =
+
+
+ PORT
+ 8080
+ =
+
+
+
+
+
+
+
+
+ continue
+
+ false
+ 10
+
+ 50
+ 10
+ false
+
+
+ true
+
+
+
+
+
+
+ false
+ admin
+ =
+ true
+ username
+
+
+ false
+ admin123
+ =
+ true
+ password
+
+
+
+ ${BASE_URL}
+ ${PORT}
+ http
+
+ /api/v1/auth/login
+ POST
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ Content-Type
+ application/json
+
+
+
+
+
+ token
+ $.data.token
+ 1
+
+
+
+
+ false
+
+ saveConfig
+
+
+ true
+ true
+ true
+
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ true
+ 0
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+
+
+
+
+ false
+
+ saveConfig
+
+
+ true
+ true
+ true
+
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ true
+ 0
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+ results/login_result.csv
+
+
+
+
+
+
+ continue
+
+ false
+ 20
+
+ 100
+ 20
+ false
+
+
+ true
+
+
+
+
+
+
+ false
+ 1
+ =
+ true
+ current
+
+
+ false
+ 10
+ =
+ true
+ size
+
+
+
+ ${BASE_URL}
+ 8082
+ http
+
+ /api/v1/customer/list
+ GET
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ Authorization
+ Bearer ${token}
+
+
+
+
+
+
+ false
+
+ saveConfig
+
+
+ true
+ true
+ true
+
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ true
+ 0
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+ results/customer_result.csv
+
+
+
+
+
+
+ continue
+
+ false
+ 50
+
+ 200
+ 30
+ false
+
+
+ true
+
+
+
+
+
+
+ ${BASE_URL}
+ 8081
+ http
+
+ /api/v1/dashboard
+ GET
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ Authorization
+ Bearer ${token}
+
+
+
+
+
+
+ false
+
+ saveConfig
+
+
+ true
+ true
+ true
+
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ true
+ 0
+ true
+ true
+ true
+ true
+ true
+ true
+
+
+ results/dashboard_result.csv
+
+
+
+
+
+
diff --git a/jmeter/run-performance-test.sh b/jmeter/run-performance-test.sh
new file mode 100755
index 0000000..1d991d6
--- /dev/null
+++ b/jmeter/run-performance-test.sh
@@ -0,0 +1,145 @@
+#!/bin/bash
+
+# 资金服务平台性能测试脚本
+
+set -e
+
+echo "========================================"
+echo " 资金服务平台 JMeter 性能测试"
+echo "========================================"
+
+# 颜色定义
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m'
+
+print_info() {
+ echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+print_warn() {
+ echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+print_error() {
+ echo -e "${RED}[ERROR]${NC} $1"
+}
+
+print_title() {
+ echo -e "${BLUE}[TEST]${NC} $1"
+}
+
+# 检查JMeter
+if ! command -v jmeter &> /dev/null; then
+ print_error "JMeter 未安装,请先安装 JMeter"
+ echo "下载地址: https://jmeter.apache.org/download_jmeter.cgi"
+ exit 1
+fi
+
+# 创建结果目录
+mkdir -p results reports
+
+# 解析参数
+TEST_TYPE=${1:-all}
+BASE_URL=${2:-localhost}
+PORT=${3:-8080}
+
+print_info "测试目标: http://${BASE_URL}:${PORT}"
+
+# 函数:运行测试
+run_test() {
+ local test_name=$1
+ local thread_count=$2
+ local duration=$3
+
+ print_title "开始测试: ${test_name}"
+ print_info "并发数: ${thread_count}, 持续时间: ${duration}s"
+
+ jmeter -n -t fundplatform-test-plan.jmx \
+ -Jbase_url=${BASE_URL} \
+ -Jport=${PORT} \
+ -Jthread_count=${thread_count} \
+ -Jduration=${duration} \
+ -l results/${test_name}_$(date +%Y%m%d_%H%M%S).jtl \
+ -e -o reports/${test_name}_$(date +%Y%m%d_%H%M%S)
+
+ print_info "测试完成: ${test_name}"
+}
+
+# 根据测试类型执行
+case $TEST_TYPE in
+ login)
+ print_title "执行登录接口压测"
+ run_test "login" 50 60
+ ;;
+ customer)
+ print_title "执行客户列表查询压测"
+ run_test "customer" 100 120
+ ;;
+ dashboard)
+ print_title "执行仪表盘统计压测"
+ run_test "dashboard" 200 180
+ ;;
+ all)
+ print_title "执行全部性能测试"
+ print_warn "这将执行所有测试场景,耗时较长..."
+
+ # 登录接口压测
+ print_title "场景1: 登录接口压测"
+ print_info "并发50用户,持续60秒"
+ jmeter -n -t fundplatform-test-plan.jmx \
+ -Jbase_url=${BASE_URL} \
+ -Jport=${PORT} \
+ -l results/login_test_$(date +%Y%m%d_%H%M%S).jtl
+
+ # 客户查询压测
+ print_title "场景2: 客户列表查询压测"
+ print_info "并发100用户,持续120秒"
+ # 这里可以添加更多测试场景
+
+ # 仪表盘压测
+ print_title "场景3: 仪表盘统计压测"
+ print_info "并发200用户,持续180秒"
+
+ print_info "所有测试完成"
+ ;;
+ report)
+ print_title "生成测试报告"
+ if [ -f "results/latest.jtl" ]; then
+ jmeter -g results/latest.jtl -o reports/latest_report
+ print_info "报告已生成: reports/latest_report/index.html"
+ else
+ print_error "没有找到测试结果文件"
+ exit 1
+ fi
+ ;;
+ *)
+ echo "用法: $0 {login|customer|dashboard|all|report} [base_url] [port]"
+ echo ""
+ echo "测试类型:"
+ echo " login - 登录接口压测 (50并发)"
+ echo " customer - 客户列表查询压测 (100并发)"
+ echo " dashboard - 仪表盘统计压测 (200并发)"
+ echo " all - 执行全部测试"
+ echo " report - 生成测试报告"
+ echo ""
+ echo "示例:"
+ echo " $0 all localhost 8080"
+ echo " $0 login 192.168.1.100 8080"
+ exit 1
+ ;;
+esac
+
+echo ""
+echo "========================================"
+echo " 性能测试执行完成"
+echo "========================================"
+echo ""
+echo "测试结果:"
+echo " - JTL文件: results/"
+echo " - HTML报告: reports/"
+echo ""
+echo "查看报告:"
+echo " open reports/*/index.html"
diff --git a/开发规则清单.md b/开发规则清单.md
new file mode 100644
index 0000000..3924276
--- /dev/null
+++ b/开发规则清单.md
@@ -0,0 +1,429 @@
+# 资金服务平台 - 开发规则清单
+
+> **创建时间**: 2026-02-17
+> **用途**: 重新生成代码前的规范和约束
+> **版本**: v1.0
+
+---
+
+## 1. 技术栈规范
+
+### 1.1 核心框架
+- **Java**: 21
+- **Spring Boot**: 3.2.0
+- **Spring Cloud**: 2023.0.0
+- **认证框架**: Apache Shiro 2.0.0(必须,不使用 Spring Security)
+- **数据库连接池**: HikariCP
+- **ORM框架**: MyBatis-Plus 3.5.6
+
+### 1.2 依赖版本锁定
+```xml
+1.18.30
+2.0.0
+4.4.0
+5.8.23
+3.5.6
+```
+
+### 1.3 禁用的依赖
+- ❌ `spring-boot-starter-security`
+- ❌ Shiro 1.x(不兼容 Spring Boot 3)
+
+---
+
+## 2. Maven 多模块配置规范
+
+### 2.1 父 POM 配置
+```xml
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+ 21
+ 21
+
+ -parameters
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+
+
+
+```
+
+### 2.2 子模块 POM 配置
+**每个子模块都必须显式激活 maven-compiler-plugin**:
+```xml
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+ 21
+ 21
+
+ -parameters
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+
+
+
+```
+
+### 2.3 依赖冲突检查
+- ⚠️ 避免在 pom.xml 中重复声明同一依赖
+- ⚠️ 使用 `mvn dependency:tree` 检查依赖冲突
+
+---
+
+## 3. Lombok 使用规范
+
+### 3.1 禁止在继承 ServiceImpl 的类上使用 @Slf4j
+
+**❌ 错误示例**:
+```java
+@Slf4j
+@Service
+public class DeptService extends ServiceImpl {
+ public void saveDept(Dept dept) {
+ log.info("保存部门"); // 编译错误!
+ }
+}
+```
+
+**✅ 正确示例**:
+```java
+@Service
+public class DeptService extends ServiceImpl {
+ private static final Logger logger = LoggerFactory.getLogger(DeptService.class);
+
+ public void saveDept(Dept dept) {
+ logger.info("保存部门");
+ }
+}
+```
+
+**原因**: MyBatis-Plus 的 ServiceImpl 基类已定义 `protected Log log`(org.apache.ibatis.logging.Log),与 Lombok @Slf4j 生成的字段冲突。
+
+### 3.2 实体类 Lombok 注解规范
+```java
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("sys_dept")
+public class Dept extends BaseEntity {
+ // 必须确保 import lombok.Data 和 import lombok.EqualsAndHashCode 存在
+}
+```
+
+### 3.3 Controller/Component Lombok 注解
+```java
+@RestController
+@RequestMapping("/api/v1/dept")
+@RequiredArgsConstructor // 推荐用于依赖注入
+public class DeptController {
+ private final DeptService deptService;
+}
+```
+
+---
+
+## 4. Shiro 认证框架规范
+
+### 4.1 核心组件清单
+1. **JwtToken.java** - 自定义 Token 类
+2. **JwtRealm.java** - 认证授权域
+3. **JwtFilter.java** - JWT 过滤器
+4. **ShiroConfig.java** - Shiro 配置类
+5. **PasswordEncoderConfig.java** - 密码编码器(使用 BCrypt)
+
+### 4.2 权限注解规范
+- ✅ 使用 `@RequiresRoles("ADMIN")`
+- ✅ 使用 `@RequiresPermissions("system:user:view")`
+- ❌ 不使用 Spring Security 注解(@PreAuthorize)
+
+### 4.3 认证流程
+1. 用户登录 → 查询数据库
+2. BCrypt 验证密码
+3. 生成 JWT Token(包含 userId, username, tenantId)
+4. 每次请求通过 JwtFilter 验证 Token
+5. JwtRealm 加载用户权限
+
+---
+
+## 5. 代码编写规范
+
+### 5.1 分层架构
+```
+Controller → Service → Mapper → Database
+ ↓ ↓
+ DTO Entity
+```
+
+### 5.2 包结构规范
+```
+com.fundplatform.sys
+├── controller # 控制器层
+├── service # 业务逻辑层
+├── mapper # 数据访问层
+├── entity # 实体类
+├── dto # 数据传输对象
+├── vo # 视图对象
+├── security # 安全相关(Shiro配置)
+├── config # 配置类
+└── util # 工具类
+```
+
+### 5.3 命名规范
+- **Entity**: 单数名词,如 `User.java`、`Dept.java`
+- **Mapper**: 实体名 + Mapper,如 `UserMapper.java`
+- **Service**: 实体名 + Service,如 `UserService.java`
+- **Controller**: 实体名 + Controller,如 `UserController.java`
+
+### 5.4 日志规范
+```java
+// 业务操作日志
+logger.info("[ServiceName] 业务操作描述: {}, 参数: {}", entity.getName(), param);
+
+// 错误日志
+logger.error("[ServiceName] 操作失败: {}", errorMessage, exception);
+```
+
+---
+
+## 6. 数据库规范
+
+### 6.1 表命名规范
+- 系统表前缀: `sys_`(如 `sys_user`, `sys_role`)
+- 业务表前缀: `biz_`(如 `biz_project`, `biz_fund`)
+
+### 6.2 字段规范
+- 主键: `主表名_id`(如 `user_id`, `dept_id`)
+- 租户ID: `tenant_id`(必须字段,默认值 1)
+- 软删除: `deleted`(0-未删除,1-已删除)
+- 状态: `status`(1-启用,0-禁用)
+- 时间: `created_time`, `updated_time`
+
+### 6.3 BaseEntity 基类
+```java
+@Data
+public class BaseEntity implements Serializable {
+ private Long tenantId; // 租户ID
+ private Integer deleted; // 删除标记
+ private LocalDateTime createdTime; // 创建时间
+ private Long createdBy; // 创建人
+ private LocalDateTime updatedTime; // 更新时间
+ private Long updatedBy; // 更新人
+}
+```
+
+---
+
+## 7. API 规范
+
+### 7.1 RESTful 路径规范
+```
+GET /api/v1/dept # 查询列表
+GET /api/v1/dept/{id} # 查询单个
+POST /api/v1/dept # 创建
+PUT /api/v1/dept/{id} # 更新
+DELETE /api/v1/dept/{id} # 删除
+```
+
+### 7.2 统一响应格式
+```java
+public class Result {
+ private Integer code; // 200-成功,其他-失败
+ private String message;
+ private T data;
+}
+```
+
+### 7.3 认证路径规范
+```
+POST /api/v1/auth/login # 登录(不需要认证)
+POST /api/v1/auth/logout # 登出
+POST /api/v1/auth/refresh # 刷新Token
+```
+
+---
+
+## 8. 多租户规范
+
+### 8.1 租户隔离
+- 所有业务表必须包含 `tenant_id` 字段
+- Service 层插入数据时自动设置 `tenant_id`
+- 查询时自动过滤 `tenant_id`
+
+### 8.2 默认租户
+- 系统租户: `tenant_id = 0`
+- 业务租户: `tenant_id >= 1`
+
+---
+
+## 9. 编译和测试规范
+
+### 9.1 编译命令
+```bash
+# 清理编译
+cd /home/along/MyCode/wanjiabuluo/fundplatform/fundplatform
+mvn clean compile -DskipTests
+
+# 安装到本地仓库
+mvn clean install -DskipTests
+
+# 编译单个模块
+mvn clean compile -pl fund-sys -am -DskipTests
+```
+
+### 9.2 禁止的操作
+- ❌ 不要在 run_in_terminal 中重复 `cd` 到同一目录
+- ❌ 不要使用 `mvn clean` 后立即执行其他命令(应分开执行)
+
+---
+
+## 10. Git 规范
+
+### 10.1 分支策略
+- `main` - 生产分支
+- `develop` - 开发分支
+- `feature/*` - 功能分支
+- `bugfix/*` - 修复分支
+
+### 10.2 提交规范
+```
+feat: 新功能
+fix: 修复bug
+refactor: 重构
+docs: 文档更新
+style: 代码格式调整
+test: 测试相关
+chore: 构建/工具链相关
+```
+
+---
+
+## 11. 常见陷阱清单
+
+### 11.1 Lombok 相关
+- ✓ 每个子模块必须显式配置 maven-compiler-plugin
+- ✓ 继承 ServiceImpl 的类不能使用 @Slf4j
+- ✓ 确保所有 import lombok.* 语句存在
+
+### 11.2 Shiro 相关
+- ✓ 使用 Shiro 2.0.0(支持 jakarta.servlet)
+- ✓ 保留 spring-security-crypto(用于 BCrypt)
+- ✓ 登录接口路径配置为匿名访问
+
+### 11.3 Maven 相关
+- ✓ 避免在 pom.xml 中重复声明依赖
+- ✓ 父 pom 中未创建的子模块需注释掉
+- ✓ 使用 `-U` 参数强制更新依赖
+
+### 11.4 数据库相关
+- ✓ 初始化 SQL 主键必须与字段定义一致
+- ✓ tenant_id 必须有默认值保障机制
+- ✓ 软删除字段 deleted 默认为 0
+
+---
+
+## 12. 重新生成代码的策略
+
+### 12.1 代码生成工具选择
+**选项 A**: MyBatis-Plus 代码生成器
+- 优点: 自动生成 Entity、Mapper、Service、Controller
+- 缺点: 需要手动调整生成的代码
+
+**选项 B**: 手动编写
+- 优点: 完全可控,符合规范
+- 缺点: 工作量大
+
+**选项 C**: 模板化生成
+- 优点: 可重复使用,质量稳定
+- 缺点: 需要先建立模板
+
+### 12.2 推荐策略
+1. **第一步**: 手动编写核心基础类(BaseEntity、Result、自定义异常)
+2. **第二步**: 使用 MyBatis-Plus 生成器生成基础 CRUD
+3. **第三步**: 手动完善业务逻辑和复杂查询
+4. **第四步**: 编写单元测试验证
+
+### 12.3 生成顺序
+```
+1. fund-common (基础类)
+ ├── BaseEntity
+ ├── Result
+ └── 工具类
+
+2. fund-sys (系统模块)
+ ├── 实体类 (Entity)
+ ├── Mapper 接口
+ ├── Service 实现
+ ├── Controller
+ └── Shiro 配置
+
+3. fund-gateway (网关)
+ └── 路由配置
+
+4. fund-cust (客户模块)
+5. fund-proj (项目模块)
+```
+
+---
+
+## 13. 架构约束
+
+### 13.1 必须遵守的架构要求
+1. ✅ 使用 Shiro 作为认证框架(架构文档要求)
+2. ✅ 实现多租户数据隔离
+3. ✅ Header 透传(X-Uid, X-Uname)
+4. ✅ 全链路日志追踪
+
+### 13.2 性能要求
+- 接口响应时间 < 500ms
+- 数据库查询使用索引
+- 使用 Redis 缓存热点数据
+
+---
+
+## 14. 下一步行动
+
+### 14.1 准备工作
+- [ ] 备份当前代码
+- [ ] 清理所有 target 目录
+- [ ] 确认数据库表结构
+- [ ] 准备初始化 SQL 脚本
+
+### 14.2 代码生成
+- [ ] 生成 fund-common 基础类
+- [ ] 生成 fund-sys 实体类和 Mapper
+- [ ] 实现 Shiro 认证框架
+- [ ] 编写核心业务逻辑
+
+### 14.3 验证
+- [ ] 编译通过(mvn clean compile)
+- [ ] 启动成功
+- [ ] 登录功能正常
+- [ ] 基础 CRUD 接口正常
+
+---
+
+**注意**: 本规则清单将持续更新,每次遇到问题都会添加到"常见陷阱清单"中。
+