init: 2026Technology-Competition initial commit
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
-- generated by seed-7d.mjs
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '6 days' + interval '00 hours 00 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '6 days' + interval '00 hours 00 minutes 00 seconds' WHERE id = 1;
|
||||
UPDATE issues SET closed_at = created_at + interval '2 days' WHERE id = 1;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '6 days' + interval '10 hours 20 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '6 days' + interval '10 hours 20 minutes 00 seconds' WHERE id = 2;
|
||||
UPDATE issues SET closed_at = created_at + interval '3 days' WHERE id = 2;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '6 days' + interval '00 hours 00 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '6 days' + interval '00 hours 00 minutes 00 seconds' WHERE id = 3;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '6 days' + interval '14 hours 05 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '6 days' + interval '14 hours 05 minutes 00 seconds' WHERE id = 4;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '6 days' + interval '16 hours 40 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '6 days' + interval '16 hours 40 minutes 00 seconds' WHERE id = 5;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '5 days' + interval '09 hours 30 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '5 days' + interval '09 hours 30 minutes 00 seconds' WHERE id = 6;
|
||||
UPDATE issues SET closed_at = created_at + interval '2 days' WHERE id = 6;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '5 days' + interval '11 hours 10 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '5 days' + interval '11 hours 10 minutes 00 seconds' WHERE id = 7;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '5 days' + interval '13 hours 45 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '5 days' + interval '13 hours 45 minutes 00 seconds' WHERE id = 8;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '5 days' + interval '15 hours 20 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '5 days' + interval '15 hours 20 minutes 00 seconds' WHERE id = 9;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '5 days' + interval '17 hours 55 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '5 days' + interval '17 hours 55 minutes 00 seconds' WHERE id = 10;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '4 days' + interval '09 hours 00 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '4 days' + interval '09 hours 00 minutes 00 seconds' WHERE id = 11;
|
||||
UPDATE issues SET closed_at = created_at + interval '2 days' WHERE id = 11;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '4 days' + interval '10 hours 15 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '4 days' + interval '10 hours 15 minutes 00 seconds' WHERE id = 12;
|
||||
UPDATE issues SET closed_at = created_at + interval '3 days' WHERE id = 12;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '4 days' + interval '12 hours 30 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '4 days' + interval '12 hours 30 minutes 00 seconds' WHERE id = 13;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '4 days' + interval '14 hours 50 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '4 days' + interval '14 hours 50 minutes 00 seconds' WHERE id = 14;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '4 days' + interval '16 hours 25 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '4 days' + interval '16 hours 25 minutes 00 seconds' WHERE id = 15;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '3 days' + interval '09 hours 10 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '3 days' + interval '09 hours 10 minutes 00 seconds' WHERE id = 16;
|
||||
UPDATE issues SET closed_at = created_at + interval '2 days' WHERE id = 16;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '3 days' + interval '10 hours 40 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '3 days' + interval '10 hours 40 minutes 00 seconds' WHERE id = 17;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '3 days' + interval '13 hours 20 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '3 days' + interval '13 hours 20 minutes 00 seconds' WHERE id = 18;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '3 days' + interval '15 hours 35 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '3 days' + interval '15 hours 35 minutes 00 seconds' WHERE id = 19;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '3 days' + interval '17 hours 10 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '3 days' + interval '17 hours 10 minutes 00 seconds' WHERE id = 20;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '2 days' + interval '09 hours 25 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '2 days' + interval '09 hours 25 minutes 00 seconds' WHERE id = 21;
|
||||
UPDATE issues SET closed_at = created_at + interval '1 days' WHERE id = 21;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '2 days' + interval '10 hours 50 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '2 days' + interval '10 hours 50 minutes 00 seconds' WHERE id = 22;
|
||||
UPDATE issues SET closed_at = created_at + interval '2 days' WHERE id = 22;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '2 days' + interval '12 hours 00 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '2 days' + interval '12 hours 00 minutes 00 seconds' WHERE id = 23;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '2 days' + interval '14 hours 30 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '2 days' + interval '14 hours 30 minutes 00 seconds' WHERE id = 24;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '2 days' + interval '16 hours 05 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '2 days' + interval '16 hours 05 minutes 00 seconds' WHERE id = 25;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '1 days' + interval '09 hours 15 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '1 days' + interval '09 hours 15 minutes 00 seconds' WHERE id = 26;
|
||||
UPDATE issues SET closed_at = created_at + interval '1 days' WHERE id = 26;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '1 days' + interval '10 hours 30 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '1 days' + interval '10 hours 30 minutes 00 seconds' WHERE id = 27;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '1 days' + interval '11 hours 50 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '1 days' + interval '11 hours 50 minutes 00 seconds' WHERE id = 28;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '1 days' + interval '14 hours 10 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '1 days' + interval '14 hours 10 minutes 00 seconds' WHERE id = 29;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '1 days' + interval '16 hours 45 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '1 days' + interval '16 hours 45 minutes 00 seconds' WHERE id = 30;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '0 days' + interval '09 hours 05 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '0 days' + interval '09 hours 05 minutes 00 seconds' WHERE id = 31;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '0 days' + interval '10 hours 35 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '0 days' + interval '10 hours 35 minutes 00 seconds' WHERE id = 32;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '0 days' + interval '14 hours 20 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '0 days' + interval '14 hours 20 minutes 00 seconds' WHERE id = 33;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '0 days' + interval '16 hours 00 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '0 days' + interval '16 hours 00 minutes 00 seconds' WHERE id = 34;
|
||||
UPDATE issues SET created_at = date_trunc('day', now()) - interval '0 days' + interval '17 hours 30 minutes 00 seconds', updated_at = date_trunc('day', now()) - interval '0 days' + interval '17 hours 30 minutes 00 seconds' WHERE id = 35;
|
||||
UPDATE issue_logs l SET created_at = i.created_at + (rn.rn * interval '30 minutes') FROM issues i, (SELECT id, row_number() OVER (PARTITION BY issue_id ORDER BY id) rn FROM issue_logs) rn WHERE l.id = rn.id AND l.issue_id = i.id;
|
||||
@@ -0,0 +1,170 @@
|
||||
import { writeFileSync } from 'node:fs'
|
||||
import { join, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const BASE = 'http://localhost:8080/api/v1'
|
||||
|
||||
async function api(path, options = {}) {
|
||||
const res = await fetch(`${BASE}${path}`, options)
|
||||
const text = await res.text()
|
||||
let json = null
|
||||
try { json = JSON.parse(text) } catch { /* empty body */ }
|
||||
if (!res.ok) {
|
||||
throw new Error(`${options.method || 'GET'} ${path} -> ${res.status} ${text}`)
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
function auth(token) {
|
||||
return { 'content-type': 'application/json', authorization: `Bearer ${token}` }
|
||||
}
|
||||
|
||||
// deadline 相对天数(正值未来/负值过去),null 表示无截止日期
|
||||
const dl = (n) => {
|
||||
if (n === null || n === undefined) return null
|
||||
const d = new Date(Date.now() + n * 86400000)
|
||||
const pad = (x) => String(x).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
||||
}
|
||||
|
||||
// daysAgo: 0=今天, 6=6天前;time: 当天创建时刻;closedDaysAfter: 关闭距创建天数
|
||||
const issues = [
|
||||
// ============ D-6(6 天前)============
|
||||
{ title: '用户注册流程中重复邮箱校验失败提示不明确', description: '重复邮箱注册时仅提示「注册失败」无具体原因,需明确提示邮箱已被占用。', status: 'closed', priority: 'medium', phase: '基本设计', subProject: '用户管理子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(-3), daysAgo: 6, time: '00:00:00', closedDaysAfter: 2 },
|
||||
{ title: '接口鉴权异常返回状态码与文档不一致', description: 'Token 过期时接口返回 403,而接口文档约定为 401,前端异常处理无法正确触发刷新。', status: 'closed', priority: 'high', phase: '详细设计', subProject: '权限控制子系统', category: '安全漏洞', impactLevel: '较高', impactScope: '影响全部用户', assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: dl(-4), daysAgo: 6, time: '10:20', closedDaysAfter: 3 },
|
||||
{ title: '大屏报表在高峰时段刷新导致页面卡死(已逾期)', description: '数据大屏每分钟自动刷新,高峰时段后端响应慢,前端堆叠请求导致页面卡死,属逾期紧急问题。', status: 'pending', priority: 'urgent', phase: '综合测试 (ST)', subProject: '数据报表子系统', category: '性能问题', impactLevel: '严重', impactScope: '影响全部用户', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(-6), daysAgo: 6, time: '00:00:00' },
|
||||
{ title: '用户批量导入时角色字段被忽略', description: '批量导入用户时角色列数据未写入,导入后全部用户无角色权限。', status: 'in_progress', priority: 'medium', phase: '单体测试', subProject: '用户管理子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: dl(2), daysAgo: 6, time: '14:05' },
|
||||
{ title: '报表导出按钮在移动端被遮挡', description: '移动端打开报表页,导出按钮被底部导航遮挡,需调整响应式布局。', status: 'draft', priority: 'low', phase: '基本设计', subProject: '数据报表子系统', category: 'UI/UX问题', impactLevel: '轻微', impactScope: '仅本页面', assigneeId: 2, reviewerId: 1, departmentId: 4, deadline: null, daysAgo: 6, time: '16:40' },
|
||||
// ============ D-5(5 天前)============
|
||||
{ title: '角色权限矩阵说明文档缺少新增权限项', description: '新增「报表导出」权限后,角色权限矩阵说明文档未同步更新。', status: 'closed', priority: 'low', phase: '详细设计', subProject: '权限控制子系统', category: '文档错误', impactLevel: '轻微', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: dl(-3), daysAgo: 5, time: '09:30', closedDaysAfter: 2 },
|
||||
{ title: '用户停用后再启用,登录时提示账号不存在', description: '停用再启用的用户登录时被缓存系统判定不存在,需清理登录缓存或调整查询逻辑。', status: 'pending_confirm', priority: 'high', phase: '结合测试 (IT)', subProject: '用户管理子系统', category: '功能缺陷', impactLevel: '较高', impactScope: '影响部分模块', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(1), daysAgo: 5, time: '11:10' },
|
||||
{ title: '报表筛选条件切换时偶发白屏', description: '快速切换筛选条件时偶发白屏,前端异常未捕获,需增加错误边界。', status: 'in_progress', priority: 'medium', phase: '单体测试', subProject: '数据报表子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: dl(2), daysAgo: 5, time: '13:45', attachments: ['report-white-screen.log'] },
|
||||
{ title: '密码重置链接可在过期后仍被使用', description: '密码重置链接生成 30 分钟后仍可使用,安全窗口过长,需收紧有效期并校验。', status: 'pending', priority: 'urgent', phase: '综合测试 (ST)', subProject: '权限控制子系统', category: '安全漏洞', impactLevel: '严重', impactScope: '影响全部用户', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(-4), daysAgo: 5, time: '15:20' },
|
||||
{ title: '个人中心头像上传后不即时刷新', description: '上传头像成功后页面仍显示旧头像,需刷新浏览器才更新。', status: 'draft', priority: 'medium', phase: '基本设计', subProject: '用户管理子系统', category: 'UI/UX问题', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 4, deadline: null, daysAgo: 5, time: '17:55' },
|
||||
// ============ D-4(4 天前)============
|
||||
{ title: '子账号无法继承上级部门的默认权限', description: '新建子账号后未继承所属部门默认权限,需手动逐个配置,影响开通效率。', status: 'closed', priority: 'high', phase: '详细设计', subProject: '权限控制子系统', category: '功能缺陷', impactLevel: '较高', impactScope: '影响部分模块', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(-2), daysAgo: 4, time: '09:00', closedDaysAfter: 2 },
|
||||
{ title: '报表时间筛选默认值不正确导致首屏查询异常', description: '默认时间范围取到上个月而非当月,首屏数据与预期不符。', status: 'closed', priority: 'medium', phase: '单体测试', subProject: '数据报表子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: dl(-1), daysAgo: 4, time: '10:15', closedDaysAfter: 3, attachments: ['report-date-filter.log'] },
|
||||
{ title: '用户详情页部门信息保存后丢失', description: '编辑用户部门后保存成功,但刷新页面部门回退为原值,保存逻辑未持久化。', status: 'pending', priority: 'medium', phase: '结合测试 (IT)', subProject: '用户管理子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(3), daysAgo: 4, time: '12:30' },
|
||||
{ title: '权限配置界面缺少操作指引文案', description: '权限树配置界面无任何操作指引,新用户难以理解继承与覆盖规则。', status: 'in_progress', priority: 'low', phase: '基本设计', subProject: '权限控制子系统', category: '文档错误', impactLevel: '轻微', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: dl(3), daysAgo: 4, time: '14:50' },
|
||||
{ title: '报表缓存命中率低导致频繁全量查询', description: '缓存 key 未细化到筛选条件,命中率低,频繁触发全量查询拖慢接口。', status: 'pending_confirm', priority: 'high', phase: '综合测试 (ST)', subProject: '数据报表子系统', category: '性能问题', impactLevel: '较高', impactScope: '影响部分模块', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(-1), daysAgo: 4, time: '16:25' },
|
||||
// ============ D-3(3 天前)============
|
||||
{ title: '登录日志中明文记录密码字段', description: '审计日志将密码字段明文写入,存在泄露风险,需脱敏处理。', status: 'closed', priority: 'urgent', phase: '综合测试 (ST)', subProject: '用户管理子系统', category: '安全漏洞', impactLevel: '严重', impactScope: '影响全部用户', assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: dl(-1), daysAgo: 3, time: '09:10', closedDaysAfter: 2 },
|
||||
{ title: '菜单权限下发后部分终端 5 分钟未生效', description: '角色菜单权限更新后,部分终端最长 5 分钟才生效,需清理网关侧权限缓存。', status: 'in_progress', priority: 'high', phase: '结合测试 (IT)', subProject: '权限控制子系统', category: '功能缺陷', impactLevel: '较高', impactScope: '影响部分模块', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(2), daysAgo: 3, time: '10:40' },
|
||||
{ title: '图表导出 Excel 后公式失效', description: '报表图表导出 Excel 后数据变为静态值,原有公式/联动丢失。', status: 'pending', priority: 'medium', phase: '详细设计', subProject: '数据报表子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: dl(2), daysAgo: 3, time: '13:20' },
|
||||
{ title: '深色主题下校验错误提示看不清', description: '深色主题下表单校验错误文字对比度不足,难以辨认。', status: 'pending_confirm', priority: 'low', phase: '基本设计', subProject: '用户管理子系统', category: 'UI/UX问题', impactLevel: '轻微', impactScope: '仅本页面', assigneeId: 2, reviewerId: 1, departmentId: 4, deadline: dl(-1), daysAgo: 3, time: '15:35' },
|
||||
{ title: '权限变更审计日志缺少操作前后对比', description: '权限变更日志只记录变更后结果,缺少变更前后对比,无法追溯误操作。', status: 'draft', priority: 'medium', phase: '详细设计', subProject: '权限控制子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '影响部分模块', assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: dl(4), daysAgo: 3, time: '17:10' },
|
||||
// ============ D-2(2 天前)============
|
||||
{ title: '报表订阅任务偶发重复推送', description: '订阅报表在任务重试时未做幂等处理,偶发同一份报表重复推送。', status: 'closed', priority: 'medium', phase: '单体测试', subProject: '数据报表子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(-1), daysAgo: 2, time: '09:25', closedDaysAfter: 1, attachments: ['subscribe-duplicate.log'] },
|
||||
{ title: '用户导出接口存在越权查看他人信息风险', description: '用户导出接口未按数据权限过滤,可导出全部用户信息,已加数据权限校验。', status: 'closed', priority: 'high', phase: '结合测试 (IT)', subProject: '用户管理子系统', category: '安全漏洞', impactLevel: '较高', impactScope: '影响全部用户', assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: dl(0), daysAgo: 2, time: '10:50', closedDaysAfter: 2 },
|
||||
{ title: '权限变更后旧 Token 权限未即时回收(已逾期)', description: '调整角色权限后,用户已签发的旧 Token 仍持有旧权限,需在鉴权时实时校验。', status: 'in_progress', priority: 'urgent', phase: '综合测试 (ST)', subProject: '权限控制子系统', category: '安全漏洞', impactLevel: '严重', impactScope: '影响全部用户', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(-1), daysAgo: 2, time: '12:00', agent: { goal: '分析旧 Token 权限未即时回收问题并生成对应方案', action: 'approve-pending' } },
|
||||
{ title: '大数据量筛选查询未走索引', description: '报表大表筛选条件未命中索引,全表扫描导致响应慢,需评估加复合索引。', status: 'pending', priority: 'medium', phase: '详细设计', subProject: '数据报表子系统', category: '性能问题', impactLevel: '一般', impactScope: '影响部分模块', assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: dl(3), daysAgo: 2, time: '14:30' },
|
||||
{ title: '用户操作手册缺少批量操作章节', description: '操作手册未收录用户批量导入/停用/导出说明,新员工无法按手册操作。', status: 'draft', priority: 'low', phase: '基本设计', subProject: '用户管理子系统', category: '文档错误', impactLevel: '轻微', impactScope: '仅本页面', assigneeId: 2, reviewerId: 1, departmentId: 3, deadline: null, daysAgo: 2, time: '16:05' },
|
||||
// ============ D-1(1 天前)============
|
||||
{ title: '权限树刷新后折叠状态丢失', description: '刷新页面后权限树展开/折叠状态丢失,需记忆用户浏览位置。', status: 'closed', priority: 'high', phase: '综合测试 (ST)', subProject: '权限控制子系统', category: '功能缺陷', impactLevel: '较高', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: dl(0), daysAgo: 1, time: '09:15', closedDaysAfter: 1 },
|
||||
{ title: '报表订阅邮件正文乱码', description: '订阅邮件正文中文显示乱码,邮件未指定 UTF-8 编码。', status: 'in_progress', priority: 'medium', phase: '结合测试 (IT)', subProject: '数据报表子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '影响部分模块', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(0), daysAgo: 1, time: '10:30', agent: { goal: '分析订阅邮件乱码问题并生成修复方案', action: 'reject' } },
|
||||
{ title: '验证码接口未限制调用频率(即将到期)', description: '图形验证码接口可被高频调用,存在资源耗尽与爆破风险,需限流。', status: 'in_progress', priority: 'urgent', phase: '综合测试 (ST)', subProject: '用户管理子系统', category: '安全漏洞', impactLevel: '严重', impactScope: '影响全部用户', assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: dl(1), daysAgo: 1, time: '11:50' },
|
||||
{ title: '角色复制时资源权限合并逻辑错误', description: '角色 A 复制给 B 时,B 原有资源权限被整体覆盖而非合并,导致权限丢失。', status: 'pending_confirm', priority: 'high', phase: '单体测试', subProject: '权限控制子系统', category: '功能缺陷', impactLevel: '较高', impactScope: '影响部分模块', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(0), daysAgo: 1, time: '14:10' },
|
||||
{ title: '移动端表单在输入法弹出时遮挡提交按钮', description: '移动端输入时软键盘遮挡提交按钮,无法完成提交,需处理键盘弹出滚动。', status: 'pending', priority: 'medium', phase: '详细设计', subProject: '用户管理子系统', category: 'UI/UX问题', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 4, deadline: dl(1), daysAgo: 1, time: '16:45' },
|
||||
// ============ D0(今天)============
|
||||
{ title: '权限复制操作未校验目标角色是否存在', description: '权限复制时未校验目标角色 ID 有效性,传非法 ID 返回成功但实际未复制。', status: 'in_progress', priority: 'high', phase: '结合测试 (IT)', subProject: '权限控制子系统', category: '功能缺陷', impactLevel: '较高', impactScope: '影响部分模块', assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: dl(0), daysAgo: 0, time: '09:05' },
|
||||
{ title: '报表导出任务失败后无重试机制', description: '报表导出失败后任务直接终止,无重试与失败通知,需增加重试策略。', status: 'pending', priority: 'medium', phase: '单体测试', subProject: '数据报表子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面', assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: dl(1), daysAgo: 0, time: '10:35' },
|
||||
{ title: '页面底部固定操作栏遮挡内容', description: '详情页底部固定操作栏遮挡正文内容,滚动到底部时无法查看最后几行。', status: 'pending_confirm', priority: 'low', phase: '基本设计', subProject: '用户管理子系统', category: 'UI/UX问题', impactLevel: '轻微', impactScope: '仅本页面', assigneeId: null, reviewerId: 1, departmentId: 2, deadline: null, daysAgo: 0, time: '14:20' },
|
||||
{ title: '权限模板缺少批量应用入口', description: '权限模板只能逐个应用到角色,缺少批量应用入口,操作效率低。', status: 'draft', priority: 'medium', phase: '详细设计', subProject: '权限控制子系统', category: '功能缺陷', impactLevel: '一般', impactScope: '影响部分模块', assigneeId: null, reviewerId: 1, departmentId: 2, deadline: null, daysAgo: 0, time: '16:00' },
|
||||
{ title: '登录失败三次后未触发锁定策略(今日截止)', description: '连续登录失败三次后账号未被锁定,暴力破解防护缺失,今日截止需尽快整改。', status: 'pending', priority: 'urgent', phase: '综合测试 (ST)', subProject: '用户管理子系统', category: '安全漏洞', impactLevel: '严重', impactScope: '影响全部用户', assigneeId: null, reviewerId: 1, departmentId: 2, deadline: dl(0), daysAgo: 0, time: '17:30' },
|
||||
]
|
||||
|
||||
const FLOW = {
|
||||
draft: [],
|
||||
pending: ['pending'],
|
||||
in_progress: ['pending', 'in_progress'],
|
||||
pending_confirm: ['pending', 'in_progress', 'pending_confirm'],
|
||||
closed: ['pending', 'in_progress', 'pending_confirm', 'closed'],
|
||||
}
|
||||
|
||||
const ATTACH_CONTENT = {
|
||||
'report-white-screen.log': '2026-08-0X 13:47:32 ERROR Uncaught TypeError: Cannot read properties of undefined (reading map)',
|
||||
'report-date-filter.log': '2026-08-0X 10:16:01 WARN default date range resolved to 2026-06-01 ~ 2026-06-30',
|
||||
'subscribe-duplicate.log': '2026-08-0X 09:26:40 ERROR duplicated push job#7821 retried 2 times',
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const login = await api('/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ username: 'admin', password: 'Admin@2026' }),
|
||||
})
|
||||
const token = login.data.accessToken
|
||||
console.log('登录成功\n')
|
||||
|
||||
const sql = ['-- generated by seed-7d.mjs']
|
||||
const created = []
|
||||
|
||||
for (const [i, it] of issues.entries()) {
|
||||
const num = String(i + 1).padStart(2, '0')
|
||||
const { status, daysAgo, time, closedDaysAfter, attachments, agent, ...payload } = it
|
||||
const res = await api('/issues', {
|
||||
method: 'POST',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
const issue = res.data
|
||||
console.log(`[${num}] 创建 ${issue.issueNo} (D-${daysAgo} ${time}) - ${issue.title}`)
|
||||
|
||||
for (const s of FLOW[status] || []) {
|
||||
await api(`/issues/${issue.id}/status`, {
|
||||
method: 'PATCH',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify({ status: s, remark: '7天数据流转' }),
|
||||
})
|
||||
}
|
||||
|
||||
if (attachments && attachments.length) {
|
||||
for (const name of attachments) {
|
||||
const fd = new FormData()
|
||||
fd.append('file', new Blob([ATTACH_CONTENT[name] || name], { type: 'text/plain' }), name)
|
||||
await api(`/issues/${issue.id}/attachments`, { method: 'POST', headers: { authorization: `Bearer ${token}` }, body: fd })
|
||||
console.log(` 上传附件 ${name}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (agent) {
|
||||
const exec = await api('/agent/execute', {
|
||||
method: 'POST',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify({ issueId: issue.id, goal: agent.goal }),
|
||||
})
|
||||
const planId = exec.data.planId
|
||||
console.log(` Agent 执行 -> plan#${planId}`)
|
||||
if (agent.action === 'reject') {
|
||||
await api(`/agent/approval/${planId}/reject`, {
|
||||
method: 'POST',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify({ comment: '方案不适用,请人工处理' }),
|
||||
})
|
||||
console.log(` Agent 已驳回 plan#${planId}`)
|
||||
} else if (agent.action === 'approve-pending') {
|
||||
console.log(` Agent 审批保持 pending`)
|
||||
}
|
||||
}
|
||||
|
||||
// 回填 created_at(今天 - daysAgo 天 + time 时刻)
|
||||
const [hh, mm, ss = '00'] = time.split(':')
|
||||
sql.push(`UPDATE issues SET created_at = date_trunc('day', now()) - interval '${daysAgo} days' + interval '${hh} hours ${mm} minutes ${ss} seconds', updated_at = date_trunc('day', now()) - interval '${daysAgo} days' + interval '${hh} hours ${mm} minutes ${ss} seconds' WHERE id = ${issue.id};`)
|
||||
if (closedDaysAfter !== undefined) {
|
||||
sql.push(`UPDATE issues SET closed_at = created_at + interval '${closedDaysAfter} days' WHERE id = ${issue.id};`)
|
||||
}
|
||||
created.push(issue)
|
||||
}
|
||||
|
||||
// 回填 issue_logs.created_at(按创建时刻逐条 +30 分钟错开)
|
||||
sql.push(`UPDATE issue_logs l SET created_at = i.created_at + (rn.rn * interval '30 minutes') FROM issues i, (SELECT id, row_number() OVER (PARTITION BY issue_id ORDER BY id) rn FROM issue_logs) rn WHERE l.id = rn.id AND l.issue_id = i.id;`)
|
||||
|
||||
const outPath = join(dirname(fileURLToPath(import.meta.url)), 'seed-7d-backdate.sql')
|
||||
writeFileSync(outPath, sql.join('\n') + '\n', 'utf8')
|
||||
|
||||
console.log(`\n完成:共创建 ${created.length} 条 7 天数据`)
|
||||
console.log(`回填 SQL 已生成:${outPath}`)
|
||||
console.log(`请在 WSL 执行:docker exec -i ims-postgres psql -U ims -d ims -v ON_ERROR_STOP=1 < /mnt/c/Users/NB-070/Desktop/work2/ims-master/frontend/scripts/seed-7d-backdate.sql`)
|
||||
}
|
||||
|
||||
main().catch((e) => { console.error('失败:', e.message); process.exit(1) })
|
||||
@@ -0,0 +1,173 @@
|
||||
const BASE = 'http://localhost:8080/api/v1'
|
||||
|
||||
async function api(path, options = {}) {
|
||||
const res = await fetch(`${BASE}${path}`, options)
|
||||
const text = await res.text()
|
||||
let json = null
|
||||
try { json = JSON.parse(text) } catch { /* empty body */ }
|
||||
if (!res.ok) {
|
||||
throw new Error(`${options.method || 'GET'} ${path} -> ${res.status} ${text}`)
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
function auth(token) {
|
||||
return { 'content-type': 'application/json', authorization: `Bearer ${token}` }
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const login = await api('/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ username: 'admin', password: 'Admin@2026' }),
|
||||
})
|
||||
const token = login.data.accessToken
|
||||
console.log('登录成功\n')
|
||||
|
||||
const daysFromNow = (n) => {
|
||||
const d = new Date(Date.now() + n * 86400000)
|
||||
const pad = (x) => String(x).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
||||
}
|
||||
|
||||
const issues = [
|
||||
{
|
||||
title: '【演示】登录页面白屏无法进入系统',
|
||||
description: '输入正确账号密码点击登录后,页面白屏无任何响应,控制台报 React 渲染错误。',
|
||||
status: 'draft', priority: 'high', phase: '测试阶段', subProject: '前端开发',
|
||||
category: '界面问题', impactLevel: '严重', impactScope: '影响全部用户',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(7),
|
||||
},
|
||||
{
|
||||
title: '【演示】CSV 导出中文乱码',
|
||||
description: '导出指摘列表 CSV 后,用 Excel 打开中文标题显示为乱码,需在文件头加 BOM。',
|
||||
status: 'pending', priority: 'medium', phase: '编码阶段', subProject: '后端开发',
|
||||
category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: daysFromNow(5),
|
||||
},
|
||||
{
|
||||
title: '【演示】Dashboard 统计图表不渲染',
|
||||
description: '工作台首页图表区域空白,ECharts 容器高度为 0,需确认图表初始化时机。',
|
||||
status: 'in_progress', priority: 'high', phase: '测试阶段', subProject: '前端开发',
|
||||
category: '界面问题', impactLevel: '较大', impactScope: '影响部分模块',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(3),
|
||||
attachments: [{ name: 'screenshot-dashboard-blank.png', content: 'PNG 占位:Dashboard 图表白屏截图(演示数据)' }],
|
||||
},
|
||||
{
|
||||
title: '【演示】Agent 审批卡点击无响应',
|
||||
description: 'Agent 执行后弹出审批卡,点击批准/驳回按钮无反应,接口 500。',
|
||||
status: 'in_progress', priority: 'urgent', phase: '编码阶段', subProject: '后端开发',
|
||||
category: '功能缺陷', impactLevel: '严重', impactScope: '影响全部用户',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 4, deadline: daysFromNow(2),
|
||||
agent: { goal: '查找登录白屏相似案例并生成对应方案', action: 'approve-pending' },
|
||||
},
|
||||
{
|
||||
title: '【演示】通知铃铛未读数量不更新',
|
||||
description: '收到状态流转通知后,右上角铃铛未读数仍为 0,需刷新才显示。',
|
||||
status: 'pending_confirm', priority: 'medium', phase: '上线阶段', subProject: '通知模块',
|
||||
category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: daysFromNow(6),
|
||||
},
|
||||
{
|
||||
title: '【演示】附件下载返回 404',
|
||||
description: '点击附件下载按钮,接口返回 404,MinIO 对象与数据库记录不一致。',
|
||||
status: 'closed', priority: 'low', phase: '测试阶段', subProject: '后端开发',
|
||||
category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(-3),
|
||||
attachments: [{ name: 'error-log.txt', content: '2026-08-01 10:23:45 ERROR download attachment 404' }],
|
||||
},
|
||||
{
|
||||
title: '【演示】逾期未处理的紧急指摘',
|
||||
description: '该指摘已超过整改截止日期仍未处理,需催办担当者。',
|
||||
status: 'pending', priority: 'urgent', phase: '上线阶段', subProject: '数据库',
|
||||
category: '数据问题', impactLevel: '严重', impactScope: '影响全部用户',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: daysFromNow(-1),
|
||||
},
|
||||
{
|
||||
title: '【演示】Agent 方案被驳回回退人工',
|
||||
description: 'Agent 生成方案后经审批被驳回,回退人工模式由担当者手动处理。',
|
||||
status: 'in_progress', priority: 'high', phase: '编码阶段', subProject: '前端开发',
|
||||
category: '需求变更', impactLevel: '较大', impactScope: '影响部分模块',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 4, deadline: daysFromNow(4),
|
||||
agent: { goal: '自动生成附件下载修复方案', action: 'reject' },
|
||||
},
|
||||
{
|
||||
title: '【演示】CSV 导出大数据量性能',
|
||||
description: '导出 1 万条指摘时接口耗时超过 30 秒,前端超时。',
|
||||
status: 'closed', priority: 'medium', phase: '编码阶段', subProject: '后端开发',
|
||||
category: '性能问题', impactLevel: '较大', impactScope: '影响部分模块',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: daysFromNow(-2),
|
||||
},
|
||||
{
|
||||
title: '【演示】多状态流转完整链路验证',
|
||||
description: '该指摘完整走完 草稿→待对应→对应中→待确认→已关闭 全链路,用于演示状态机与流转日志。',
|
||||
status: 'closed', priority: 'medium', phase: '需求阶段', subProject: '业务验证',
|
||||
category: '需求变更', impactLevel: '一般', impactScope: '仅本页面',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 3, deadline: daysFromNow(-5),
|
||||
},
|
||||
]
|
||||
|
||||
const FLOW = {
|
||||
draft: [],
|
||||
pending: ['pending'],
|
||||
in_progress: ['pending', 'in_progress'],
|
||||
pending_confirm: ['pending', 'in_progress', 'pending_confirm'],
|
||||
closed: ['pending', 'in_progress', 'pending_confirm', 'closed'],
|
||||
}
|
||||
|
||||
const created = []
|
||||
for (const [i, it] of issues.entries()) {
|
||||
const num = String(i + 1).padStart(2, '0')
|
||||
const { status, attachments, agent, ...payload } = it
|
||||
const res = await api('/issues', {
|
||||
method: 'POST',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
const issue = res.data
|
||||
console.log(`[${num}] 创建 ${issue.issueNo} - ${issue.title}`)
|
||||
|
||||
for (const s of FLOW[status] || []) {
|
||||
await api(`/issues/${issue.id}/status`, {
|
||||
method: 'PATCH',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify({ status: s, remark: '演示流转' }),
|
||||
})
|
||||
console.log(` 流转 -> ${s}`)
|
||||
}
|
||||
|
||||
if (attachments && attachments.length) {
|
||||
for (const a of attachments) {
|
||||
const fd = new FormData()
|
||||
fd.append('file', new Blob([a.content], { type: 'text/plain' }), a.name)
|
||||
await api(`/issues/${issue.id}/attachments`, { method: 'POST', headers: { authorization: `Bearer ${token}` }, body: fd })
|
||||
console.log(` 上传附件 ${a.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (agent) {
|
||||
const exec = await api('/agent/execute', {
|
||||
method: 'POST',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify({ issueId: issue.id, goal: agent.goal }),
|
||||
})
|
||||
const planId = exec.data.planId
|
||||
console.log(` Agent 执行 -> plan#${planId}`)
|
||||
if (agent.action === 'approve-pending') {
|
||||
console.log(` Agent 审批保持 pending(演示审批中)`)
|
||||
} else if (agent.action === 'reject') {
|
||||
await api(`/agent/approval/${planId}/reject`, {
|
||||
method: 'POST',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify({ comment: '方案不适用,请人工处理' }),
|
||||
})
|
||||
console.log(` Agent 已驳回 plan#${planId}`)
|
||||
}
|
||||
}
|
||||
created.push(issue)
|
||||
}
|
||||
|
||||
console.log(`\n完成:共创建 ${created.length} 条演示数据`)
|
||||
}
|
||||
|
||||
main().catch((e) => { console.error('失败:', e.message); process.exit(1) })
|
||||
@@ -0,0 +1,244 @@
|
||||
const BASE = 'http://localhost:8080/api/v1'
|
||||
|
||||
async function api(path, options = {}) {
|
||||
const res = await fetch(`${BASE}${path}`, options)
|
||||
const text = await res.text()
|
||||
let json = null
|
||||
try { json = JSON.parse(text) } catch { /* empty body */ }
|
||||
if (!res.ok) {
|
||||
throw new Error(`${options.method || 'GET'} ${path} -> ${res.status} ${text}`)
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
function auth(token) {
|
||||
return { 'content-type': 'application/json', authorization: `Bearer ${token}` }
|
||||
}
|
||||
|
||||
const daysFromNow = (n) => {
|
||||
const d = new Date(Date.now() + n * 86400000)
|
||||
const pad = (x) => String(x).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
||||
}
|
||||
|
||||
const issues = [
|
||||
{
|
||||
title: '「用户管理」批量导入用户时部门字段校验缺失',
|
||||
description: '批量导入 Excel 时,部门字段为空或填写错误也能导入成功,导致用户归属部门错乱,需增加必填与有效性校验。',
|
||||
status: 'draft', priority: 'high', phase: '基本设计', subProject: '用户管理子系统',
|
||||
category: '功能缺陷', impactLevel: '较高', impactScope: '影响全部用户',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(10),
|
||||
},
|
||||
{
|
||||
title: '权限控制子系统操作日志字段说明文档缺失',
|
||||
description: '操作日志接口返回字段(requestId、clientIp、duration)缺少字段说明,开发对接困难,需补齐接口文档。',
|
||||
status: 'draft', priority: 'medium', phase: '详细设计', subProject: '权限控制子系统',
|
||||
category: '文档错误', impactLevel: '一般', impactScope: '仅本页面',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: daysFromNow(12),
|
||||
},
|
||||
{
|
||||
title: '用户列表页在高分辨率下表格布局错位',
|
||||
description: '2560x1440 分辨率下用户列表表格列宽挤压错位,操作按钮被遮挡,需调整弹性布局。',
|
||||
status: 'draft', priority: 'low', phase: '基本设计', subProject: '用户管理子系统',
|
||||
category: 'UI/UX问题', impactLevel: '轻微', impactScope: '影响部分模块',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 4, deadline: daysFromNow(14),
|
||||
},
|
||||
{
|
||||
title: '报表导出超时:5万行数据接口响应超过60秒',
|
||||
description: '导出 5 万行报表数据时接口耗时超过 60 秒,前端请求超时,需改为异步导出或分批流式输出。',
|
||||
status: 'pending', priority: 'urgent', phase: '综合测试 (ST)', subProject: '数据报表子系统',
|
||||
category: '性能问题', impactLevel: '严重', impactScope: '影响全部用户',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: daysFromNow(-2),
|
||||
},
|
||||
{
|
||||
title: '越权访问:普通用户可调用管理员角色接口',
|
||||
description: '使用普通账号登录后,直接访问 /admin/* 接口返回 200,权限过滤未生效,存在越权风险。',
|
||||
status: 'pending', priority: 'urgent', phase: '结合测试 (IT)', subProject: '权限控制子系统',
|
||||
category: '安全漏洞', impactLevel: '严重', impactScope: '影响全部用户',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(-1),
|
||||
},
|
||||
{
|
||||
title: '忘记密码流程未发送重置邮件',
|
||||
description: '点击「忘记密码」并提交邮箱后未收到重置邮件,邮件服务异常但无错误提示,用户无法自助找回密码。',
|
||||
status: 'pending', priority: 'medium', phase: '单体测试', subProject: '用户管理子系统',
|
||||
category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: daysFromNow(-1),
|
||||
},
|
||||
{
|
||||
title: '角色权限勾选后未实时生效,需刷新页面',
|
||||
description: '修改角色权限并保存后,对应账号重新登录仍显示旧菜单,需强制刷新浏览器才能生效。',
|
||||
status: 'pending', priority: 'medium', phase: '详细设计', subProject: '权限控制子系统',
|
||||
category: '功能缺陷', impactLevel: '一般', impactScope: '影响部分模块',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(5),
|
||||
},
|
||||
{
|
||||
title: '数据报表子系统接口文档缺少分页参数说明',
|
||||
description: '报表查询接口文档未说明 page/pageSize 参数默认值与上限,前端翻页按 20 条处理与后端默认值不一致。',
|
||||
status: 'pending', priority: 'low', phase: '基本设计', subProject: '数据报表子系统',
|
||||
category: '文档错误', impactLevel: '轻微', impactScope: '仅本页面',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: daysFromNow(7),
|
||||
},
|
||||
{
|
||||
title: '登录接口存在暴力破解风险,无验证码与限流',
|
||||
description: '登录接口连续失败无次数限制、无验证码、无 IP 限流,可被脚本暴力破解弱口令账号。',
|
||||
status: 'in_progress', priority: 'urgent', phase: '综合测试 (ST)', subProject: '用户管理子系统',
|
||||
category: '安全漏洞', impactLevel: '严重', impactScope: '影响全部用户',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(-3),
|
||||
},
|
||||
{
|
||||
title: '权限复制功能在子菜单继承时丢失父级权限',
|
||||
description: '将角色 A 权限复制给角色 B 后,B 的某些子菜单有权限但父级菜单无权限,导致页面 403。',
|
||||
status: 'in_progress', priority: 'high', phase: '结合测试 (IT)', subProject: '权限控制子系统',
|
||||
category: '功能缺陷', impactLevel: '较高', impactScope: '影响部分模块',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: daysFromNow(2),
|
||||
agent: { goal: '分析权限复制子菜单继承丢失父级权限问题并生成对应方案', action: 'reject' },
|
||||
},
|
||||
{
|
||||
title: '报表图表加载慢:首屏渲染耗时 8 秒',
|
||||
description: '打开报表图表页首屏渲染需 8 秒,前端一次性拉取全部历史数据,需改为按日期范围按需加载。',
|
||||
status: 'in_progress', priority: 'high', phase: '单体测试', subProject: '数据报表子系统',
|
||||
category: '性能问题', impactLevel: '较高', impactScope: '影响部分模块',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 3, deadline: daysFromNow(4),
|
||||
},
|
||||
{
|
||||
title: '修改手机号后旧 Token 仍有效,未强制下线',
|
||||
description: '用户修改绑定手机号后,旧 Token 依旧有效可继续访问,存在账号被冒用风险,应在敏感信息变更后使旧 Token 失效。',
|
||||
status: 'in_progress', priority: 'medium', phase: '详细设计', subProject: '用户管理子系统',
|
||||
category: '安全漏洞', impactLevel: '较高', impactScope: '影响全部用户',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: daysFromNow(6),
|
||||
agent: { goal: '修改手机号后旧Token失效方案设计,检索知识库相似案例', action: 'approve-pending' },
|
||||
},
|
||||
{
|
||||
title: '用户搜索输入中文时出现乱码',
|
||||
description: '用户列表搜索框输入中文关键词后,接口返回数据为空,日志显示查询参数编码异常。',
|
||||
status: 'in_progress', priority: 'medium', phase: '单体测试', subProject: '用户管理子系统',
|
||||
category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(8),
|
||||
attachments: [{ name: 'search-garbled-log.txt', content: '2026-08-06 14:32:10 WARN query keyword=[\uFFFD\uFFFD] returns 0 rows' }],
|
||||
},
|
||||
{
|
||||
title: '数据报表缓存未失效,修改后仍显示旧数据',
|
||||
description: '修改基础数据后刷新报表,图表仍展示缓存中的旧数据,缓存 key 未包含数据更新时间。',
|
||||
status: 'pending_confirm', priority: 'high', phase: '综合测试 (ST)', subProject: '数据报表子系统',
|
||||
category: '功能缺陷', impactLevel: '较高', impactScope: '影响部分模块',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: daysFromNow(1),
|
||||
},
|
||||
{
|
||||
title: '用户批量停用后历史操作记录被误删',
|
||||
description: '批量停用用户时,误触发历史操作记录清理逻辑,导致该用户历史日志丢失,需与停用流程解耦。',
|
||||
status: 'pending_confirm', priority: 'high', phase: '结合测试 (IT)', subProject: '用户管理子系统',
|
||||
category: '功能缺陷', impactLevel: '较高', impactScope: '影响部分模块',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(3),
|
||||
},
|
||||
{
|
||||
title: '权限树勾选父节点时子节点状态不同步',
|
||||
description: '权限树中勾选父节点后,部分子节点显示为半选状态,保存后子节点权限丢失,需级联同步。',
|
||||
status: 'pending_confirm', priority: 'medium', phase: '单体测试', subProject: '权限控制子系统',
|
||||
category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 2, deadline: daysFromNow(5),
|
||||
attachments: [{ name: 'screenshot-permission-tree.png', content: 'PNG 占位:权限树半选状态截图(测试数据)' }],
|
||||
},
|
||||
{
|
||||
title: '综合测试环境 SQL 注入漏洞:动态拼接登录查询',
|
||||
description: '登录接口存在动态拼接 SQL,构造恶意用户名可绕过密码校验,已复现并完成修复,待上线验证。',
|
||||
status: 'closed', priority: 'urgent', phase: '综合测试 (ST)', subProject: '用户管理子系统',
|
||||
category: '安全漏洞', impactLevel: '严重', impactScope: '影响全部用户',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 2, deadline: daysFromNow(-6),
|
||||
},
|
||||
{
|
||||
title: '用户编辑时角色下拉缺少默认选中值',
|
||||
description: '编辑已有用户时,角色下拉框未回显当前角色,保存时如未重新选择会误覆盖角色,已改为编辑时预填当前角色。',
|
||||
status: 'closed', priority: 'high', phase: '结合测试 (IT)', subProject: '用户管理子系统',
|
||||
category: '功能缺陷', impactLevel: '较高', impactScope: '影响部分模块',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: daysFromNow(-4),
|
||||
},
|
||||
{
|
||||
title: '报表页日期筛选在 Chrome 下无法选择',
|
||||
description: '报表页日期范围控件在 Chrome 最新版下点击无响应,Safari 正常,需兼容第三方日期组件初始化时机。',
|
||||
status: 'closed', priority: 'medium', phase: '单体测试', subProject: '数据报表子系统',
|
||||
category: '功能缺陷', impactLevel: '一般', impactScope: '仅本页面',
|
||||
assigneeId: 2, reviewerId: 1, departmentId: 4, deadline: daysFromNow(-2),
|
||||
attachments: [{ name: 'repro-datepicker-steps.txt', content: '复现:1.打开报表页 2.点击日期框 3.Chrome v126 无弹层' }],
|
||||
},
|
||||
{
|
||||
title: '权限控制子系统部署说明文档版本过旧',
|
||||
description: '部署文档仍为 v1.2 旧版,与实际 v2.0 配置(新增 JWT 密钥项)不一致,需同步更新部署手册。',
|
||||
status: 'closed', priority: 'low', phase: '详细设计', subProject: '权限控制子系统',
|
||||
category: '文档错误', impactLevel: '轻微', impactScope: '仅本页面',
|
||||
assigneeId: 3, reviewerId: 1, departmentId: 3, deadline: daysFromNow(-5),
|
||||
},
|
||||
]
|
||||
|
||||
const FLOW = {
|
||||
draft: [],
|
||||
pending: ['pending'],
|
||||
in_progress: ['pending', 'in_progress'],
|
||||
pending_confirm: ['pending', 'in_progress', 'pending_confirm'],
|
||||
closed: ['pending', 'in_progress', 'pending_confirm', 'closed'],
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const login = await api('/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ username: 'admin', password: 'Admin@2026' }),
|
||||
})
|
||||
const token = login.data.accessToken
|
||||
console.log('登录成功\n')
|
||||
|
||||
const created = []
|
||||
for (const [i, it] of issues.entries()) {
|
||||
const num = String(i + 1).padStart(2, '0')
|
||||
const { status, attachments, agent, ...payload } = it
|
||||
const res = await api('/issues', {
|
||||
method: 'POST',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
const issue = res.data
|
||||
console.log(`[${num}] 创建 ${issue.issueNo} - ${issue.title}`)
|
||||
|
||||
for (const s of FLOW[status] || []) {
|
||||
await api(`/issues/${issue.id}/status`, {
|
||||
method: 'PATCH',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify({ status: s, remark: '测试数据流转' }),
|
||||
})
|
||||
console.log(` 流转 -> ${s}`)
|
||||
}
|
||||
|
||||
if (attachments && attachments.length) {
|
||||
for (const a of attachments) {
|
||||
const fd = new FormData()
|
||||
fd.append('file', new Blob([a.content], { type: 'text/plain' }), a.name)
|
||||
await api(`/issues/${issue.id}/attachments`, { method: 'POST', headers: { authorization: `Bearer ${token}` }, body: fd })
|
||||
console.log(` 上传附件 ${a.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (agent) {
|
||||
const exec = await api('/agent/execute', {
|
||||
method: 'POST',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify({ issueId: issue.id, goal: agent.goal }),
|
||||
})
|
||||
const planId = exec.data.planId
|
||||
console.log(` Agent 执行 -> plan#${planId}`)
|
||||
if (agent.action === 'approve-pending') {
|
||||
console.log(` Agent 审批保持 pending(演示审批中)`)
|
||||
} else if (agent.action === 'reject') {
|
||||
await api(`/agent/approval/${planId}/reject`, {
|
||||
method: 'POST',
|
||||
headers: auth(token),
|
||||
body: JSON.stringify({ comment: '方案不适用,请人工处理' }),
|
||||
})
|
||||
console.log(` Agent 已驳回 plan#${planId}`)
|
||||
}
|
||||
}
|
||||
created.push(issue)
|
||||
}
|
||||
|
||||
console.log(`\n完成:共创建 ${created.length} 条测试数据`)
|
||||
}
|
||||
|
||||
main().catch((e) => { console.error('失败:', e.message); process.exit(1) })
|
||||
Reference in New Issue
Block a user