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,172 @@
package model
import (
"time"
)
// UserRoleMapping 用户-角色关联模型
// 对应数据库 iam_user_roles 表
type UserRoleMapping struct {
ID int64 // 主键ID
UserID int64 // 用户ID
RoleID int64 // 角色ID (FK -> iam_roles.id)
TenantID int64 // 租户范围NULL表示全局0也代表全局
GrantedBy int64 // 授权人ID
ExpiresAt *time.Time // 角色过期时间nil表示永不过期
IsActive bool // 是否激活
// 审计字段
RequestID string // 请求追踪ID
CreatedIP string // 创建者IP
UpdatedIP string // 更新者IP
Version int // 乐观锁版本号
// 时间戳
CreatedAt *time.Time // 创建时间
UpdatedAt *time.Time // 更新时间
GrantedAt *time.Time // 授权时间
}
// NewUserRoleMapping 创建新的用户-角色映射
func NewUserRoleMapping(userID, roleID, tenantID int64) *UserRoleMapping {
now := time.Now()
return &UserRoleMapping{
UserID: userID,
RoleID: roleID,
TenantID: tenantID,
IsActive: true,
RequestID: generateRequestID(),
Version: 1,
CreatedAt: &now,
UpdatedAt: &now,
}
}
// NewUserRoleMappingWithGrant 创建带授权信息的用户-角色映射
func NewUserRoleMappingWithGrant(userID, roleID, tenantID, grantedBy int64, expiresAt *time.Time) *UserRoleMapping {
now := time.Now()
return &UserRoleMapping{
UserID: userID,
RoleID: roleID,
TenantID: tenantID,
GrantedBy: grantedBy,
ExpiresAt: expiresAt,
GrantedAt: &now,
IsActive: true,
RequestID: generateRequestID(),
Version: 1,
CreatedAt: &now,
UpdatedAt: &now,
}
}
// HasRole 检查用户是否拥有指定角色
func (m *UserRoleMapping) HasRole(roleID int64) bool {
return m.RoleID == roleID && m.IsActive
}
// IsGlobalRole 检查是否为全局角色租户ID为0或nil
func (m *UserRoleMapping) IsGlobalRole() bool {
return m.TenantID == 0
}
// IsExpired 检查角色是否已过期
func (m *UserRoleMapping) IsExpired() bool {
if m.ExpiresAt == nil {
return false // 永不过期
}
return time.Now().After(*m.ExpiresAt)
}
// IsValid 检查角色分配是否有效(激活且未过期)
func (m *UserRoleMapping) IsValid() bool {
return m.IsActive && !m.IsExpired()
}
// Revoke 撤销角色分配
func (m *UserRoleMapping) Revoke() {
m.IsActive = false
m.UpdatedAt = nowPtr()
}
// Grant 重新授予角色
func (m *UserRoleMapping) Grant() {
m.IsActive = true
m.UpdatedAt = nowPtr()
}
// IncrementVersion 递增版本号
func (m *UserRoleMapping) IncrementVersion() {
m.Version++
m.UpdatedAt = nowPtr()
}
// ExtendExpiration 延长过期时间
func (m *UserRoleMapping) ExtendExpiration(newExpiresAt *time.Time) {
m.ExpiresAt = newExpiresAt
m.UpdatedAt = nowPtr()
}
// UserRoleMappingInfo 用户-角色映射信息用于API响应
type UserRoleMappingInfo struct {
UserID int64 `json:"user_id"`
RoleID int64 `json:"role_id"`
TenantID int64 `json:"tenant_id"`
IsActive bool `json:"is_active"`
ExpiresAt *string `json:"expires_at,omitempty"`
}
// ToInfo 转换为映射信息
func (m *UserRoleMapping) ToInfo() *UserRoleMappingInfo {
info := &UserRoleMappingInfo{
UserID: m.UserID,
RoleID: m.RoleID,
TenantID: m.TenantID,
IsActive: m.IsActive,
}
if m.ExpiresAt != nil {
expStr := m.ExpiresAt.Format(time.RFC3339)
info.ExpiresAt = &expStr
}
return info
}
// UserRoleAssignmentInfo 用户角色分配详情用于API响应
type UserRoleAssignmentInfo struct {
UserID int64 `json:"user_id"`
RoleCode string `json:"role_code"`
RoleName string `json:"role_name"`
TenantID int64 `json:"tenant_id"`
GrantedBy int64 `json:"granted_by"`
GrantedAt string `json:"granted_at"`
ExpiresAt string `json:"expires_at,omitempty"`
IsActive bool `json:"is_active"`
IsExpired bool `json:"is_expired"`
}
// UserRoleWithDetails 用户角色分配(含角色详情)
type UserRoleWithDetails struct {
*UserRoleMapping
RoleCode string
RoleName string
}
// ToAssignmentInfo 转换为分配详情
func (m *UserRoleWithDetails) ToAssignmentInfo() *UserRoleAssignmentInfo {
info := &UserRoleAssignmentInfo{
UserID: m.UserID,
RoleCode: m.RoleCode,
RoleName: m.RoleName,
TenantID: m.TenantID,
GrantedBy: m.GrantedBy,
IsActive: m.IsActive,
IsExpired: m.IsExpired(),
}
if m.GrantedAt != nil {
info.GrantedAt = m.GrantedAt.Format(time.RFC3339)
}
if m.ExpiresAt != nil {
info.ExpiresAt = m.ExpiresAt.Format(time.RFC3339)
}
return info
}