# 地域限制机制

<cite>
**本文档引用文件**  
- [province.go](file://api/restriction/v1/province.go)
- [collection.go](file://api/restriction/v1/collection.go)
- [restriction_v1_query_all_province.go](file://internal/controller/restriction/restriction_v1_query_all_province.go)
- [restriction_v1_check_ip_allowed.go](file://internal/controller/restriction/restriction_v1_check_ip_allowed.go)
- [restriction.go](file://internal/logic/restriction/restriction.go)
- [location.go](file://utility/integration/restriction/location.go)
- [consts.go](file://utility/integration/restriction/consts.go)
- [impl.go](file://utility/integration/restriction/impl.go)
- [models.go](file://utility/integration/restriction/models.go)
</cite>

## 目录
1. [引言](#引言)
2. [核心数据结构与接口定义](#核心数据结构与接口定义)
3. [地理位置查询机制](#地理位置查询机制)
4. [省份限制规则配置与管理](#省份限制规则配置与管理)
5. [地域限制查询接口](#地域限制查询接口)
6. [IP地址解析与匹配流程](#ip地址解析与匹配流程)
7. [生效流程与执行逻辑](#生效流程与执行逻辑)
8. [与其他风控措施的协同机制](#与其他风控措施的协同机制)
9. [最佳实践与应用场景](#最佳实践与应用场景)
10. [总结](#总结)

## 引言
地域限制机制是kami_backend系统中用于实现基于地理位置访问控制的核心风控模块。该机制通过解析用户IP地址获取其地理位置信息，并依据预设的省份限制规则判断是否允许访问特定服务。本机制广泛应用于区域化服务控制、合规性要求满足等场景，有效防止跨区域非法操作，提升系统安全性和业务合规性。

## 核心数据结构与接口定义

### 请求与响应结构
系统定义了标准化的API请求与响应结构，用于地域限制相关操作。

```mermaid
classDiagram
class QueryAllProvinceReq {
+string path : /restriction/location/getAllProvince
+string method : GET
+string summary : 获取所有的地理位置
}
class QueryAllProvinceRes {
+[]string List
}
class CheckIPAllowedReq {
+string Ip
+string DeviceId
+uint MerchantDeployID
+string OrderNo
+string CardPass
}
class CheckIPAllowedRes {
+bool IsAllowed
}
QueryAllProvinceReq --> QueryAllProvinceRes : "响应"
CheckIPAllowedReq --> CheckIPAllowedRes : "响应"
```

**图示来源**  
- [province.go](file://api/restriction/v1/province.go#L1-L28)

**本节来源**  
- [province.go](file://api/restriction/v1/province.go#L1-L28)

## 地理位置查询机制

### 多源IP定位服务
系统集成多个第三方IP地理位置查询服务，确保定位结果的准确性与可靠性。采用随机轮询策略调用不同服务商接口，并在失败时自动重试。

```mermaid
sequenceDiagram
participant Client as "客户端"
participant Service as "GetLocationByIP"
participant Provider as "第三方服务商"
Client->>Service : QueryLocationByIP(ctx, ip)
Service->>Service : 随机选择实现
loop 所有服务商
Service->>Provider : 调用QueryLocationByIP
Provider-->>Service : 返回地理位置
alt 城市信息存在
Service-->>Client : 返回结果
break 成功
end
end
alt 所有调用失败
Service-->>Client : 抛出“ip location not found”错误
end
```

**图示来源**  
- [location.go](file://utility/integration/restriction/location.go#L0-L38)
- [impl.go](file://utility/integration/restriction/impl.go#L0-L239)

**本节来源**  
- [location.go](file://utility/integration/restriction/location.go#L0-L38)
- [impl.go](file://utility/integration/restriction/impl.go#L0-L239)

### 支持的服务商列表
系统当前支持以下IP地理位置查询服务商：
- CSDN
- PCOnline
- OlTools
- IdCd
- MeiTu
- Vo
- DBIP
- QJQQ
- IQIYI
- IP66

每个服务商通过独立的客户端实现`Location`接口，统一由工厂模式管理。

## 省份限制规则配置与管理

### 全国省份列表
系统内置完整的中国省级行政区划列表，包含34个省级单位，涵盖大陆、港澳台及“海外”特殊标识。

```go
var AllProvinceList = []string{
    "北京市", "天津市", "河北省", "山西省", "内蒙古自治区", 
    "辽宁省", "吉林省", "黑龙江省", "上海市", "江苏省", 
    "浙江省", "安徽省", "福建省", "江西省", "山东省", 
    "河南省", "湖北省", "湖南省", "广东省", "广西壮族自治区", 
    "海南省", "重庆市", "四川省", "贵州省", "云南省", 
    "西藏自治区", "陕西省", "甘肃省", "青海省", "宁夏回族自治区", 
    "新疆维吾尔自治区", "香港特别行政区", "澳门特别行政区", "台湾省", "海外",
}
```

**本节来源**  
- [consts.go](file://utility/integration/restriction/consts.go#L0-L24)

### 限制策略配置
省份限制规则作为商户部署策略的一部分进行配置，由`MerchantDeployInfo`服务管理。限制策略包含以下字段：
- `IsRestrictIp`: 是否启用IP地域限制
- `RestrictArea`: 限制的省份列表
- `IsRestrictInternalIp`: 是否限制内网IP
- 其他关联风控规则（设备、卡密、代理等）

## 地域限制查询接口

### 获取全部省份接口
提供RESTful API供前端或外部系统获取系统支持的所有省份列表。

```mermaid
flowchart TD
A[HTTP GET请求] --> B[/restriction/location/getAllProvince]
B --> C{调用QueryAllProvince}
C --> D[service.Restriction().GetAllProvince(ctx)]
D --> E[返回省份列表]
E --> F[HTTP 200 OK + 省份数组]
```

**图示来源**  
- [restriction_v1_query_all_province.go](file://internal/controller/restriction/restriction_v1_query_all_province.go#L0-L15)
- [restriction.go](file://internal/logic/restriction/restriction.go#L0-L173)

**本节来源**  
- [restriction_v1_query_all_province.go](file://internal/controller/restriction/restriction_v1_query_all_province.go#L0-L15)

### 检查IP是否允许接口
核心接口，用于在业务流程中实时判断某IP是否被允许访问。

```mermaid
flowchart TD
A[HTTP GET请求] --> B[/restriction/location/checkIPAllowed]
B --> C{调用CheckIPAllowed}
C --> D[获取商户部署限制策略]
D --> E{策略存在?}
E --> |否| F[返回内部错误]
E --> |是| G{各项限制条件检查}
G --> H[卡密限制]
G --> I[设备限制]
G --> J[代理限制]
G --> K[IP黑名单]
G --> L[内网IP处理]
G --> M[省份区域限制]
H --> N{是否触发限制?}
I --> N
J --> N
K --> N
L --> N
M --> N
N --> |是| O[IsAllowed = false]
N --> |否| P[IsAllowed = true]
P --> Q[记录访问日志]
O --> Q
Q --> R[返回响应]
```

**图示来源**  
- [restriction_v1_check_ip_allowed.go](file://internal/controller/restriction/restriction_v1_check_ip_allowed.go#L0-L69)

**本节来源**  
- [restriction_v1_check_ip_allowed.go](file://internal/controller/restriction/restriction_v1_check_ip_allowed.go#L0-L69)

## IP地址解析与匹配流程

### IP地理位置解析流程
系统通过`GetLocationByIP`函数获取IP对应的地理位置信息，该函数会：
1. 随机选择一个服务商客户端
2. 发起HTTP请求查询IP信息
3. 解析返回数据并标准化为统一`Response`结构
4. 若城市为空则视为查询失败并重试其他服务商

### 中英文省份匹配
为兼容可能返回英文省份名称的服务商，系统实现拼音转换匹配机制：

```go
// 当IP返回英文省份时
if validator.IsAlpha(ipRecord.IpProvince) {
    ipRecord.IpProvince = gstr.ToLower(ipRecord.IpProvince)
    // 将待查省份转为拼音小写
    slice.ForEach(provinceCopier, func(index int, item string) {
        provinceCopier[index] = gstr.ToLower(pinyin.NewDict().Convert(item, "").None())
    })
    // 将全部省份列表转为拼音小写
    slice.ForEachConcurrent(allProvinceListCopier, func(index int, item string) {
        allProvinceListCopier[index] = gstr.ToLower(pinyin.NewDict().Convert(item, "").None())
    }, 5)
}
```

**本节来源**  
- [restriction.go](file://internal/logic/restriction/restriction.go#L85-L112)

### 海外地区特殊处理
系统对“海外”这一特殊区域进行专门判断：
- 若限制规则包含“海外”
- 且IP所在省份不在中国34个省级单位内
- 则判定为属于限制区域，返回`true`

## 生效流程与执行逻辑

### 完整判断流程
当调用`CheckIPIsAllowed`时，系统执行以下逻辑：
1. 调用`IsInProvince`判断IP是否在指定省份内
2. 若**不在**指定省份内，则`IsAllowed = true`（白名单逻辑）
3. 否则`IsAllowed = false`

### 访问记录持久化
每次检查后，系统通过`defer`机制自动记录本次访问结果到数据库：
- 写入`v1_restrict_ip_order_access`表
- 关联IP记录、设备ID、订单号、卡密等信息
- 更新订单封单状态

该机制确保所有访问行为可追溯，便于后续审计与分析。

**本节来源**  
- [restriction_v1_check_ip_allowed.go](file://internal/controller/restriction/restriction_v1_check_ip_allowed.go#L0-L69)
- [restriction.go](file://internal/logic/restriction/restriction.go#L0-L173)

## 与其他风控措施的协同机制

### 综合风控策略执行
地域限制并非孤立运行，而是作为综合风控策略的一部分，与其他限制条件共同决策：

```mermaid
graph TD
A[开始检查] --> B{卡密限制启用?}
B --> |是| C{卡密已限制?}
C --> |是| D[拒绝访问]
B --> |否| E{设备限制启用?}
E --> |是| F{设备已限制?}
F --> |是| D
E --> |否| G{代理限制启用?}
G --> |是| H{使用代理?}
H --> |是| D
G --> |否| I{IP限制启用?}
I --> |是| J{IP在黑名单?}
J --> |是| D
I --> |否| K{内网IP?}
K --> |是| L{限制内网IP?}
L --> |否| M[允许访问]
K --> |否| N{省份限制启用?}
N --> |是| O{IP在允许区域?}
O --> |是| M
O --> |否| D
N --> |否| M
D --> P[记录日志]
M --> P
P --> Q[返回结果]
```

**图示来源**  
- [restriction_v1_check_ip_allowed.go](file://internal/controller/restriction/restriction_v1_check_ip_allowed.go#L0-L69)

**本节来源**  
- [restriction_v1_check_ip_allowed.go](file://internal/controller/restriction/restriction_v1_check_ip_allowed.go#L0-L69)

### 协同工作特点
- **短路判断**：任一条件触发即拒绝，提高效率
- **统一入口**：所有风控由`CheckIPAllowed`统一调度
- **数据联动**：共享限制状态与访问记录
- **策略可配置**：各风控开关独立配置，灵活组合

## 最佳实践与应用场景

### 区域化服务控制
- **场景**：某商户仅允许江苏省内用户购买
- **配置**：`RestrictArea = ["江苏省"]`, `IsRestrictIp = true`
- **效果**：非江苏IP用户提交订单时被拒绝

### 合规性要求满足
- **场景**：金融类产品需遵守属地监管要求
- **配置**：按监管区域设置允许省份列表
- **效果**：确保服务仅对合规区域用户提供

### 防止跨区套利
- **场景**：不同地区定价策略不同，防止用户跨区购买
- **配置**：结合卡密限制与地域限制
- **效果**：同一卡密只能在固定区域使用

### 内网访问策略
- **场景**：测试环境允许内网访问，生产环境严格限制
- **配置**：`IsRestrictInternalIp = false`
- **效果**：内网IP自动放行，无需配置具体省份

## 总结
kami_backend的地域限制机制通过多源IP定位、灵活的省份规则配置、高效的匹配算法以及与其他风控措施的深度协同，构建了一套完整可靠的基于地理位置的访问控制系统。该机制不仅满足了基本的区域访问控制需求，还通过精细化的策略配置支持了多样化的业务场景，为系统的安全性与合规性提供了有力保障。