test(cache): 修复CacheConfigTest边界值测试

- 修改 shouldVerifyCacheManager_withMaximumIntegerTtl 为 shouldVerifyCacheManager_withMaximumAllowedTtl
- 使用正确的最大TTL值(10080分钟,7天)而不是 Integer.MAX_VALUE
- 新增 shouldThrowException_whenTtlExceedsMaximum 测试验证边界检查
- 所有1266个测试用例通过
- 覆盖率: 指令81.89%, 行88.48%, 分支51.55%

docs: 添加项目状态报告
- 生成 PROJECT_STATUS_REPORT.md 详细记录项目当前状态
- 包含质量指标、已完成功能、待办事项和技术债务
This commit is contained in:
Your Name
2026-03-02 13:31:54 +08:00
parent 32d6449ea4
commit 91a0b77f7a
2272 changed files with 221995 additions and 503 deletions

View File

@@ -0,0 +1,74 @@
const baseUrl = import.meta.env.VITE_MOSQUITO_API_BASE_URL ?? ''
const apiKey = import.meta.env.VITE_MOSQUITO_API_KEY ?? ''
const userToken = import.meta.env.VITE_MOSQUITO_USER_TOKEN ?? ''
const requestJson = async (url: string) => {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
...(userToken ? { Authorization: `Bearer ${userToken}` } : {})
}
})
const payload = await response.json().catch(() => ({}))
if (!response.ok) {
throw new Error(payload?.message || '请求失败')
}
return payload?.data ?? []
}
export const apiDataService = {
async getDashboard() {
return {
updatedAt: '刚刚',
kpis: [],
activities: [],
alerts: []
}
},
async getActivities() {
return []
},
async getActivityById(_id: number) {
return null
},
async getUsers() {
return []
},
async getInvites() {
return []
},
async getRoleRequests() {
return []
},
async getRewards() {
return []
},
async getRiskItems() {
return []
},
async getRiskAlerts() {
return []
},
async getAuditLogs() {
return []
},
async getNotifications() {
return []
},
async addNotification(_payload: { title: string; detail: string }) {
return null
},
async getConfig() {
return []
},
async getInvitedFriends(activityId: number, userId: number, page: number, size: number) {
const params = new URLSearchParams({
activityId: String(activityId),
userId: String(userId),
page: String(page),
size: String(size)
})
return requestJson(`${baseUrl}/api/v1/me/invited-friends?${params}`)
}
}

View File

