feat(P1/P2): 完成TDD开发及P1/P2设计文档

## 设计文档
- multi_role_permission_design: 多角色权限设计 (CONDITIONAL GO)
- audit_log_enhancement_design: 审计日志增强 (CONDITIONAL GO)
- routing_strategy_template_design: 路由策略模板 (CONDITIONAL GO)
- sso_saml_technical_research: SSO/SAML调研 (CONDITIONAL GO)
- compliance_capability_package_design: 合规能力包设计 (CONDITIONAL GO)

## TDD开发成果
- IAM模块: supply-api/internal/iam/ (111个测试)
- 审计日志模块: supply-api/internal/audit/ (40+测试)
- 路由策略模块: gateway/internal/router/ (33+测试)
- 合规能力包: gateway/internal/compliance/ + scripts/ci/compliance/

## 规范文档
- parallel_agent_output_quality_standards: 并行Agent产出质量规范
- project_experience_summary: 项目经验总结 (v2)
- 2026-04-02-p1-p2-tdd-execution-plan: TDD执行计划

## 评审报告
- 5个CONDITIONAL GO设计文档评审报告
- fix_verification_report: 修复验证报告
- full_verification_report: 全面质量验证报告
- tdd_module_quality_verification: TDD模块质量验证
- tdd_execution_summary: TDD执行总结

依据: Superpowers执行框架 + TDD规范
This commit is contained in:
Your Name
2026-04-02 23:35:53 +08:00
parent ed0961d486
commit 89104bd0db
94 changed files with 24738 additions and 5 deletions

View File

@@ -0,0 +1,114 @@
package middleware
import (
"context"
"database/sql"
"fmt"
"sync"
"time"
_ "github.com/jackc/pgx/v5/stdlib"
)
// DatabaseAuditEmitter 实现 AuditEmitter 接口,将审计事件存入数据库
type DatabaseAuditEmitter struct {
db *sql.DB
mu sync.RWMutex
now func() time.Time
}
// NewDatabaseAuditEmitter 创建数据库审计发射器
func NewDatabaseAuditEmitter(dsn string, now func() time.Time) (*DatabaseAuditEmitter, error) {
if now == nil {
now = time.Now
}
db, err := sql.Open("pgx", dsn)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
}
// 测试连接
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("failed to ping database: %w", err)
}
emitter := &DatabaseAuditEmitter{
db: db,
now: now,
}
// 初始化表
if err := emitter.initSchema(); err != nil {
return nil, fmt.Errorf("failed to init schema: %w", err)
}
return emitter, nil
}
// initSchema 创建审计表
func (e *DatabaseAuditEmitter) initSchema() error {
schema := `
CREATE TABLE IF NOT EXISTS token_audit_events (
event_id VARCHAR(64) PRIMARY KEY,
event_name VARCHAR(128) NOT NULL,
request_id VARCHAR(128) NOT NULL,
token_id VARCHAR(128),
subject_id VARCHAR(128),
route VARCHAR(256) NOT NULL,
result_code VARCHAR(64) NOT NULL,
client_ip VARCHAR(64),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_token_audit_request_id ON token_audit_events(request_id);
CREATE INDEX IF NOT EXISTS idx_token_audit_token_id ON token_audit_events(token_id);
CREATE INDEX IF NOT EXISTS idx_token_audit_subject_id ON token_audit_events(subject_id);
CREATE INDEX IF NOT EXISTS idx_token_audit_created_at ON token_audit_events(created_at);
`
_, err := e.db.Exec(schema)
return err
}
// Emit 实现 AuditEmitter 接口
func (e *DatabaseAuditEmitter) Emit(_ context.Context, event AuditEvent) error {
if event.EventID == "" {
event.EventID = fmt.Sprintf("evt-%d", e.now().UnixNano())
}
if event.CreatedAt.IsZero() {
event.CreatedAt = e.now()
}
query := `
INSERT INTO token_audit_events (event_id, event_name, request_id, token_id, subject_id, route, result_code, client_ip, created_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
`
_, err := e.db.Exec(query,
event.EventID,
event.EventName,
event.RequestID,
nullString(event.TokenID),
nullString(event.SubjectID),
event.Route,
event.ResultCode,
nullString(event.ClientIP),
event.CreatedAt,
)
return err
}
// Close 关闭数据库连接
func (e *DatabaseAuditEmitter) Close() error {
if e.db != nil {
return e.db.Close()
}
return nil
}
// nullString 安全处理空字符串
func nullString(s string) sql.NullString {
if s == "" {
return sql.NullString{}
}
return sql.NullString{String: s, Valid: true}
}