add
This commit is contained in:
112
internal/domains/api/entities/query_whitelist_entry.go
Normal file
112
internal/domains/api/entities/query_whitelist_entry.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
QueryWhitelistGlobalUserID = "*" // 全局规则:对所有用户生效
|
||||
QueryWhitelistWildcardName = "*" // 仅匹配身份证,不校验姓名(兼容历史硬编码)
|
||||
QueryWhitelistStatusEnabled = "enabled"
|
||||
QueryWhitelistStatusDisabled = "disabled"
|
||||
QueryWhitelistTableName = "query_whitelist_entries"
|
||||
)
|
||||
|
||||
// APICodeList 生效的 API 编码列表,["*"] 表示全部「身份证必填」类接口
|
||||
type APICodeList []string
|
||||
|
||||
func (a APICodeList) Value() (driver.Value, error) {
|
||||
if a == nil {
|
||||
return "[]", nil
|
||||
}
|
||||
data, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func (a *APICodeList) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
*a = APICodeList{}
|
||||
return nil
|
||||
}
|
||||
var bytes []byte
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
bytes = v
|
||||
case string:
|
||||
bytes = []byte(v)
|
||||
default:
|
||||
return errors.New("无法扫描 APICodeList 类型")
|
||||
}
|
||||
if len(bytes) == 0 || string(bytes) == "null" {
|
||||
*a = APICodeList{}
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(bytes, a)
|
||||
}
|
||||
|
||||
// QueryWhitelistEntry 查询白名单:命中后返回「查询为空」,不调用上游
|
||||
type QueryWhitelistEntry struct {
|
||||
ID string `gorm:"type:varchar(36);primaryKey" json:"id"`
|
||||
UserID string `gorm:"type:varchar(36);not null;index:idx_qwl_user_id_card_hash,priority:1" json:"user_id"`
|
||||
Name string `gorm:"type:varchar(100);not null" json:"name"`
|
||||
IDCardHash string `gorm:"type:varchar(64);not null;index:idx_qwl_user_id_card_hash,priority:2" json:"-"`
|
||||
IDCardMasked string `gorm:"type:varchar(32);not null" json:"id_card_masked"`
|
||||
APICodes APICodeList `gorm:"type:json;not null" json:"api_codes"`
|
||||
Status string `gorm:"type:varchar(20);not null;default:'enabled'" json:"status"`
|
||||
Remark string `gorm:"type:varchar(500)" json:"remark"`
|
||||
CreatedBy *string `gorm:"type:varchar(36)" json:"created_by,omitempty"`
|
||||
UpdatedBy *string `gorm:"type:varchar(36)" json:"updated_by,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
}
|
||||
|
||||
func (QueryWhitelistEntry) TableName() string {
|
||||
return QueryWhitelistTableName
|
||||
}
|
||||
|
||||
func (e *QueryWhitelistEntry) BeforeCreate(tx *gorm.DB) error {
|
||||
if e.ID == "" {
|
||||
e.ID = uuid.New().String()
|
||||
}
|
||||
if e.Status == "" {
|
||||
e.Status = QueryWhitelistStatusEnabled
|
||||
}
|
||||
if e.APICodes == nil {
|
||||
e.APICodes = APICodeList{"*"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *QueryWhitelistEntry) IsGlobal() bool {
|
||||
return e.UserID == QueryWhitelistGlobalUserID
|
||||
}
|
||||
|
||||
func (e *QueryWhitelistEntry) IsEnabled() bool {
|
||||
return e.Status == QueryWhitelistStatusEnabled
|
||||
}
|
||||
|
||||
func (e *QueryWhitelistEntry) MatchesAPICode(apiCode string) bool {
|
||||
for _, code := range e.APICodes {
|
||||
if code == "*" || code == apiCode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *QueryWhitelistEntry) MatchesName(name string) bool {
|
||||
if e.Name == QueryWhitelistWildcardName {
|
||||
return true
|
||||
}
|
||||
return e.Name == name
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"tyapi-server/internal/domains/api/entities"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
type QueryWhitelistRepository interface {
|
||||
Create(ctx context.Context, entry *entities.QueryWhitelistEntry) error
|
||||
Update(ctx context.Context, entry *entities.QueryWhitelistEntry) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
FindByID(ctx context.Context, id string) (*entities.QueryWhitelistEntry, error)
|
||||
FindEnabledByUserIDsAndIDCardHash(ctx context.Context, userIDs []string, idCardHash string) ([]*entities.QueryWhitelistEntry, error)
|
||||
FindAllEnabled(ctx context.Context) ([]*entities.QueryWhitelistEntry, error)
|
||||
List(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) ([]*entities.QueryWhitelistEntry, int64, error)
|
||||
ExistsByUserIDCardHashAndName(ctx context.Context, userID, idCardHash, name string, excludeID string) (bool, error)
|
||||
}
|
||||
@@ -29,6 +29,8 @@ type FormConfig struct {
|
||||
// FormConfigService 表单配置服务接口
|
||||
type FormConfigService interface {
|
||||
GetFormConfig(ctx context.Context, apiCode string) (*FormConfig, error)
|
||||
// RequiresIdentityInput 该 API 入参是否要求身份证(id_card / idCard 为必填)
|
||||
RequiresIdentityInput(ctx context.Context, apiCode string) bool
|
||||
}
|
||||
|
||||
// FormConfigServiceImpl 表单配置服务实现
|
||||
@@ -72,6 +74,35 @@ func (s *FormConfigServiceImpl) GetFormConfig(ctx context.Context, apiCode strin
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// RequiresIdentityInput 判断 API 是否以身份证为必填入参。
|
||||
// api_codes 为 ["*"] 时,仅对此类接口生效;无 id_card 必填字段的接口不会被拦截。
|
||||
func (s *FormConfigServiceImpl) RequiresIdentityInput(ctx context.Context, apiCode string) bool {
|
||||
if len(apiCode) >= 4 && strings.EqualFold(apiCode[:4], "COMB") {
|
||||
// 组合包是否拦截由请求入参是否含 id_card 决定(在 QueryWhitelist 上层已校验)
|
||||
return true
|
||||
}
|
||||
dtoStruct, err := s.getDTOStruct(ctx, apiCode)
|
||||
if err != nil || dtoStruct == nil {
|
||||
return false
|
||||
}
|
||||
return dtoStructRequiresIdentityInput(dtoStruct)
|
||||
}
|
||||
|
||||
func dtoStructRequiresIdentityInput(dtoStruct interface{}) bool {
|
||||
t := reflect.TypeOf(dtoStruct).Elem()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
jsonTag := strings.Split(field.Tag.Get("json"), ",")[0]
|
||||
if jsonTag != "id_card" && jsonTag != "idCard" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(field.Tag.Get("validate"), "required") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getDTOStruct 根据API代码获取对应的DTO结构体
|
||||
func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string) (interface{}, error) {
|
||||
// 建立API代码到DTO结构体的映射
|
||||
|
||||
@@ -22,9 +22,6 @@ func ProcessFLXG0V4BRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
|
||||
body := map[string]string{
|
||||
"name": paramsDto.Name,
|
||||
|
||||
@@ -21,10 +21,6 @@ func ProcessFLXG2E8FRequest(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
|
||||
@@ -20,10 +20,6 @@ func ProcessFLXG3A9BRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
// 去掉司法案件案件去掉身份证号码
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
|
||||
@@ -20,9 +20,6 @@ func ProcessFLXG5A3BRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
|
||||
body := map[string]string{
|
||||
"name": paramsDto.Name,
|
||||
|
||||
@@ -20,9 +20,6 @@ func ProcessFLXG7E8FRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
|
||||
body := map[string]string{
|
||||
"name": paramsDto.Name,
|
||||
|
||||
@@ -20,9 +20,6 @@ func ProcessFLXG9C1DRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
|
||||
@@ -20,9 +20,6 @@ func ProcessFLXGCA3DRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
|
||||
@@ -20,10 +20,6 @@ func ProcessFLXGDEA8Request(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
// 去掉司法案件案件去掉身份证号码
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
|
||||
@@ -20,10 +20,6 @@ func ProcessFLXGDEA9Request(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
body := map[string]string{
|
||||
"name": paramsDto.Name,
|
||||
"idCard": paramsDto.IDCard,
|
||||
|
||||
@@ -25,10 +25,6 @@ func ProcessFLXGHB4FRequest(ctx context.Context, params []byte, deps *processors
|
||||
if deps.HaiyuapiService == nil {
|
||||
return nil, errors.Join(processors.ErrSystem, errors.New("海宇API服务未初始化"))
|
||||
}
|
||||
// 去掉司法案件案件去掉身份证号码
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
|
||||
reqParams := map[string]interface{}{
|
||||
"name": paramsDto.Name,
|
||||
|
||||
@@ -20,10 +20,6 @@ func ProcessFLXGK5D2Request(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
// 去掉司法案件案件去掉身份证号码
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
|
||||
@@ -20,10 +20,6 @@ func ProcessJRZQ8A2DRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
// 去掉司法案件案件去掉身份证号码
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
|
||||
body := map[string]string{
|
||||
"name": paramsDto.Name,
|
||||
@@ -56,7 +52,6 @@ func ProcessJRZQ8A2DRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
|
||||
// respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI018", reqData)
|
||||
@@ -73,6 +68,6 @@ func ProcessJRZQ8A2DRequest(ctx context.Context, params []byte, deps *processors
|
||||
// if err != nil {
|
||||
// return nil, errors.Join(processors.ErrSystem, err)
|
||||
// }
|
||||
|
||||
// return respBytes, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,41 @@
|
||||
package jrzq
|
||||
|
||||
import "strings"
|
||||
|
||||
// mapNuoerLoanRiskTagV23ToResponse 将 nuoer data 转为 JRZQ9D4E 对外结构:
|
||||
// 解包 result,score/reason/contents 原样透传。
|
||||
// 解包 result,score/reason 原样透传,contents 内 TC_ 前缀标签码映射为 BH_。
|
||||
func mapNuoerLoanRiskTagV23ToResponse(data map[string]interface{}) map[string]interface{} {
|
||||
if data == nil {
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
if result, ok := data["result"].(map[string]interface{}); ok {
|
||||
return result
|
||||
result := data
|
||||
if unwrapped, ok := data["result"].(map[string]interface{}); ok {
|
||||
result = unwrapped
|
||||
}
|
||||
return data
|
||||
return mapLoanRiskTagV23ContentsKeys(result)
|
||||
}
|
||||
|
||||
func mapLoanRiskTagV23ContentsKeys(data map[string]interface{}) map[string]interface{} {
|
||||
out := make(map[string]interface{}, len(data))
|
||||
for key, val := range data {
|
||||
if key == "contents" {
|
||||
if contents, ok := val.(map[string]interface{}); ok {
|
||||
out[key] = renameLoanRiskTagTCKeyPrefix(contents)
|
||||
continue
|
||||
}
|
||||
}
|
||||
out[key] = val
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func renameLoanRiskTagTCKeyPrefix(contents map[string]interface{}) map[string]interface{} {
|
||||
out := make(map[string]interface{}, len(contents))
|
||||
for key, val := range contents {
|
||||
if strings.HasPrefix(key, "TC_") {
|
||||
key = "BH_" + key[3:]
|
||||
}
|
||||
out[key] = val
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package jrzq
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMapNuoerLoanRiskTagV23ToResponse_RenamesTCContentsKeys(t *testing.T) {
|
||||
raw := map[string]interface{}{
|
||||
"result": map[string]interface{}{
|
||||
"score": "750",
|
||||
"reason": "ok",
|
||||
"contents": map[string]interface{}{
|
||||
"TC_Q016_q20": "1.5",
|
||||
"TC_A001": "3",
|
||||
"BH_A002": "2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := mapNuoerLoanRiskTagV23ToResponse(raw)
|
||||
|
||||
if got["score"] != "750" {
|
||||
t.Fatalf("score = %v, want 750", got["score"])
|
||||
}
|
||||
contents, ok := got["contents"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("contents type = %T, want map[string]interface{}", got["contents"])
|
||||
}
|
||||
if contents["BH_A001"] != "3" {
|
||||
t.Fatalf("BH_A001 = %v, want 3", contents["BH_A001"])
|
||||
}
|
||||
if contents["BH_A002"] != "2" {
|
||||
t.Fatalf("BH_A002 = %v, want 2", contents["BH_A002"])
|
||||
}
|
||||
if _, exists := contents["TC_Q016_q20"]; exists {
|
||||
t.Fatal("TC_Q016_q20 should be renamed")
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,6 @@ func ProcessJRZQV7MDRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
// 去掉司法案件案件去掉身份证号码
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" || paramsDto.IDCard == "350681198412013041" || paramsDto.IDCard == "33072619741031111X" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
|
||||
body := map[string]string{
|
||||
"name": paramsDto.Name,
|
||||
|
||||
@@ -31,9 +31,6 @@ func ProcessQYGL2S0WRequest(ctx context.Context, params []byte, deps *processors
|
||||
fmt.Print("个人身份证件号不能为空")
|
||||
return nil, fmt.Errorf("%s: %w", processors.ErrInvalidParam, errors.New("当失信被执行人类型为个人时,身份证件号不能为空"))
|
||||
}
|
||||
if paramsDto.IDCard == "410482198504029333" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
} else if paramsDto.Type == "ent" {
|
||||
// 企业查询:name 和 entMark 两者必填其一
|
||||
nameValue = paramsDto.EntName
|
||||
|
||||
65
internal/domains/api/services/query_whitelist_helpers.go
Normal file
65
internal/domains/api/services/query_whitelist_helpers.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NormalizeIDCard 归一化身份证号(去空格、末位 X 大写)
|
||||
func NormalizeIDCard(idCard string) string {
|
||||
idCard = strings.TrimSpace(idCard)
|
||||
return strings.ToUpper(idCard)
|
||||
}
|
||||
|
||||
// HashIDCard 计算身份证号 SHA256 哈希,用于索引与匹配
|
||||
func HashIDCard(idCard string) string {
|
||||
sum := sha256.Sum256([]byte(NormalizeIDCard(idCard)))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// MaskIDCard 脱敏展示:350681********0611
|
||||
func MaskIDCard(idCard string) string {
|
||||
id := NormalizeIDCard(idCard)
|
||||
if len(id) <= 10 {
|
||||
return id
|
||||
}
|
||||
return id[:6] + "********" + id[len(id)-4:]
|
||||
}
|
||||
|
||||
// IdentityParams 从请求参数中提取的身份证与姓名
|
||||
type IdentityParams struct {
|
||||
IDCard string
|
||||
Name string
|
||||
OK bool
|
||||
}
|
||||
|
||||
// ExtractIdentityParams 从解密后的 params map 提取 id_card + name
|
||||
func ExtractIdentityParams(params map[string]interface{}) IdentityParams {
|
||||
idCard := firstNonEmptyString(params, "id_card", "idCard")
|
||||
name := firstNonEmptyString(params, "name")
|
||||
if idCard == "" {
|
||||
return IdentityParams{OK: false}
|
||||
}
|
||||
return IdentityParams{
|
||||
IDCard: NormalizeIDCard(idCard),
|
||||
Name: strings.TrimSpace(name),
|
||||
OK: true,
|
||||
}
|
||||
}
|
||||
|
||||
func firstNonEmptyString(params map[string]interface{}, keys ...string) string {
|
||||
for _, key := range keys {
|
||||
v, ok := params[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if s, ok := v.(string); ok {
|
||||
s = strings.TrimSpace(s)
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"tyapi-server/internal/domains/api/entities"
|
||||
)
|
||||
|
||||
func TestNormalizeIDCard(t *testing.T) {
|
||||
if got := NormalizeIDCard("13032319930128263x"); got != "13032319930128263X" {
|
||||
t.Fatalf("expected uppercase X, got %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaskIDCard(t *testing.T) {
|
||||
masked := MaskIDCard("350681198611130611")
|
||||
if masked != "350681********0611" {
|
||||
t.Fatalf("unexpected mask: %s", masked)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractIdentityParams(t *testing.T) {
|
||||
params := map[string]interface{}{
|
||||
"id_card": "350681198611130611",
|
||||
"name": "张三",
|
||||
}
|
||||
identity := ExtractIdentityParams(params)
|
||||
if !identity.OK || identity.Name != "张三" {
|
||||
t.Fatalf("unexpected identity: %+v", identity)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryWhitelistEntry_Matches(t *testing.T) {
|
||||
entry := &entities.QueryWhitelistEntry{
|
||||
UserID: entities.QueryWhitelistGlobalUserID,
|
||||
Name: entities.QueryWhitelistWildcardName,
|
||||
APICodes: entities.APICodeList{"FLXG0V4B"},
|
||||
Status: entities.QueryWhitelistStatusEnabled,
|
||||
}
|
||||
|
||||
if !entry.MatchesAPICode("FLXG0V4B") {
|
||||
t.Fatal("should match api code")
|
||||
}
|
||||
if entry.MatchesAPICode("JRZQ8A2D") {
|
||||
t.Fatal("should not match other api code")
|
||||
}
|
||||
if !entry.MatchesName("任意姓名") {
|
||||
t.Fatal("wildcard name should match any name")
|
||||
}
|
||||
|
||||
strict := &entities.QueryWhitelistEntry{
|
||||
Name: "李四",
|
||||
}
|
||||
if strict.MatchesName("李四") == false || strict.MatchesName("张三") {
|
||||
t.Fatal("strict name matching failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryWhitelistEntry_GlobalWildcardAPICodes(t *testing.T) {
|
||||
entry := &entities.QueryWhitelistEntry{
|
||||
APICodes: entities.APICodeList{"*"},
|
||||
}
|
||||
if !entry.MatchesAPICode("ANY_CODE") {
|
||||
t.Fatal("* should match any api code")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"tyapi-server/internal/domains/api/entities"
|
||||
)
|
||||
|
||||
func TestRequiresIdentityInput_FLXG0V4B(t *testing.T) {
|
||||
svc := NewFormConfigServiceWithoutDependencies()
|
||||
if !svc.RequiresIdentityInput(context.Background(), "FLXG0V4B") {
|
||||
t.Fatal("FLXG0V4B should require id_card")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequiresIdentityInput_EnterpriseAPI(t *testing.T) {
|
||||
svc := NewFormConfigServiceWithoutDependencies()
|
||||
// QYGL8261 等企业类接口通常不要求 id_card 必填
|
||||
if svc.RequiresIdentityInput(context.Background(), "QYGL8261") {
|
||||
t.Fatal("QYGL8261 should not require id_card")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequiresIdentityInput_COMB(t *testing.T) {
|
||||
svc := NewFormConfigServiceWithoutDependencies()
|
||||
if !svc.RequiresIdentityInput(context.Background(), "COMBXXXX") {
|
||||
t.Fatal("COMB should be eligible when params contain id_card")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldReturnEmpty_SkipsNonIdentityAPIEvenWithWildcard(t *testing.T) {
|
||||
idCard := "350681198611130611"
|
||||
hash := HashIDCard(idCard)
|
||||
svc := newTestQueryWhitelistService(&mockQueryWhitelistRepo{
|
||||
entries: []*entities.QueryWhitelistEntry{
|
||||
{
|
||||
ID: "1",
|
||||
UserID: entities.QueryWhitelistGlobalUserID,
|
||||
Name: entities.QueryWhitelistWildcardName,
|
||||
IDCardHash: hash,
|
||||
APICodes: entities.APICodeList{"*"},
|
||||
Status: entities.QueryWhitelistStatusEnabled,
|
||||
},
|
||||
},
|
||||
}, false)
|
||||
|
||||
params := map[string]interface{}{"id_card": idCard, "name": "张三"}
|
||||
if svc.ShouldReturnEmpty(context.Background(), "user-a", "QYGL8261", params) {
|
||||
t.Fatal("non-identity API should not be intercepted even with api_codes=*")
|
||||
}
|
||||
}
|
||||
185
internal/domains/api/services/query_whitelist_service.go
Normal file
185
internal/domains/api/services/query_whitelist_service.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/domains/api/entities"
|
||||
"tyapi-server/internal/domains/api/repositories"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type QueryWhitelistService interface {
|
||||
ShouldReturnEmpty(ctx context.Context, userID, apiCode string, params map[string]interface{}) bool
|
||||
InvalidateCache(userID, idCardHash string)
|
||||
InvalidateAllCache()
|
||||
}
|
||||
|
||||
// queryWhitelistSnapshot 全量 enabled 规则快照,按 id_card_hash 索引,热路径只读内存。
|
||||
type queryWhitelistSnapshot struct {
|
||||
byHash map[string][]*entities.QueryWhitelistEntry
|
||||
}
|
||||
|
||||
type QueryWhitelistServiceImpl struct {
|
||||
repo repositories.QueryWhitelistRepository
|
||||
formConfigService FormConfigService
|
||||
logger *zap.Logger
|
||||
|
||||
snapshot atomic.Pointer[queryWhitelistSnapshot]
|
||||
snapshotMu sync.Mutex
|
||||
|
||||
// apiCode -> 是否要求身份证入参(FormConfig 反射结果,进程内永久缓存)
|
||||
identityAPICache sync.Map
|
||||
}
|
||||
|
||||
func NewQueryWhitelistService(
|
||||
repo repositories.QueryWhitelistRepository,
|
||||
formConfigService FormConfigService,
|
||||
logger *zap.Logger,
|
||||
) QueryWhitelistService {
|
||||
s := &QueryWhitelistServiceImpl{
|
||||
repo: repo,
|
||||
formConfigService: formConfigService,
|
||||
logger: logger,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ShouldReturnEmpty 检查是否应返回「查询为空」。
|
||||
// 热路径:入参提取 → API 类型缓存 → 内存快照匹配,不逐请求查库。
|
||||
func (s *QueryWhitelistServiceImpl) ShouldReturnEmpty(
|
||||
ctx context.Context,
|
||||
userID, apiCode string,
|
||||
params map[string]interface{},
|
||||
) bool {
|
||||
identity := ExtractIdentityParams(params)
|
||||
if !identity.OK {
|
||||
return false
|
||||
}
|
||||
if !s.requiresIdentityInput(ctx, apiCode) {
|
||||
return false
|
||||
}
|
||||
|
||||
idCardHash := HashIDCard(identity.IDCard)
|
||||
entries, err := s.lookupEntries(ctx, userID, idCardHash)
|
||||
if err != nil {
|
||||
s.logger.Error("查询白名单快照失败", zap.Error(err), zap.String("user_id", userID))
|
||||
return false
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if !entry.IsEnabled() {
|
||||
continue
|
||||
}
|
||||
if !entry.MatchesAPICode(apiCode) {
|
||||
continue
|
||||
}
|
||||
if !entry.MatchesName(identity.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
s.logger.Info("命中查询白名单",
|
||||
zap.String("user_id", userID),
|
||||
zap.String("api_code", apiCode),
|
||||
zap.String("whitelist_id", entry.ID),
|
||||
zap.Bool("is_global", entry.IsGlobal()),
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *QueryWhitelistServiceImpl) requiresIdentityInput(ctx context.Context, apiCode string) bool {
|
||||
if s.formConfigService == nil {
|
||||
return false
|
||||
}
|
||||
if cached, ok := s.identityAPICache.Load(apiCode); ok {
|
||||
return cached.(bool)
|
||||
}
|
||||
result := s.formConfigService.RequiresIdentityInput(ctx, apiCode)
|
||||
s.identityAPICache.Store(apiCode, result)
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *QueryWhitelistServiceImpl) lookupEntries(ctx context.Context, userID, idCardHash string) ([]*entities.QueryWhitelistEntry, error) {
|
||||
snap, err := s.getSnapshot(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
candidates := snap.byHash[idCardHash]
|
||||
if len(candidates) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
result := make([]*entities.QueryWhitelistEntry, 0, len(candidates))
|
||||
for _, entry := range candidates {
|
||||
if entry.UserID == userID || entry.UserID == entities.QueryWhitelistGlobalUserID {
|
||||
result = append(result, entry)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *QueryWhitelistServiceImpl) getSnapshot(ctx context.Context) (*queryWhitelistSnapshot, error) {
|
||||
if snap := s.snapshot.Load(); snap != nil {
|
||||
return snap, nil
|
||||
}
|
||||
return s.reloadSnapshot(ctx)
|
||||
}
|
||||
|
||||
func (s *QueryWhitelistServiceImpl) reloadSnapshot(ctx context.Context) (*queryWhitelistSnapshot, error) {
|
||||
s.snapshotMu.Lock()
|
||||
defer s.snapshotMu.Unlock()
|
||||
|
||||
if snap := s.snapshot.Load(); snap != nil {
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
entries, err := s.repo.FindAllEnabled(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
byHash := make(map[string][]*entities.QueryWhitelistEntry, len(entries))
|
||||
for _, entry := range entries {
|
||||
byHash[entry.IDCardHash] = append(byHash[entry.IDCardHash], entry)
|
||||
}
|
||||
|
||||
snap := &queryWhitelistSnapshot{byHash: byHash}
|
||||
s.snapshot.Store(snap)
|
||||
s.logger.Info("查询白名单快照已加载", zap.Int("entries", len(entries)), zap.Int("hash_buckets", len(byHash)))
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
// refreshSnapshotAsync 管理端变更后异步刷新,避免阻塞写请求。
|
||||
func (s *QueryWhitelistServiceImpl) refreshSnapshotAsync() {
|
||||
go func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
s.snapshotMu.Lock()
|
||||
defer s.snapshotMu.Unlock()
|
||||
s.snapshot.Store(nil)
|
||||
|
||||
entries, err := s.repo.FindAllEnabled(ctx)
|
||||
if err != nil {
|
||||
s.logger.Error("刷新查询白名单快照失败", zap.Error(err))
|
||||
return
|
||||
}
|
||||
byHash := make(map[string][]*entities.QueryWhitelistEntry, len(entries))
|
||||
for _, entry := range entries {
|
||||
byHash[entry.IDCardHash] = append(byHash[entry.IDCardHash], entry)
|
||||
}
|
||||
s.snapshot.Store(&queryWhitelistSnapshot{byHash: byHash})
|
||||
s.logger.Info("查询白名单快照已刷新", zap.Int("entries", len(entries)))
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *QueryWhitelistServiceImpl) InvalidateCache(_ string, _ string) {
|
||||
s.refreshSnapshotAsync()
|
||||
}
|
||||
|
||||
func (s *QueryWhitelistServiceImpl) InvalidateAllCache() {
|
||||
s.refreshSnapshotAsync()
|
||||
}
|
||||
147
internal/domains/api/services/query_whitelist_service_test.go
Normal file
147
internal/domains/api/services/query_whitelist_service_test.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"tyapi-server/internal/domains/api/entities"
|
||||
"tyapi-server/internal/domains/api/repositories"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type mockQueryWhitelistRepo struct {
|
||||
entries []*entities.QueryWhitelistEntry
|
||||
}
|
||||
|
||||
func (m *mockQueryWhitelistRepo) Create(ctx context.Context, entry *entities.QueryWhitelistEntry) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockQueryWhitelistRepo) Update(ctx context.Context, entry *entities.QueryWhitelistEntry) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockQueryWhitelistRepo) Delete(ctx context.Context, id string) error { return nil }
|
||||
func (m *mockQueryWhitelistRepo) FindByID(ctx context.Context, id string) (*entities.QueryWhitelistEntry, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockQueryWhitelistRepo) List(ctx context.Context, filters map[string]interface{}, options interfaces.ListOptions) ([]*entities.QueryWhitelistEntry, int64, error) {
|
||||
return nil, 0, nil
|
||||
}
|
||||
func (m *mockQueryWhitelistRepo) ExistsByUserIDCardHashAndName(ctx context.Context, userID, idCardHash, name, excludeID string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (m *mockQueryWhitelistRepo) FindEnabledByUserIDsAndIDCardHash(ctx context.Context, userIDs []string, idCardHash string) ([]*entities.QueryWhitelistEntry, error) {
|
||||
var result []*entities.QueryWhitelistEntry
|
||||
for _, entry := range m.entries {
|
||||
if entry.IDCardHash != idCardHash || !entry.IsEnabled() {
|
||||
continue
|
||||
}
|
||||
for _, uid := range userIDs {
|
||||
if entry.UserID == uid {
|
||||
result = append(result, entry)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *mockQueryWhitelistRepo) FindAllEnabled(ctx context.Context) ([]*entities.QueryWhitelistEntry, error) {
|
||||
var result []*entities.QueryWhitelistEntry
|
||||
for _, entry := range m.entries {
|
||||
if entry.IsEnabled() {
|
||||
result = append(result, entry)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type mockFormConfigService struct {
|
||||
requiresIdentity bool
|
||||
}
|
||||
|
||||
func (m *mockFormConfigService) GetFormConfig(ctx context.Context, apiCode string) (*FormConfig, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *mockFormConfigService) RequiresIdentityInput(ctx context.Context, apiCode string) bool {
|
||||
return m.requiresIdentity
|
||||
}
|
||||
|
||||
func newTestQueryWhitelistService(repo repositories.QueryWhitelistRepository, requiresIdentity bool) QueryWhitelistService {
|
||||
return NewQueryWhitelistService(repo, &mockFormConfigService{requiresIdentity: requiresIdentity}, zap.NewNop())
|
||||
}
|
||||
|
||||
func TestShouldReturnEmpty_GlobalRule(t *testing.T) {
|
||||
idCard := "350681198611130611"
|
||||
hash := HashIDCard(idCard)
|
||||
svc := newTestQueryWhitelistService(&mockQueryWhitelistRepo{
|
||||
entries: []*entities.QueryWhitelistEntry{
|
||||
{
|
||||
ID: "1",
|
||||
UserID: entities.QueryWhitelistGlobalUserID,
|
||||
Name: entities.QueryWhitelistWildcardName,
|
||||
IDCardHash: hash,
|
||||
APICodes: entities.APICodeList{"*"},
|
||||
Status: entities.QueryWhitelistStatusEnabled,
|
||||
},
|
||||
},
|
||||
}, true)
|
||||
|
||||
params := map[string]interface{}{"id_card": idCard, "name": "任意姓名"}
|
||||
if !svc.ShouldReturnEmpty(context.Background(), "user-a", "FLXG0V4B", params) {
|
||||
t.Fatal("global rule should hit for any user")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldReturnEmpty_UserSpecificRule(t *testing.T) {
|
||||
idCard := "350681198611130611"
|
||||
hash := HashIDCard(idCard)
|
||||
svc := newTestQueryWhitelistService(&mockQueryWhitelistRepo{
|
||||
entries: []*entities.QueryWhitelistEntry{
|
||||
{
|
||||
ID: "2",
|
||||
UserID: "user-a",
|
||||
Name: "张三",
|
||||
IDCardHash: hash,
|
||||
APICodes: entities.APICodeList{"FLXG0V4B"},
|
||||
Status: entities.QueryWhitelistStatusEnabled,
|
||||
},
|
||||
},
|
||||
}, true)
|
||||
|
||||
params := map[string]interface{}{"id_card": idCard, "name": "张三"}
|
||||
if !svc.ShouldReturnEmpty(context.Background(), "user-a", "FLXG0V4B", params) {
|
||||
t.Fatal("user-a should hit")
|
||||
}
|
||||
if svc.ShouldReturnEmpty(context.Background(), "user-b", "FLXG0V4B", params) {
|
||||
t.Fatal("user-b should not hit user-a rule")
|
||||
}
|
||||
if svc.ShouldReturnEmpty(context.Background(), "user-a", "JRZQ8A2D", params) {
|
||||
t.Fatal("wrong api code should not hit")
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldReturnEmpty_NameMismatch(t *testing.T) {
|
||||
idCard := "350681198611130611"
|
||||
hash := HashIDCard(idCard)
|
||||
svc := newTestQueryWhitelistService(&mockQueryWhitelistRepo{
|
||||
entries: []*entities.QueryWhitelistEntry{
|
||||
{
|
||||
ID: "3",
|
||||
UserID: "user-a",
|
||||
Name: "张三",
|
||||
IDCardHash: hash,
|
||||
APICodes: entities.APICodeList{"*"},
|
||||
Status: entities.QueryWhitelistStatusEnabled,
|
||||
},
|
||||
},
|
||||
}, true)
|
||||
|
||||
params := map[string]interface{}{"id_card": idCard, "name": "李四"}
|
||||
if svc.ShouldReturnEmpty(context.Background(), "user-a", "FLXG0V4B", params) {
|
||||
t.Fatal("name mismatch should not hit")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user