feat: 初始化居家上门服务系统完整项目代码
- Spring Boot 后端服务 (hss-home-service) - delivery-miniapp 配送小程序 - website 官网 (Nuxt) - docs 架构设计文档 - Docker 容器化部署配置 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
82
hss-home-service/website/tests/api.spec.ts
Normal file
82
hss-home-service/website/tests/api.spec.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
const API = 'http://172.31.12.249:18080/api/hss'
|
||||
const H = { 'Content-Type': 'application/json', 'X-Tenant-Id': '1', 'X-Org-Id': '1', 'X-User-Role': 'ADMIN' }
|
||||
|
||||
test.describe('后端 API 连通性', () => {
|
||||
test('OpenAPI 文档可达', async ({ request }) => {
|
||||
const res = await request.get('http://172.31.12.249:18080/api-docs')
|
||||
expect(res.status()).toBe(200)
|
||||
const json = await res.json()
|
||||
expect(json.openapi).toBeDefined()
|
||||
expect(Object.keys(json.paths).length).toBeGreaterThan(80)
|
||||
})
|
||||
|
||||
test('管理端仪表盘返回数据', async ({ request }) => {
|
||||
const res = await request.get(`${API}/admin/dashboard`, { headers: H })
|
||||
expect(res.status()).toBe(200)
|
||||
const json = await res.json()
|
||||
expect(json.code).toBe(200)
|
||||
expect(json.data).toBeDefined()
|
||||
})
|
||||
|
||||
test('分析汇总返回数据', async ({ request }) => {
|
||||
const res = await request.get(`${API}/analytics/summary`, { headers: H })
|
||||
expect(res.status()).toBe(200)
|
||||
})
|
||||
|
||||
test('应用列表返回数据', async ({ request }) => {
|
||||
const res = await request.get(`${API}/applications?page=1&size=5`, { headers: H })
|
||||
expect(res.status()).toBe(200)
|
||||
})
|
||||
|
||||
test('Leads 提交成功', async ({ request }) => {
|
||||
const key = 'pw-test-' + Date.now()
|
||||
const res = await request.post(`${API}/leads`, {
|
||||
headers: { ...H, 'Idempotency-Key': key },
|
||||
data: { type: 'demo', name: 'PW测试', orgName: '测试机构', phone: '13800000000', source: 'playwright' }
|
||||
})
|
||||
expect(res.status()).toBe(200)
|
||||
const json = await res.json()
|
||||
expect(json.code).toBe(200)
|
||||
})
|
||||
|
||||
test('Leads 幂等去重', async ({ request }) => {
|
||||
const key = 'pw-idem-' + Date.now()
|
||||
const data = { type: 'demo', name: '幂等测试', orgName: '幂等机构', phone: '13900000000', source: 'playwright' }
|
||||
const r1 = await request.post(`${API}/leads`, { headers: { ...H, 'Idempotency-Key': key }, data })
|
||||
const r2 = await request.post(`${API}/leads`, { headers: { ...H, 'Idempotency-Key': key }, data })
|
||||
expect(r1.status()).toBe(200)
|
||||
const j1 = await r1.json(); const j2 = await r2.json()
|
||||
expect(j1.data?.id || j1.data?.status).toBe(j2.data?.id || j2.data?.status)
|
||||
})
|
||||
|
||||
test('非法状态转换被拦截', async ({ request }) => {
|
||||
const res = await request.post(`${API}/applications/99999/accept`, { headers: H })
|
||||
expect(res.status()).toBe(422)
|
||||
})
|
||||
|
||||
test('主数据接口可达', async ({ request }) => {
|
||||
for (const ep of ['/master/service-items', '/master/price-rules', '/master/regions', '/master/staff']) {
|
||||
const res = await request.get(`${API}${ep}`, { headers: H })
|
||||
expect(res.status()).toBe(200)
|
||||
}
|
||||
})
|
||||
|
||||
test('运力与绩效接口可达', async ({ request }) => {
|
||||
for (const ep of ['/capacity/dashboard', '/analytics/quality', '/performance/ranking']) {
|
||||
const res = await request.get(`${API}${ep}`, { headers: H })
|
||||
expect(res.status(), `${ep} should return 200`).toBe(200)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('官网表单提交通过 Nginx 代理', () => {
|
||||
test('预约演示表单 mock 提交', async ({ request }) => {
|
||||
const res = await request.post('http://172.31.12.249:3080/api/hss/leads', {
|
||||
headers: { ...H, 'Idempotency-Key': 'pw-nginx-' + Date.now() },
|
||||
data: { type: 'demo', name: 'Nginx代理测试', orgName: '测试机构', phone: '13800000001', source: 'website' }
|
||||
})
|
||||
expect(res.status()).toBe(200)
|
||||
})
|
||||
})
|
||||
51
hss-home-service/website/tests/homepage.spec.ts
Normal file
51
hss-home-service/website/tests/homepage.spec.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('首页模块完整性', () => {
|
||||
test.beforeEach(async ({ page }) => { await page.goto('/') })
|
||||
|
||||
test('Hero 首屏存在', async ({ page }) => {
|
||||
await expect(page.locator('h1')).toBeVisible()
|
||||
await expect(page.getByText('预约演示').first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('行业痛点模块存在', async ({ page }) => {
|
||||
await expect(page.getByText('居家上门服务,为什么这么难管?')).toBeVisible()
|
||||
await expect(page.getByText('申请受理难')).toBeVisible()
|
||||
await expect(page.getByText('派单调度难')).toBeVisible()
|
||||
await expect(page.getByText('过程监管难')).toBeVisible()
|
||||
})
|
||||
|
||||
test('服务闭环流程模块存在', async ({ page }) => {
|
||||
await page.getByText('完整服务闭环').scrollIntoViewIfNeeded()
|
||||
await page.waitForTimeout(500)
|
||||
await expect(page.getByText('完整服务闭环')).toBeVisible()
|
||||
})
|
||||
|
||||
test('核心能力模块存在', async ({ page }) => {
|
||||
await expect(page.getByText('八大核心能力')).toBeVisible()
|
||||
})
|
||||
|
||||
test('应用场景模块存在', async ({ page }) => {
|
||||
await expect(page.getByText('覆盖五大应用场景')).toBeVisible()
|
||||
await expect(page.getByText('政府监管')).toBeVisible()
|
||||
await expect(page.getByText('医院延续护理')).toBeVisible()
|
||||
})
|
||||
|
||||
test('数据看板模块存在', async ({ page }) => {
|
||||
await expect(page.getByText('实时监管,数据驱动')).toBeVisible()
|
||||
})
|
||||
|
||||
test('安全合规模块存在', async ({ page }) => {
|
||||
await expect(page.getByText('医疗级安全合规')).toBeVisible()
|
||||
})
|
||||
|
||||
test('CTA 区块存在', async ({ page }) => {
|
||||
await expect(page.getByText('准备好提升居家服务管理效率了吗?')).toBeVisible()
|
||||
})
|
||||
|
||||
test('SVG 图标渲染正常(非 emoji)', async ({ page }) => {
|
||||
const svgs = page.locator('svg')
|
||||
const count = await svgs.count()
|
||||
expect(count).toBeGreaterThan(5)
|
||||
})
|
||||
})
|
||||
52
hss-home-service/website/tests/pages.spec.ts
Normal file
52
hss-home-service/website/tests/pages.spec.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
const pages = [
|
||||
{ path: '/', title: '首页', seoTitle: '首页 | 智慧医养居家上门服务平台' },
|
||||
{ path: '/solution', title: '解决方案', seoTitle: '解决方案 | 智慧医养居家上门服务平台' },
|
||||
{ path: '/capabilities', title: '核心能力', seoTitle: '核心能力 | 智慧医养居家上门服务平台' },
|
||||
{ path: '/scenarios', title: '应用场景', seoTitle: '应用场景 | 智慧医养居家上门服务平台' },
|
||||
{ path: '/service-loop', title: '服务闭环', seoTitle: '服务闭环 | 智慧医养居家上门服务平台' },
|
||||
{ path: '/security', title: '安全合规', seoTitle: '安全合规 | 智慧医养居家上门服务平台' },
|
||||
{ path: '/resources', title: '资源中心', seoTitle: '资源中心 | 智慧医养居家上门服务平台' },
|
||||
{ path: '/about', title: '关于我们', seoTitle: '关于我们 | 智慧医养居家上门服务平台' },
|
||||
{ path: '/contact', title: '联系我们', seoTitle: '联系我们 | 智慧医养居家上门服务平台' },
|
||||
{ path: '/demo', title: '平台演示', seoTitle: '平台演示 | 智慧医养居家上门服务平台' },
|
||||
]
|
||||
|
||||
for (const p of pages) {
|
||||
test.describe(p.title, () => {
|
||||
test('页面可达 HTTP 200', async ({ page }) => {
|
||||
const res = await page.goto(p.path)
|
||||
expect(res?.status()).toBe(200)
|
||||
})
|
||||
|
||||
test('SEO title 正确', async ({ page }) => {
|
||||
await page.goto(p.path)
|
||||
await expect(page).toHaveTitle(p.seoTitle)
|
||||
})
|
||||
|
||||
test('导航栏存在', async ({ page }) => {
|
||||
await page.goto(p.path)
|
||||
await expect(page.locator('header')).toBeVisible()
|
||||
})
|
||||
|
||||
test('页脚存在', async ({ page }) => {
|
||||
await page.goto(p.path)
|
||||
await expect(page.locator('footer')).toBeVisible()
|
||||
})
|
||||
|
||||
test('无控制台错误', async ({ page }) => {
|
||||
const errors: string[] = []
|
||||
page.on('pageerror', e => errors.push(e.message))
|
||||
await page.goto(p.path)
|
||||
await page.waitForTimeout(1000)
|
||||
expect(errors.filter(e => !e.includes('hydrat'))).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('移动端响应式正常', async ({ page: mobile }) => {
|
||||
const res = await mobile.goto(p.path)
|
||||
expect(res?.status()).toBe(200)
|
||||
await expect(mobile.locator('header')).toBeVisible()
|
||||
})
|
||||
})
|
||||
}
|
||||
97
hss-home-service/website/tests/platform.spec.ts
Normal file
97
hss-home-service/website/tests/platform.spec.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
async function loginAsAdmin(page: any) {
|
||||
// Inject auth directly via localStorage to bypass login flow
|
||||
await page.goto('/')
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem('hss_platform_user', JSON.stringify({
|
||||
userId: '1', userName: '系统管理员', userRole: 'ADMIN', tenantId: '1', orgId: '1'
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
test.describe('平台登录与角色切换', () => {
|
||||
test('登录页可达', async ({ page }) => {
|
||||
await page.goto('/platform/login')
|
||||
await expect(page.getByText('选择登录角色')).toBeVisible()
|
||||
await expect(page.getByText('系统管理员')).toBeVisible()
|
||||
})
|
||||
|
||||
test('选择管理员登录并进入工作台', async ({ page }) => {
|
||||
await page.goto('/platform/login')
|
||||
await page.getByText('系统管理员').click()
|
||||
await page.getByText('进入平台').click()
|
||||
await page.waitForURL(/\/platform/)
|
||||
await page.waitForTimeout(1500)
|
||||
await expect(page.getByText('工作台')).toBeVisible()
|
||||
await expect(page.getByText('今日工单')).toBeVisible()
|
||||
})
|
||||
|
||||
test('工作台显示统计数字', async ({ page }) => {
|
||||
await page.goto('/platform/login')
|
||||
await page.getByText('系统管理员').click()
|
||||
await page.getByText('进入平台').click()
|
||||
await page.waitForURL(/\/platform/)
|
||||
await page.waitForTimeout(2000)
|
||||
const statCards = page.locator('.bg-white.rounded-xl.p-4')
|
||||
await expect(statCards.first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('未登录访问平台被重定向', async ({ page }) => {
|
||||
await page.goto('/platform')
|
||||
await page.waitForTimeout(1000)
|
||||
expect(page.url()).toContain('/platform/login')
|
||||
})
|
||||
|
||||
test('退出登录返回登录页', async ({ page }) => {
|
||||
await page.goto('/platform/login')
|
||||
await page.getByText('系统管理员').click()
|
||||
await page.getByText('进入平台').click()
|
||||
await page.waitForURL(/\/platform/)
|
||||
await page.getByText('退出登录').first().click()
|
||||
await page.waitForTimeout(500)
|
||||
expect(page.url()).toContain('/platform/login')
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('服务申请管理', () => {
|
||||
test('申请管理页可达并显示列表', async ({ page }) => {
|
||||
await loginAsAdmin(page)
|
||||
await page.goto('/platform/applications')
|
||||
await page.waitForTimeout(3000)
|
||||
await expect(page.getByText('服务申请管理')).toBeVisible()
|
||||
})
|
||||
|
||||
test('打开新建申请表单', async ({ page }) => {
|
||||
await loginAsAdmin(page)
|
||||
await page.goto('/platform/applications')
|
||||
await page.waitForTimeout(2000)
|
||||
await page.getByText('新建申请').click()
|
||||
await expect(page.getByText('新建服务申请')).toBeVisible()
|
||||
await expect(page.getByPlaceholder('如 2001')).toBeVisible()
|
||||
})
|
||||
|
||||
test('创建新申请', async ({ page }) => {
|
||||
await loginAsAdmin(page)
|
||||
await page.goto('/platform/applications')
|
||||
await page.waitForTimeout(2000)
|
||||
await page.getByText('新建申请').click()
|
||||
await page.getByPlaceholder('如 2001').fill('2001')
|
||||
await page.getByPlaceholder('姓名').fill('PW测试')
|
||||
await page.getByPlaceholder('手机号').fill('13800000000')
|
||||
await page.getByPlaceholder('详细地址').fill('梅江区')
|
||||
await page.getByText('提交申请').click()
|
||||
await page.waitForTimeout(3000)
|
||||
// New application should appear in the table
|
||||
await expect(page.getByText('服务申请管理')).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('工单管理', () => {
|
||||
test('工单管理页可达', async ({ page }) => {
|
||||
await loginAsAdmin(page)
|
||||
await page.goto('/platform/work-orders')
|
||||
await page.waitForTimeout(3000)
|
||||
await expect(page.getByText('工单管理')).toBeVisible()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user