feat: systematic functional audit — fix 18 issues across Phase A/B

Phase A (P1 production blockers):
- A1: Apply IP rate limiting to public routes (login/refresh)
- A2: Publish domain events for workflow instance state transitions
  (completed/suspended/resumed/terminated) via outbox pattern
- A3: Replace hardcoded nil UUID default tenant with dynamic DB lookup
- A4: Add GET /api/v1/audit-logs query endpoint with pagination
- A5: Enhance CORS wildcard warning for production environments

Phase B (P2 functional gaps):
- B1: Remove dead erp-common crate (zero references in codebase)
- B2: Refactor 5 settings pages to use typed API modules instead of
  direct client calls; create api/themes.ts; delete dead errors.ts
- B3: Add resume/suspend buttons to InstanceMonitor page
- B4: Remove unused EventHandler trait from erp-core
- B5: Handle task.completed events in message module (send notifications)
- B6: Wire TimeoutChecker as 60s background task
- B7: Auto-skip ServiceTask nodes instead of crashing the process
- B8: Remove empty register_routes() from ErpModule trait and modules
This commit is contained in:
iven
2026-04-12 15:22:28 +08:00
parent 685df5e458
commit 14f431efff
34 changed files with 785 additions and 304 deletions

View File

@@ -16,22 +16,18 @@ import {
Tag,
} from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import client from '../../api/client';
import {
getMenus,
createMenu,
updateMenu,
deleteMenu,
type MenuInfo,
type MenuItemReq,
} from '../../api/menus';
// --- Types ---
interface MenuItem {
id: string;
parent_id?: string | null;
title: string;
path?: string;
icon?: string;
menu_type: 'directory' | 'menu' | 'button';
sort_order: number;
visible: boolean;
permission?: string;
children?: MenuItem[];
}
type MenuItem = MenuInfo;
// --- Helpers ---
@@ -105,9 +101,7 @@ export default function MenuConfig() {
const fetchMenus = useCallback(async () => {
setLoading(true);
try {
const { data: resp } = await client.get('/config/menus');
// 后端返回嵌套树结构,直接使用
const tree: MenuItem[] = resp.data ?? resp;
const tree = await getMenus();
setMenus(flattenMenuTree(tree));
setMenuTree(tree);
} catch {
@@ -120,22 +114,13 @@ export default function MenuConfig() {
fetchMenus();
}, [fetchMenus]);
const handleSubmit = async (values: {
parent_id?: string;
title: string;
path?: string;
icon?: string;
menu_type: 'directory' | 'menu' | 'button';
sort_order: number;
visible: boolean;
permission?: string;
}) => {
const handleSubmit = async (values: MenuItemReq) => {
try {
if (editMenu) {
await client.put(`/config/menus/${editMenu.id}`, values);
await updateMenu(editMenu.id, values);
message.success('菜单更新成功');
} else {
await client.post('/config/menus', values);
await createMenu(values);
message.success('菜单创建成功');
}
closeModal();
@@ -150,7 +135,7 @@ export default function MenuConfig() {
const handleDelete = async (id: string) => {
try {
await client.delete(`/config/menus/${id}`);
await deleteMenu(id);
message.success('菜单已删除');
fetchMenus();
} catch {