feat: 优化web

This commit is contained in:
2026-04-23 18:58:13 +08:00
commit 544a2f3428
160 changed files with 27327 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
package entity
import (
"time"
)
// SystemParam 系统参数实体
// 用于存储系统运行所需的各种配置参数,支持多种数据类型和分组管理
type SystemParam struct {
// ID 主键,使用 UUID v4 保证全局唯一性,避免自增 ID 带来的信息泄露风险
ID string `json:"id" gorm:"column:id;type:varchar(36);primaryKey;not null;comment:主键"`
// ParamKey 参数键名,全局唯一,用于标识和访问参数值
// 命名规范:小写字母 + 下划线,如:site_name, max_upload_size
ParamKey string `json:"param_key" gorm:"column:param_key;type:varchar(100);uniqueIndex;not null;comment:参数键"`
// ParamValue 参数值,存储实际配置内容
// 根据 ParamType 不同,可能是字符串、数字、布尔值或 JSON 数组
ParamValue string `json:"param_value" gorm:"column:param_value;type:varchar(1000);not null;comment:参数值"`
// ParamType 参数类型,决定参数的校验规则和展示方式
// 可选值:text(文本), number(数字), boolean(布尔), select(下拉选择)
ParamType string `json:"param_type" gorm:"column:param_type;type:varchar(20);not null;default:'text';comment:类型:text,number,boolean,select"`
// ParamGroup 参数分组,用于对参数进行逻辑分组管理
// 常见分组:basic(基础), security(安全), business(业务), system(系统)
ParamGroup string `json:"param_group" gorm:"column:param_group;type:varchar(50);not null;default:'default';comment:分组"`
// ParamDesc 参数描述,说明该参数的用途、取值范围、默认值等信息
// 建议包含:参数说明、可选值说明、修改影响等
ParamDesc string `json:"param_desc" gorm:"column:param_desc;type:varchar(500);comment:描述"`
// CreatorID 创建人 ID,记录创建该参数的用户标识
// 用于审计追踪,定位参数创建者
CreatorID string `json:"creator_id" gorm:"column:creator_id;type:varchar(36);not null;default:'';comment:创建人 ID"`
// CreateTime 创建时间,记录参数创建的时间点
// 使用指针类型,可以区分"未设置"和"已设置"状态
// 数据库层面使用 CURRENT_TIMESTAMP 自动填充
CreateTime *time.Time `json:"create_time" gorm:"column:create_time;type:datetime;default:current_timestamp;comment:创建时间"`
// LastUpdaterID 最后更新人 ID,记录最后一次修改该参数的用户标识
// 用于审计追踪,定位参数修改者
LastUpdaterID string `json:"last_updater_id" gorm:"column:last_updater_id;type:varchar(36);not null;default:'';comment:最后更新人 ID"`
// UpdateTime 最后更新时间,记录参数最后一次修改的时间点
// 使用指针类型,可以区分"未设置"和"已设置"状态
// 数据库层面使用 ON UPDATE CURRENT_TIMESTAMP 自动更新
UpdateTime *time.Time `json:"update_time" gorm:"column:update_time;type:datetime;default:current_timestamp;on update current_timestamp;comment:最后更新时间"`
}
// TableName 指定表名为 system_param
// 遵循数据库命名规范:小写字母 + 下划线,复数形式
func (SystemParam) TableName() string {
return "system_param"
}
// ParamType 参数类型常量
// 定义系统支持的参数类型,用于前端展示和后端校验
type ParamType string
const (
// ParamTypeText 文本类型,适用于字符串值
ParamTypeText ParamType = "text"
// ParamTypeNumber 数字类型,适用于整数值
ParamTypeNumber ParamType = "number"
// ParamTypeBoolean 布尔类型,适用于 true/false 值
ParamTypeBoolean ParamType = "boolean"
// ParamTypeSelect 下拉选择类型,适用于预定义选项值
ParamTypeSelect ParamType = "select"
)
// ParamGroup 参数分组常量
// 定义系统参数的逻辑分组,便于分类管理和权限控制
type ParamGroup string
const (
// GroupBasic 基础配置分组,包含系统基本信息
// 如:站点名称、Logo、联系方式等
GroupBasic ParamGroup = "basic"
// GroupSecurity 安全配置分组,包含安全相关参数
// 如:密码策略、登录限制、Token 有效期等
GroupSecurity ParamGroup = "security"
// GroupBusiness 业务配置分组,包含业务逻辑相关参数
// 如:订单配置、支付参数、业务开关等
GroupBusiness ParamGroup = "business"
// GroupSystem 系统配置分组,包含系统运行参数
// 如:缓存配置、日志级别、性能参数等
GroupSystem ParamGroup = "system"
// GroupDefault 默认分组,未明确分组的参数归入此类
GroupDefault ParamGroup = "default"
)
+177
View File
@@ -0,0 +1,177 @@
package handler
import (
"net/http"
"strconv"
"giter.top/smart/internal/system/service"
"github.com/gin-gonic/gin"
)
// ParamHandler 系统参数 HTTP 处理器
type ParamHandler struct {
service service.ParamService
}
// NewParamHandler 创建参数处理器实例
func NewParamHandler(svc service.ParamService) *ParamHandler {
return &ParamHandler{service: svc}
}
// CreateParam 创建系统参数
// @Summary 创建系统参数
// @Tags 系统参数
// @Accept json
// @Produce json
// @Param request body service.CreateParamRequest true "创建参数请求"
// @Success 201 {object} entity.SystemParam
// @Router /api/v1/system/params [post]
func (h *ParamHandler) CreateParam(c *gin.Context) {
var req service.CreateParamRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// TODO: 从上下文获取用户 ID(实际项目中从 JWT token 解析)
creatorID := "system"
param, err := h.service.CreateParam(c.Request.Context(), &req, creatorID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, param)
}
// UpdateParam 更新系统参数
// @Summary 更新系统参数
// @Tags 系统参数
// @Accept json
// @Produce json
// @Param id path string true "参数 ID"
// @Param request body service.UpdateParamRequest true "更新参数请求"
// @Success 200 {object} entity.SystemParam
// @Router /api/v1/system/params/{id} [put]
func (h *ParamHandler) UpdateParam(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"})
return
}
var req service.UpdateParamRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// TODO: 从上下文获取用户 ID
lastUpdaterID := "system"
param, err := h.service.UpdateParam(c.Request.Context(), id, &req, lastUpdaterID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, param)
}
// DeleteParams 批量删除系统参数
// @Summary 批量删除系统参数
// @Tags 系统参数
// @Accept json
// @Produce json
// @Param request body []string true "参数 ID 列表"
// @Success 204
// @Router /api/v1/system/params/batch [delete]
func (h *ParamHandler) DeleteParams(c *gin.Context) {
var ids []string
if err := c.ShouldBindJSON(&ids); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := h.service.DeleteParams(c.Request.Context(), ids); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.Status(http.StatusNoContent)
}
// GetParam 获取单个系统参数
// @Summary 获取单个系统参数
// @Tags 系统参数
// @Produce json
// @Param id path string true "参数 ID"
// @Success 200 {object} entity.SystemParam
// @Router /api/v1/system/params/{id} [get]
func (h *ParamHandler) GetParam(c *gin.Context) {
id := c.Param("id")
if id == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"})
return
}
param, err := h.service.GetParam(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, param)
}
// GetParamByKey 根据键获取系统参数
// @Summary 根据键获取系统参数
// @Tags 系统参数
// @Produce json
// @Param key path string true "参数键"
// @Success 200 {object} entity.SystemParam
// @Router /api/v1/system/params/key/{key} [get]
func (h *ParamHandler) GetParamByKey(c *gin.Context) {
key := c.Param("key")
param, err := h.service.GetParamByKey(c.Request.Context(), key)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, param)
}
// ListParams 获取系统参数列表
// @Summary 获取系统参数列表
// @Tags 系统参数
// @Produce json
// @Param group query string false "分组"
// @Param param_key query string false "参数键(模糊搜索)"
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Success 200 {object} service.ParamListResponse
// @Router /api/v1/system/params [get]
func (h *ParamHandler) ListParams(c *gin.Context) {
group := c.Query("group")
paramKey := c.Query("param_key")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
response, err := h.service.ListParams(c.Request.Context(), group, paramKey, page, pageSize)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, response)
}
// GetAllParams 获取所有系统参数
// @Summary 获取所有系统参数
// @Tags 系统参数
// @Produce json
// @Success 200 {object} map[string]entity.SystemParam
// @Router /api/v1/system/params/all [get]
func (h *ParamHandler) GetAllParams(c *gin.Context) {
params, err := h.service.GetAllParams(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, params)
}
+33
View File
@@ -0,0 +1,33 @@
package system
import (
"giter.top/smart/internal/system/handler"
"github.com/gin-gonic/gin"
)
// SystemRoutes 注册 system 模块的 HTTP 路由。
type SystemRoutes struct {
paramHandler *handler.ParamHandler
}
// NewSystemRoutes 构造 system 模块的路由注册器,由 Wire 注入。
func NewSystemRoutes( paramHandler *handler.ParamHandler) *SystemRoutes {
return &SystemRoutes{
paramHandler: paramHandler,
}
}
// TODO 添加注册信息
func (s *SystemRoutes) Register(engine *gin.Engine, apiGroup *gin.RouterGroup) {
group := apiGroup.Group("/system")
s.registerParamRoutes(group)
}
// 系统参数路由
func (s *SystemRoutes) registerParamRoutes(group *gin.RouterGroup) {
paramGroup := group.Group("/param")
{
paramGroup.POST("/create", s.paramHandler.CreateParam)
paramGroup.PUT("/update", s.paramHandler.UpdateParam)
paramGroup.DELETE("/delete-batch", s.paramHandler.DeleteParams)
paramGroup.GET("/get", s.paramHandler.GetParam)
paramGroup.GET("/list", s.paramHandler.ListParams)
}
}
@@ -0,0 +1,139 @@
package repository
import (
"context"
"errors"
"giter.top/smart/internal/system/entity"
"gorm.io/gorm"
)
// ErrNotFound 记录未找到
var ErrNotFound = errors.New("param not found")
// ParamRepository 系统参数数据访问层
type ParamRepository interface {
// Create 创建系统参数
Create(ctx context.Context, param *entity.SystemParam) error
// Update 更新系统参数
Update(ctx context.Context, param *entity.SystemParam) error
// Delete 删除系统参数
Delete(ctx context.Context, id string) error
// DeleteBatch 批量删除
DeleteBatch(ctx context.Context, ids []string) error
// GetByID 根据 ID 获取
GetByID(ctx context.Context, id string) (*entity.SystemParam, error)
// GetByKey 根据键获取
GetByKey(ctx context.Context, key string) (*entity.SystemParam, error)
// List 获取列表(支持分页和筛选)
List(ctx context.Context, group string, paramKey string, page, pageSize int) ([]entity.SystemParam, int64, error)
// GetAll 获取所有参数(用于缓存)
GetAll(ctx context.Context) (map[string]entity.SystemParam, error)
// ExistsByKey 检查键是否存在(排除指定 ID)
ExistsByKey(ctx context.Context, key string, excludeID string) (bool, error)
}
type paramRepository struct {
db *gorm.DB
}
// NewParamRepository 创建参数仓库实例
func NewParamRepository(db *gorm.DB) ParamRepository {
return &paramRepository{db: db}
}
func (r *paramRepository) Create(ctx context.Context, param *entity.SystemParam) error {
return r.db.WithContext(ctx).Create(param).Error
}
func (r *paramRepository) Update(ctx context.Context, param *entity.SystemParam) error {
return r.db.WithContext(ctx).Save(param).Error
}
func (r *paramRepository) Delete(ctx context.Context, id string) error {
return r.db.WithContext(ctx).Where("id = ?", id).Delete(&entity.SystemParam{}).Error
}
func (r *paramRepository) DeleteBatch(ctx context.Context, ids []string) error {
return r.db.WithContext(ctx).Where("id IN ?", ids).Delete(&entity.SystemParam{}).Error
}
func (r *paramRepository) GetByID(ctx context.Context, id string) (*entity.SystemParam, error) {
var param entity.SystemParam
err := r.db.WithContext(ctx).Where("id = ?", id).First(&param).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
}
return nil, err
}
return &param, nil
}
func (r *paramRepository) GetByKey(ctx context.Context, key string) (*entity.SystemParam, error) {
var param entity.SystemParam
err := r.db.WithContext(ctx).Where("param_key = ?", key).First(&param).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
}
return nil, err
}
return &param, nil
}
func (r *paramRepository) List(ctx context.Context, group string, paramKey string, page, pageSize int) ([]entity.SystemParam, int64, error) {
var params []entity.SystemParam
var total int64
query := r.db.WithContext(ctx).Model(&entity.SystemParam{})
// 应用筛选条件
if group != "" {
query = query.Where("param_group = ?", group)
}
if paramKey != "" {
query = query.Where("param_key LIKE ?", "%"+paramKey+"%")
}
// 获取总数
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
// 分页查询
offset := (page - 1) * pageSize
if offset < 0 {
offset = 0
}
if pageSize <= 0 {
pageSize = 10
}
err := query.Order("id DESC").Offset(offset).Limit(pageSize).Find(&params).Error
if err != nil {
return nil, 0, err
}
return params, total, nil
}
func (r *paramRepository) GetAll(ctx context.Context) (map[string]entity.SystemParam, error) {
var params []entity.SystemParam
err := r.db.WithContext(ctx).Find(&params).Error
if err != nil {
return nil, err
}
result := make(map[string]entity.SystemParam, len(params))
for _, param := range params {
result[param.ParamKey] = param
}
return result, nil
}
func (r *paramRepository) ExistsByKey(ctx context.Context, key string, excludeID string) (bool, error) {
query := r.db.WithContext(ctx).Where("param_key = ?", key)
if excludeID != "" {
query = query.Where("id != ?", excludeID)
}
var count int64
err := query.Model(&entity.SystemParam{}).Count(&count).Error
return count > 0, err
}
+271
View File
@@ -0,0 +1,271 @@
package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"giter.top/smart/internal/system/entity"
"giter.top/smart/internal/system/repository"
"giter.top/smart/pkg/utils/id"
"github.com/redis/go-redis/v9"
)
// ErrInvalidParam 参数无效
var ErrInvalidParam = errors.New("invalid param")
// ParamService 系统参数业务逻辑层
type ParamService interface {
// CreateParam 创建系统参数
CreateParam(ctx context.Context, req *CreateParamRequest, creatorID string) (*entity.SystemParam, error)
// UpdateParam 更新系统参数
UpdateParam(ctx context.Context, id string, req *UpdateParamRequest, lastUpdaterID string) (*entity.SystemParam, error)
// DeleteParam 删除系统参数
DeleteParam(ctx context.Context, id string) error
// DeleteParams 批量删除
DeleteParams(ctx context.Context, ids []string) error
// GetParam 获取单个参数
GetParam(ctx context.Context, id string) (*entity.SystemParam, error)
// GetParamByKey 根据键获取参数
GetParamByKey(ctx context.Context, key string) (*entity.SystemParam, error)
// ListParams 获取参数列表
ListParams(ctx context.Context, group string, paramKey string, page, pageSize int) (*ParamListResponse, error)
// GetAllParams 获取所有参数(用于缓存)
GetAllParams(ctx context.Context) (map[string]entity.SystemParam, error)
// GetParamValue 获取参数值(便捷方法)
GetParamValue(ctx context.Context, key string) (string, error)
// GetParamValueWithDefault 获取参数值,不存在则返回默认值
GetParamValueWithDefault(ctx context.Context, key string, defaultValue string) string
}
// CreateParamRequest 创建参数请求
type CreateParamRequest struct {
ParamKey string `json:"param_key" binding:"required,max=100"`
ParamValue string `json:"param_value" binding:"required"`
ParamType string `json:"param_type" binding:"required,oneof=text number boolean select"`
ParamGroup string `json:"param_group" binding:"required,max:50"`
ParamDesc string `json:"param_desc" max:"500"`
}
// UpdateParamRequest 更新参数请求
type UpdateParamRequest struct {
ParamValue string `json:"param_value"`
ParamType string `json:"param_type" binding:"omitempty,oneof=text number boolean select"`
ParamDesc string `json:"param_desc" max:"500"`
}
// ParamListResponse 参数列表响应
type ParamListResponse struct {
Items []entity.SystemParam `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalPages int `json:"total_pages"`
}
type paramService struct {
repo repository.ParamRepository
cache redis.UniversalClient
cacheKey string
}
// NewParamService 创建参数服务实例(与 cache.NewRedisClient 返回的 redis.UniversalClient 一致,便于 Wire 注入)
func NewParamService(repo repository.ParamRepository, cacheClient redis.UniversalClient) ParamService {
return &paramService{
repo: repo,
cache: cacheClient,
cacheKey: "system:params:*",
}
}
func (s *paramService) CreateParam(ctx context.Context, req *CreateParamRequest, creatorID string) (*entity.SystemParam, error) {
// 生成唯一 ID (UUID v7)
id := id.New()
// 检查键是否已存在
exists, err := s.repo.ExistsByKey(ctx, req.ParamKey, "")
if err != nil {
return nil, fmt.Errorf("检查参数键失败:%w", err)
}
if exists {
return nil, fmt.Errorf("参数键 %s 已存在", req.ParamKey)
}
param := &entity.SystemParam{
ID: id,
ParamKey: req.ParamKey,
ParamValue: req.ParamValue,
ParamType: req.ParamType,
ParamGroup: req.ParamGroup,
ParamDesc: req.ParamDesc,
CreatorID: creatorID,
LastUpdaterID: creatorID,
}
if err := s.repo.Create(ctx, param); err != nil {
return nil, fmt.Errorf("创建参数失败:%w", err)
}
// 刷新缓存
s.refreshCache(ctx)
return param, nil
}
func (s *paramService) UpdateParam(ctx context.Context, id string, req *UpdateParamRequest, lastUpdaterID string) (*entity.SystemParam, error) {
// 获取现有参数
param, err := s.repo.GetByID(ctx, id)
if err != nil {
if errors.Is(err, repository.ErrNotFound) {
return nil, fmt.Errorf("参数不存在")
}
return nil, fmt.Errorf("获取参数失败:%w", err)
}
// 更新字段
if req.ParamValue != "" {
param.ParamValue = req.ParamValue
}
if req.ParamType != "" {
param.ParamType = req.ParamType
}
if req.ParamDesc != "" {
param.ParamDesc = req.ParamDesc
}
param.LastUpdaterID = lastUpdaterID
if err := s.repo.Update(ctx, param); err != nil {
return nil, fmt.Errorf("更新参数失败:%w", err)
}
// 刷新缓存
s.refreshCache(ctx)
return param, nil
}
func (s *paramService) DeleteParam(ctx context.Context, id string) error {
if err := s.repo.Delete(ctx, id); err != nil {
return fmt.Errorf("删除参数失败:%w", err)
}
// 刷新缓存
s.refreshCache(ctx)
return nil
}
func (s *paramService) DeleteParams(ctx context.Context, ids []string) error {
if len(ids) == 0 {
return nil
}
if err := s.repo.DeleteBatch(ctx, ids); err != nil {
return fmt.Errorf("批量删除参数失败:%w", err)
}
// 刷新缓存
s.refreshCache(ctx)
return nil
}
func (s *paramService) GetParam(ctx context.Context, id string) (*entity.SystemParam, error) {
param, err := s.repo.GetByID(ctx, id)
if err != nil {
if errors.Is(err, repository.ErrNotFound) {
return nil, fmt.Errorf("参数不存在")
}
return nil, err
}
return param, nil
}
func (s *paramService) GetParamByKey(ctx context.Context, key string) (*entity.SystemParam, error) {
param, err := s.repo.GetByKey(ctx, key)
if err != nil {
if errors.Is(err, repository.ErrNotFound) {
return nil, fmt.Errorf("参数 %s 不存在", key)
}
return nil, err
}
return param, nil
}
func (s *paramService) ListParams(ctx context.Context, group string, paramKey string, page, pageSize int) (*ParamListResponse, error) {
if page <= 0 {
page = 1
}
if pageSize <= 0 {
pageSize = 10
}
items, total, err := s.repo.List(ctx, group, paramKey, page, pageSize)
if err != nil {
return nil, fmt.Errorf("获取参数列表失败:%w", err)
}
totalPages := int(total) / pageSize
if int(total)%pageSize != 0 {
totalPages++
}
return &ParamListResponse{
Items: items,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: totalPages,
}, nil
}
func (s *paramService) GetAllParams(ctx context.Context) (map[string]entity.SystemParam, error) {
// 先从缓存获取
if s.cache != nil {
cached := s.cache.Get(ctx, "system:params:all").Val()
if cached != "" {
var params map[string]entity.SystemParam
if err := json.Unmarshal([]byte(cached), &params); err == nil {
return params, nil
} else {
return nil, fmt.Errorf("解析缓存数据失败:%w", err)
}
}
}
// 缓存未命中,从数据库获取
params, err := s.repo.GetAll(ctx)
if err != nil {
return nil, err
}
// 写入缓存
if s.cache != nil {
data, _ := json.Marshal(params)
s.cache.Set(ctx, "system:params:all", string(data), 0) // 0 表示永不过期
}
return params, nil
}
func (s *paramService) GetParamValue(ctx context.Context, key string) (string, error) {
param, err := s.GetParamByKey(ctx, key)
if err != nil {
return "", err
}
return param.ParamValue, nil
}
func (s *paramService) GetParamValueWithDefault(ctx context.Context, key string, defaultValue string) string {
value, err := s.GetParamValue(ctx, key)
if err != nil {
return defaultValue
}
return value
}
// refreshCache 刷新缓存
func (s *paramService) refreshCache(ctx context.Context) {
if s.cache == nil {
return
}
// 删除缓存,让下次请求重新构建
s.cache.Del(ctx, "system:params:all")
}
+32
View File
@@ -0,0 +1,32 @@
package system
import (
"giter.top/smart/internal/system/handler"
"giter.top/smart/internal/system/repository"
"giter.top/smart/internal/system/service"
"github.com/google/wire"
)
// HandlerProviderSet 处理程序提供者集合
var handlerProviderSet = wire.NewSet(
handler.NewParamHandler,
)
// ServiceProviderSet 服务提供者集合
var serviceProviderSet = wire.NewSet(
service.NewParamService,
)
// RepositoryProviderSet 仓库提供者集合
var repositoryProviderSet = wire.NewSet(
repository.NewParamRepository,
)
var ProviderSet = wire.NewSet(
handlerProviderSet,
serviceProviderSet,
repositoryProviderSet,
NewSystemRoutes,
)