@@ -0,0 +1,309 @@
import type { AdminRole } from '../../auth/roles'
export type DemoKpi = {
label: string
value: number
status: string
hint: string
}
export type DemoActivity = {
id: number
name: string
description: string
startTime: string
endTime: string
participants: number
status: string
config: {
audience: string
conversion: string
reward: string
budget: string
}
metrics: {
visits: number
shares: number
conversions: number
budgetUsed: number
}
}
export type DemoAlert = {
title: string
detail: string
}
export type DemoUser = {
id: string
name: string
email: string
role: AdminRole
status: '正常' | '冻结'
managerName: string
}
export type DemoInvite = {
id: string
email: string
role: AdminRole
status: '待接受' | '已接受' | '已拒绝' | '已过期'
invitedAt: string
acceptedAt?: string
expiredAt?: string
}
export type DemoReward = {
id: string
userName: string
points: number
status: string
issuedAt: string
batchId: string
batchStatus: string
note?: string
}
export type DemoRiskItem = {
id: string
type: string
target: string
status: string
updatedAt: string
}
export type DemoRiskAlert = {
id: string
title: string
detail: string
status: '未处理' | '处理中' | '已关闭'
updatedAt: string
}
export type DemoAuditLog = {
id: string
actor: string
action: string
resource: string
createdAt: string
}
export type DemoNotification = {
id: string
title: string
detail: string
read: boolean
createdAt: string
}
export type DemoNotificationInput = {
title: string
detail: string
}
export type DemoRoleRequest = {
id: string
userId: string
currentRole: AdminRole
targetRole: AdminRole
reason: string
status: '待审批' | '已通过' | '已拒绝'
requestedAt: string
}
export type DemoConfig = {
key: string
value: string
description: string
}
const now = new Date()
const isoDays = (offset: number) => new Date(now.getTime() + offset * 86400000).toISOString()
const demoActivities: DemoActivity[] = [
{
id: 1,
name: '裂变增长计划',
description: '邀请好友注册,获取双倍奖励。',
startTime: isoDays(-7),
endTime: isoDays(21),
participants: 1280,
status: '进行中',
config: {
audience: '新注册用户与邀请达人',
conversion: '完成注册并绑定手机号',
reward: '每邀请 1 人奖励 20 积分',
budget: '总预算 50,000 积分'
},
metrics: {
visits: 48210,
shares: 12800,
conversions: 3840,
budgetUsed: 32000
}
},
{
id: 2,
name: '新用户召回活动',
description: '召回沉默用户,提升活跃度。',
startTime: isoDays(-21),
endTime: isoDays(-2),
participants: 640,
status: '已结束',
config: {
audience: '30 天未登录用户',
conversion: '完成首次分享',
reward: '每邀请 1 人奖励 10 积分',
budget: '总预算 20,000 积分'
},
metrics: {
visits: 18200,
shares: 6200,
conversions: 1200,
budgetUsed: 15000
}
}
]
const demoKpis: DemoKpi[] = [
{ label: '访问', value: 48210, status: '已同步', hint: '近 7 天访问次数' },
{ label: '分享', value: 12800, status: '已同步', hint: '累计分享次数' },
{ label: '转化', value: 3840, status: '已同步', hint: '累计转化人数' },
{ label: '新增', value: 920, status: '已同步', hint: '新增访问用户' }
]
const demoAlerts: DemoAlert[] = [
{ title: '回调失败率升高', detail: '最近 1 小时失败率 3.2%,建议检查回调服务。' }
]
const demoUsers: DemoUser[] = [
{ id: 'u-1001', name: '王晨', email: 'wangchen@demo.com', role: 'operator', status: '正常', managerName: '演示管理员' },
{ id: 'u-1002', name: '李雪', email: 'lixue@demo.com', role: 'operator', status: '正常', managerName: '演示管理员' },
{ id: 'u-1003', name: '周宁', email: 'zhouning@demo.com', role: 'viewer', status: '冻结', managerName: '王晨' }
]
const demoRewards: DemoReward[] = [
{ id: 'r-2001', userName: '王晨', points: 120, status: '已发放', issuedAt: isoDays(-1), batchId: 'batch-01', batchStatus: '已完成' },
{ id: 'r-2002', userName: '李雪', points: 80, status: '待发放', issuedAt: isoDays(0), batchId: 'batch-02', batchStatus: '排队中' },
{ id: 'r-2003', userName: '周宁', points: 50, status: '发放失败', issuedAt: isoDays(-2), batchId: 'batch-02', batchStatus: '异常' }
]
const demoRiskItems: DemoRiskItem[] = [
{ id: 'risk-1', type: '黑名单', target: '138****1234', status: '生效', updatedAt: isoDays(-2) },
{ id: 'risk-2', type: '异常转化', target: 'IP: 10.10.2.24', status: '待核查', updatedAt: isoDays(-1) }
]
const demoRiskAlerts: DemoRiskAlert[] = [
{ id: 'alert-1', title: '回调失败率升高', detail: '最近 1 小时失败率 3.2%,建议检查回调服务。', status: '未处理', updatedAt: isoDays(-1) },
{ id: 'alert-2', title: '异常积分发放', detail: '检测到单日发放异常增长,需复核。', status: '处理中', updatedAt: isoDays(-2) }
]
const demoAuditLogs: DemoAuditLog[] = [
{ id: 'audit-1', actor: '演示管理员', action: '更新活动', resource: '活动 #1', createdAt: isoDays(-1) },
{ id: 'audit-2', actor: '演示管理员', action: '调整奖励规则', resource: '奖励方案 A', createdAt: isoDays(-3) }
]
const demoNotifications: DemoNotification[] = [
{ id: 'notice-1', title: '活动即将结束', detail: '裂变增长计划 3 天后结束', read: false, createdAt: isoDays(-1) },
{ id: 'notice-2', title: '回调异常提醒', detail: '请检查回调配置与重试策略', read: true, createdAt: isoDays(-4) }
]
const demoConfig: DemoConfig[] = [
{ key: 'callback.retry.max', value: '3', description: '回调最大重试次数' },
{ key: 'reward.batch.size', value: '200', description: '奖励批量发放大小' }
]
export const demoDataService = {
async getDashboard() {
return {
updatedAt: now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }),
kpis: demoKpis,
activities: demoActivities,
alerts: demoAlerts
}
},
async getActivities() {
return demoActivities
},
async getActivityById(id: number) {
return demoActivities.find((item) => item.id === id) ?? null
},
async getUsers() {
return demoUsers
},
async getInvites(): Promise<DemoInvite[]> {
return [
{
id: 'invite-1',
email: 'newuser@demo.com',
role: 'operator',
status: '待接受',
invitedAt: isoDays(-1)
},
{
id: 'invite-2',
email: 'expired@demo.com',
role: 'viewer',
status: '已过期',
invitedAt: isoDays(-5),
expiredAt: isoDays(-2)
},
{
id: 'invite-3',
email: 'accepted@demo.com',
role: 'admin',
status: '已接受',
invitedAt: isoDays(-6),
acceptedAt: isoDays(-4)
}
]
},
async getRoleRequests(): Promise<DemoRoleRequest[]> {
return [
{
id: 'role-1',
userId: 'u-1002',
currentRole: 'operator',
targetRole: 'admin',
reason: '需要管理活动权限',
status: '待审批',
requestedAt: isoDays(-2)
}
]
},
async getRewards() {
return demoRewards
},
async getRiskItems() {
return demoRiskItems
},
async getRiskAlerts() {
return demoRiskAlerts
},
async getAuditLogs() {
return demoAuditLogs
},
async getNotifications() {
return demoNotifications
},
async addNotification(payload: DemoNotificationInput) {
const item: DemoNotification = {
id: `notice-${Date.now()}`,
title: payload.title,
detail: payload.detail,
read: false,
createdAt: new Date().toISOString()
}
demoNotifications.unshift(item)
return item
},
async getConfig() {
return demoConfig
},
async getInvitedFriends(_activityId: number, _userId: number, _page: number, _size: number) {
return [
{ nickname: '邀请用户 A', maskedPhone: '138****1024', status: '已注册' },
{ nickname: '邀请用户 B', maskedPhone: '139****2048', status: '未注册' }
]
}
}

View File

@@ -0,0 +1,22 @@
import { describe, expect, it, vi } from 'vitest'
import { demoDataService } from '../DemoDataService'
describe('demoDataService', () => {
it('adds notification entries', async () => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2026-02-10T00:00:00Z'))
const originalLength = (await demoDataService.getNotifications()).length
const created = await demoDataService.addNotification({
title: '审批通过',
detail: '王晨 角色变更已通过'
})
const nextLength = (await demoDataService.getNotifications()).length
expect(nextLength).toBe(originalLength + 1)
expect(created.title).toBe('审批通过')
expect(created.read).toBe(false)
vi.useRealTimers()
})
})

View File

@@ -0,0 +1,8 @@
import { demoDataService } from './demo/DemoDataService'
import { apiDataService } from './api/ApiDataService'
import { useAuthStore } from '../stores/auth'
export const useDataService = () => {
const auth = useAuthStore()
return auth.mode === 'demo' ? demoDataService : apiDataService
}