310 lines
8.9 KiB
Go
310 lines
8.9 KiB
Go
|
|
package api
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"tyapi-server/internal/application/api/dto"
|
||
|
|
"tyapi-server/internal/domains/api/entities"
|
||
|
|
"tyapi-server/internal/domains/api/repositories"
|
||
|
|
api_services "tyapi-server/internal/domains/api/services"
|
||
|
|
"tyapi-server/internal/shared/interfaces"
|
||
|
|
|
||
|
|
"go.uber.org/zap"
|
||
|
|
"gorm.io/gorm"
|
||
|
|
)
|
||
|
|
|
||
|
|
type QueryWhitelistApplicationService interface {
|
||
|
|
CreateEntry(ctx context.Context, adminUserID string, req *dto.QueryWhitelistEntryRequest) (*dto.QueryWhitelistEntryResponse, error)
|
||
|
|
UpdateEntry(ctx context.Context, adminUserID, id string, req *dto.QueryWhitelistEntryUpdateRequest) (*dto.QueryWhitelistEntryResponse, error)
|
||
|
|
UpdateEntryStatus(ctx context.Context, adminUserID, id, status string) (*dto.QueryWhitelistEntryResponse, error)
|
||
|
|
DeleteEntry(ctx context.Context, id string) error
|
||
|
|
GetEntry(ctx context.Context, id string) (*dto.QueryWhitelistEntryResponse, error)
|
||
|
|
ListEntries(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) (*dto.QueryWhitelistListResponse, error)
|
||
|
|
ImportLegacyEntries(ctx context.Context, adminUserID string) (*dto.QueryWhitelistImportLegacyResponse, error)
|
||
|
|
}
|
||
|
|
|
||
|
|
type QueryWhitelistApplicationServiceImpl struct {
|
||
|
|
repo repositories.QueryWhitelistRepository
|
||
|
|
queryWhitelistSvc api_services.QueryWhitelistService
|
||
|
|
logger *zap.Logger
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewQueryWhitelistApplicationService(
|
||
|
|
repo repositories.QueryWhitelistRepository,
|
||
|
|
queryWhitelistSvc api_services.QueryWhitelistService,
|
||
|
|
logger *zap.Logger,
|
||
|
|
) QueryWhitelistApplicationService {
|
||
|
|
return &QueryWhitelistApplicationServiceImpl{
|
||
|
|
repo: repo,
|
||
|
|
queryWhitelistSvc: queryWhitelistSvc,
|
||
|
|
logger: logger,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *QueryWhitelistApplicationServiceImpl) CreateEntry(
|
||
|
|
ctx context.Context,
|
||
|
|
adminUserID string,
|
||
|
|
req *dto.QueryWhitelistEntryRequest,
|
||
|
|
) (*dto.QueryWhitelistEntryResponse, error) {
|
||
|
|
if err := validateQueryWhitelistRequest(req.UserID, req.Name, req.IDCard, req.APICodes); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
idCardHash := api_services.HashIDCard(req.IDCard)
|
||
|
|
exists, err := s.repo.ExistsByUserIDCardHashAndName(ctx, req.UserID, idCardHash, strings.TrimSpace(req.Name), "")
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
if exists {
|
||
|
|
return nil, fmt.Errorf("该用户下已存在相同的身份证与姓名规则")
|
||
|
|
}
|
||
|
|
|
||
|
|
entry := &entities.QueryWhitelistEntry{
|
||
|
|
UserID: strings.TrimSpace(req.UserID),
|
||
|
|
Name: normalizeWhitelistName(req.Name),
|
||
|
|
IDCardHash: idCardHash,
|
||
|
|
IDCardMasked: api_services.MaskIDCard(req.IDCard),
|
||
|
|
APICodes: entities.APICodeList(req.APICodes),
|
||
|
|
Status: entities.QueryWhitelistStatusEnabled,
|
||
|
|
Remark: strings.TrimSpace(req.Remark),
|
||
|
|
CreatedBy: &adminUserID,
|
||
|
|
}
|
||
|
|
if err := s.repo.Create(ctx, entry); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
s.queryWhitelistSvc.InvalidateCache(entry.UserID, idCardHash)
|
||
|
|
resp := dto.NewQueryWhitelistEntryResponse(entry)
|
||
|
|
return &resp, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *QueryWhitelistApplicationServiceImpl) UpdateEntry(
|
||
|
|
ctx context.Context,
|
||
|
|
adminUserID, id string,
|
||
|
|
req *dto.QueryWhitelistEntryUpdateRequest,
|
||
|
|
) (*dto.QueryWhitelistEntryResponse, error) {
|
||
|
|
entry, err := s.repo.FindByID(ctx, id)
|
||
|
|
if err != nil {
|
||
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
|
|
return nil, fmt.Errorf("规则不存在")
|
||
|
|
}
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
oldHash := entry.IDCardHash
|
||
|
|
|
||
|
|
if req.Name != "" {
|
||
|
|
entry.Name = normalizeWhitelistName(req.Name)
|
||
|
|
}
|
||
|
|
if req.IDCard != "" {
|
||
|
|
if err := validateIDCard(req.IDCard); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
entry.IDCardHash = api_services.HashIDCard(req.IDCard)
|
||
|
|
entry.IDCardMasked = api_services.MaskIDCard(req.IDCard)
|
||
|
|
}
|
||
|
|
if len(req.APICodes) > 0 {
|
||
|
|
if err := validateAPICodes(req.APICodes); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
entry.APICodes = entities.APICodeList(req.APICodes)
|
||
|
|
}
|
||
|
|
if req.Remark != "" || req.Remark == "" {
|
||
|
|
entry.Remark = strings.TrimSpace(req.Remark)
|
||
|
|
}
|
||
|
|
|
||
|
|
exists, err := s.repo.ExistsByUserIDCardHashAndName(ctx, entry.UserID, entry.IDCardHash, entry.Name, entry.ID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
if exists {
|
||
|
|
return nil, fmt.Errorf("该用户下已存在相同的身份证与姓名规则")
|
||
|
|
}
|
||
|
|
|
||
|
|
entry.UpdatedBy = &adminUserID
|
||
|
|
if err := s.repo.Update(ctx, entry); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
s.queryWhitelistSvc.InvalidateCache(entry.UserID, oldHash)
|
||
|
|
s.queryWhitelistSvc.InvalidateCache(entry.UserID, entry.IDCardHash)
|
||
|
|
resp := dto.NewQueryWhitelistEntryResponse(entry)
|
||
|
|
return &resp, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *QueryWhitelistApplicationServiceImpl) UpdateEntryStatus(
|
||
|
|
ctx context.Context,
|
||
|
|
adminUserID, id, status string,
|
||
|
|
) (*dto.QueryWhitelistEntryResponse, error) {
|
||
|
|
entry, err := s.repo.FindByID(ctx, id)
|
||
|
|
if err != nil {
|
||
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
|
|
return nil, fmt.Errorf("规则不存在")
|
||
|
|
}
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
entry.Status = status
|
||
|
|
entry.UpdatedBy = &adminUserID
|
||
|
|
if err := s.repo.Update(ctx, entry); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
s.queryWhitelistSvc.InvalidateCache(entry.UserID, entry.IDCardHash)
|
||
|
|
resp := dto.NewQueryWhitelistEntryResponse(entry)
|
||
|
|
return &resp, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *QueryWhitelistApplicationServiceImpl) DeleteEntry(ctx context.Context, id string) error {
|
||
|
|
entry, err := s.repo.FindByID(ctx, id)
|
||
|
|
if err != nil {
|
||
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
|
|
return fmt.Errorf("规则不存在")
|
||
|
|
}
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if err := s.repo.Delete(ctx, id); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
s.queryWhitelistSvc.InvalidateCache(entry.UserID, entry.IDCardHash)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *QueryWhitelistApplicationServiceImpl) GetEntry(ctx context.Context, id string) (*dto.QueryWhitelistEntryResponse, error) {
|
||
|
|
entry, err := s.repo.FindByID(ctx, id)
|
||
|
|
if err != nil {
|
||
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
|
|
return nil, fmt.Errorf("规则不存在")
|
||
|
|
}
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
resp := dto.NewQueryWhitelistEntryResponse(entry)
|
||
|
|
return &resp, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *QueryWhitelistApplicationServiceImpl) ListEntries(
|
||
|
|
ctx context.Context,
|
||
|
|
filters map[string]interface{},
|
||
|
|
options interfaces.ListOptions,
|
||
|
|
) (*dto.QueryWhitelistListResponse, error) {
|
||
|
|
if idCard, ok := filters["id_card"].(string); ok && idCard != "" {
|
||
|
|
filters["id_card_hash"] = api_services.HashIDCard(idCard)
|
||
|
|
delete(filters, "id_card")
|
||
|
|
}
|
||
|
|
entries, total, err := s.repo.List(ctx, filters, options)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
items := make([]dto.QueryWhitelistEntryResponse, 0, len(entries))
|
||
|
|
for _, entry := range entries {
|
||
|
|
items = append(items, dto.NewQueryWhitelistEntryResponse(entry))
|
||
|
|
}
|
||
|
|
page := options.Page
|
||
|
|
if page < 1 {
|
||
|
|
page = 1
|
||
|
|
}
|
||
|
|
size := options.PageSize
|
||
|
|
if size < 1 {
|
||
|
|
size = 20
|
||
|
|
}
|
||
|
|
return &dto.QueryWhitelistListResponse{
|
||
|
|
Items: items,
|
||
|
|
Total: total,
|
||
|
|
Page: page,
|
||
|
|
Size: size,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *QueryWhitelistApplicationServiceImpl) ImportLegacyEntries(
|
||
|
|
ctx context.Context,
|
||
|
|
adminUserID string,
|
||
|
|
) (*dto.QueryWhitelistImportLegacyResponse, error) {
|
||
|
|
imported := 0
|
||
|
|
skipped := 0
|
||
|
|
for _, idCard := range LegacyHardcodedIDCards {
|
||
|
|
hash := api_services.HashIDCard(idCard)
|
||
|
|
exists, err := s.repo.ExistsByUserIDCardHashAndName(
|
||
|
|
ctx,
|
||
|
|
entities.QueryWhitelistGlobalUserID,
|
||
|
|
hash,
|
||
|
|
entities.QueryWhitelistWildcardName,
|
||
|
|
"",
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
if exists {
|
||
|
|
skipped++
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
entry := &entities.QueryWhitelistEntry{
|
||
|
|
UserID: entities.QueryWhitelistGlobalUserID,
|
||
|
|
Name: entities.QueryWhitelistWildcardName,
|
||
|
|
IDCardHash: hash,
|
||
|
|
IDCardMasked: api_services.MaskIDCard(idCard),
|
||
|
|
APICodes: entities.APICodeList{"*"},
|
||
|
|
Status: entities.QueryWhitelistStatusEnabled,
|
||
|
|
Remark: "自硬编码迁移-全局",
|
||
|
|
CreatedBy: &adminUserID,
|
||
|
|
}
|
||
|
|
if err := s.repo.Create(ctx, entry); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
s.queryWhitelistSvc.InvalidateCache(entities.QueryWhitelistGlobalUserID, hash)
|
||
|
|
imported++
|
||
|
|
}
|
||
|
|
return &dto.QueryWhitelistImportLegacyResponse{
|
||
|
|
Imported: imported,
|
||
|
|
Skipped: skipped,
|
||
|
|
Total: len(LegacyHardcodedIDCards),
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateQueryWhitelistRequest(userID, name, idCard string, apiCodes []string) error {
|
||
|
|
userID = strings.TrimSpace(userID)
|
||
|
|
if userID == "" {
|
||
|
|
return fmt.Errorf("user_id 不能为空")
|
||
|
|
}
|
||
|
|
if strings.TrimSpace(name) == "" {
|
||
|
|
return fmt.Errorf("name 不能为空")
|
||
|
|
}
|
||
|
|
if err := validateIDCard(idCard); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return validateAPICodes(apiCodes)
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateIDCard(idCard string) error {
|
||
|
|
idCard = api_services.NormalizeIDCard(idCard)
|
||
|
|
if len(idCard) != 18 {
|
||
|
|
return fmt.Errorf("身份证号格式不正确")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateAPICodes(apiCodes []string) error {
|
||
|
|
if len(apiCodes) == 0 {
|
||
|
|
return fmt.Errorf("api_codes 不能为空")
|
||
|
|
}
|
||
|
|
hasWildcard := false
|
||
|
|
for _, code := range apiCodes {
|
||
|
|
code = strings.TrimSpace(code)
|
||
|
|
if code == "" {
|
||
|
|
return fmt.Errorf("api_codes 不能包含空值")
|
||
|
|
}
|
||
|
|
if code == "*" {
|
||
|
|
hasWildcard = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if hasWildcard && len(apiCodes) > 1 {
|
||
|
|
return fmt.Errorf("api_codes 包含 * 时不能与其他编码混用")
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func normalizeWhitelistName(name string) string {
|
||
|
|
name = strings.TrimSpace(name)
|
||
|
|
if name == entities.QueryWhitelistWildcardName {
|
||
|
|
return entities.QueryWhitelistWildcardName
|
||
|
|
}
|
||
|
|
return name
|
||
|
|
}
|