chore: complete infrastructure config, contracts, reference images and ui redesign artifacts
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 2.1 MiB |
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
*.log
|
||||||
|
.env*
|
||||||
|
pnpm-lock.yaml
|
||||||
|
After Width: | Height: | Size: 279 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 931 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 126 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
target
|
||||||
|
.m2*
|
||||||
|
local-*
|
||||||
|
build-temp
|
||||||
|
*.log
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
# 丹阳公益婚恋服务平台 - Backend
|
# 丹阳公益婚恋服务平台 - 本地完整环境
|
||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
@@ -89,18 +88,29 @@ services:
|
|||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
|
admin-web:
|
||||||
|
build:
|
||||||
|
context: ../admin-web
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: danyang-match-admin
|
||||||
|
depends_on:
|
||||||
|
backend:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
nginx:
|
nginx:
|
||||||
image: nginx:alpine
|
image: nginx:alpine
|
||||||
container_name: danyang-match-nginx
|
container_name: danyang-match-nginx
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
backend:
|
||||||
|
condition: service_healthy
|
||||||
|
admin-web:
|
||||||
|
condition: service_started
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "6688:80"
|
||||||
- "443:443"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost/health"]
|
test: ["CMD-SHELL", "wget -qO /dev/null http://localhost/nginx-health || exit 1"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ http {
|
|||||||
server backend:8080;
|
server backend:8080;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upstream admin_web {
|
||||||
|
server admin-web:80;
|
||||||
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
@@ -59,6 +63,8 @@ http {
|
|||||||
proxy_connect_timeout 30s;
|
proxy_connect_timeout 30s;
|
||||||
proxy_send_timeout 60s;
|
proxy_send_timeout 60s;
|
||||||
proxy_read_timeout 60s;
|
proxy_read_timeout 60s;
|
||||||
|
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
||||||
|
add_header Pragma "no-cache";
|
||||||
}
|
}
|
||||||
|
|
||||||
# WebSocket proxy
|
# WebSocket proxy
|
||||||
@@ -84,17 +90,13 @@ http {
|
|||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Admin web static files
|
# Admin web
|
||||||
location /admin/ {
|
location / {
|
||||||
root /usr/share/nginx/html;
|
proxy_pass http://admin_web;
|
||||||
index index.html;
|
proxy_set_header Host $host;
|
||||||
try_files $uri $uri/ /admin/index.html;
|
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;
|
||||||
# Don't cache API responses
|
|
||||||
location /api/ {
|
|
||||||
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
|
|
||||||
add_header Pragma "no-cache";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,38 @@ assert.match(premaritalTemplate, /service-topics/, '婚前辅导页缺少四项
|
|||||||
assert.match(premaritalTemplate, /预约辅导/, '婚前辅导页缺少预约按钮')
|
assert.match(premaritalTemplate, /预约辅导/, '婚前辅导页缺少预约按钮')
|
||||||
assert.match(premaritalTemplate, /服务状态/, '婚前辅导页缺少服务状态')
|
assert.match(premaritalTemplate, /服务状态/, '婚前辅导页缺少服务状态')
|
||||||
|
|
||||||
|
const contentTemplate = read('miniapp/pages/content/content.wxml')
|
||||||
|
const contentLogic = read('miniapp/pages/content/content.js')
|
||||||
|
assert.match(contentTemplate, /content-categories/, '婚恋课堂缺少分类导航')
|
||||||
|
assert.match(contentTemplate, /content-list/, '婚恋课堂缺少内容列表')
|
||||||
|
assert.match(contentTemplate, /viewCount/, '婚恋课堂缺少真实浏览量')
|
||||||
|
assert.match(contentTemplate, /publishAtText/, '婚恋课堂缺少发布日期')
|
||||||
|
assert.match(contentLogic, /\/content\/categories/, '婚恋课堂分类必须来自正式接口')
|
||||||
|
assert.match(contentLogic, /\/content\/articles/, '婚恋课堂文章必须来自正式接口')
|
||||||
|
assert.doesNotMatch(contentLogic, /id:\s*['"]1['"]/, '婚恋课堂不得保留演示文章')
|
||||||
|
|
||||||
|
const antiFraudTemplate = read('miniapp/pages/anti-fraud/anti-fraud.wxml')
|
||||||
|
assert.match(antiFraudTemplate, /ui-service-hero/, '婚恋反诈必须使用独立主视觉')
|
||||||
|
assert.match(antiFraudTemplate, /risk-grid/, '婚恋反诈缺少四类风险')
|
||||||
|
assert.match(antiFraudTemplate, /举报可疑行为/, '婚恋反诈缺少举报入口')
|
||||||
|
assert.match(antiFraudTemplate, /安全求助/, '婚恋反诈缺少求助入口')
|
||||||
|
|
||||||
|
const stationTemplate = read('miniapp/pages/service-station/service-station.wxml')
|
||||||
|
const stationLogic = read('miniapp/pages/service-station/service-station.js')
|
||||||
|
for (const label of ['地址', '联系电话', '开放时间', '服务项目', '地图定位', '预约到站服务']) {
|
||||||
|
assert.ok(stationTemplate.includes(label), `服务站缺少 ${label}`)
|
||||||
|
}
|
||||||
|
assert.match(stationLogic, /wx\.makePhoneCall/, '服务站电话必须使用微信原生拨号')
|
||||||
|
assert.match(stationLogic, /wx\.openLocation/, '服务站定位必须使用微信原生地图')
|
||||||
|
assert.match(stationLogic, /api\.get\(['"]\/home['"]\)/, '服务站必须读取真实首页服务站数据')
|
||||||
|
assert.doesNotMatch(stationLogic, /00000000/, '服务站不得保留演示电话')
|
||||||
|
|
||||||
|
for (const envFile of ['miniapp/env.js', 'miniapp/env.ts']) {
|
||||||
|
const envSource = read(envFile)
|
||||||
|
assert.match(envSource, /getAccountInfoSync/, `${envFile} 必须通过微信运行版本识别开发环境`)
|
||||||
|
assert.match(envSource, /envVersion\s*===\s*['"]develop['"]/, `${envFile} 必须将 develop 环境指向本地 API`)
|
||||||
|
}
|
||||||
|
|
||||||
const theme = read('miniapp/app.wxss')
|
const theme = read('miniapp/app.wxss')
|
||||||
for (const token of [
|
for (const token of [
|
||||||
'--mini-night-start',
|
'--mini-night-start',
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
|
||||||
|
const read = (path) => fs.readFileSync(path, 'utf8')
|
||||||
|
const engine = read('backend/src/main/java/com/danyang/match/platform/match/service/RecommendationEngineService.java')
|
||||||
|
const legacyEligibility = read('backend/src/main/java/com/danyang/match/platform/match/service/MatchEligibilityService.java')
|
||||||
|
const home = read('backend/src/main/java/com/danyang/match/platform/home/service/HomeApplicationService.java')
|
||||||
|
const card = read('miniapp/components/recommend-card/index.wxml')
|
||||||
|
|
||||||
|
assert.match(engine, /isParticipantEligible\(userId\)/, '推荐入口必须校验当前用户资格')
|
||||||
|
assert.match(engine, /findByRelation\(targetUserId, userId, Constants\.RELATION_TYPE_BLOCKED\)/, '必须检查反向 Block')
|
||||||
|
assert.match(engine, /localEligibilityMapper\.findByUserId/, '正式推荐引擎必须接入本地资格 Provider')
|
||||||
|
assert.doesNotMatch(legacyEligibility, /PROFILE_STATUS_PENDING_REVIEW\.equals\(el\.getVerificationStatus\(\)\)/, 'pending_review 不得视为本地资格通过')
|
||||||
|
assert.match(home, /recommendationEngineService\.getDailyRecommendations\(userId\)/, '首页必须调用正式推荐引擎')
|
||||||
|
assert.doesNotMatch(card, /score|percent|匹配率|%/, '用户端不得显示数字匹配率')
|
||||||
|
|
||||||
|
console.log('Phase 06 match eligibility contracts passed')
|
||||||