# GVS-DSDK — GVS Distributed SDK

> **薄封装层**：直接包装 `gvsds/apps/msync` 原生层，作为子服务器（如 `ATestCServer`）和主服务器（`gvsds`）之间共享的通信桥梁。
>
> **设计原则**：所有方法名 1:1 对应原生层 PascalCase；零业务逻辑；纯转发。
>
> **双端版本严格一致**：主从两端必须使用同一 SDK 版本（当前 `1.0.0`），通过 `pyproject.toml` 锁定。

---

## 1. 简介

`gvsdsdk` 把 `gvsds/apps/msync` 的**所有公共类/函数**原样转发导出，使得：

- **主服务器**（`gvsds`）：可继续使用自己的 `gvsds.apps.msync` 模块
- **子服务器**（`ATestCServer`）：可从 `gvsdsdk` 导入**完全相同的 API**，但**不依赖 gvsds 模型层**
- **第三方服务**：可以独立引入 `gvsdsdk` 即可使用全部 MSYNC 能力

被转发的原生模块：

| SDK 模块 | 包装的原生模块 | 提供的类/函数 |
|---|---|---|
| `gvsdsdk.auth` | `gvsds.apps.msync.auth` | `SignPayload`, `GenerateChallenge`, `BuildAuthPayload`, `AuthenticateService`, `ServiceTokenEncoder`, … |
| `gvsdsdk.registry` | `gvsds.apps.msync.registry` | `ServiceRegistry` |
| `gvsdsdk.ogm` | `gvsds.apps.msync.ogm` | `OGMManager` |
| `gvsdsdk.transaction` | `gvsds.apps.msync.transaction` | `TransactionManager` |
| `gvsdsdk.local_tx` | `gvsds.apps.msync.local_tx` | `LocalTransactionManager`, `StepHandler`, `local_tx` |
| `gvsdsdk.client` | `gvsds.apps.msync.client` | `ServiceClient` |

每个类在 SDK 与原生层是**完全同一个对象**（`is` 比较为 `True`），无任何运行时包装开销。

## 2. 安装

子服务器（如 ATestCServer）项目根目录的 `manage.py` 已自动添加 `gvsdsdk` 的父目录到 `sys.path`：

```python
# manage.py:5
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
```

无需额外配置。

**独立使用**（非 gvsds 生态项目）：

```bash
$env:PYTHONPATH = "D:\SMANAGE\gvsdsdk"
# 或 Linux/macOS
export PYTHONPATH=/path/to/SMANAGE/gvsdsdk
```

依赖：
- **核心**：`gvsds.apps.msync`（位于 `gvsds/` 项目内）
- **Python**：仅使用标准库

## 3. 目录结构

```
gvsdsdk/
├── __init__.py         # 公共导出（re-export）
├── auth.py             # → gvsds.apps.msync.auth
├── registry.py         # → gvsds.apps.msync.registry
├── ogm.py              # → gvsds.apps.msync.ogm
├── transaction.py      # → gvsds.apps.msync.transaction
├── local_tx.py         # → gvsds.apps.msync.local_tx
└── client.py           # → gvsds.apps.msync.client
```

每个文件只有 5-25 行代码，纯粹是 `from gvsds.apps.msync.X import Y` 的重新导出。

## 4. 核心 API（PascalCase）

### 4.1 认证 (gvsdsdk.auth)

| 函数 / 类 | 描述 |
|---|---|
| `SignPayload(payload, secret)` | HMAC-SHA256 签名 |
| `VerifySignature(payload, signature, secret)` | 验签 |
| `GenerateChallenge()` | 生成挑战字符串 |
| `BuildAuthPayload(service, challenge, ts=None)` | 构造认证载荷 |
| `SignAuthPayload(payload, secret)` | 一站式签名 |
| `VerifyAuthPayload(payload, signature, secret)` | 一站式验签 |
| `ServiceTokenEncoder` | JWT 风格令牌编解码 |
| `IssueServiceToken(service_node, ...)` | 签发令牌 |
| `AuthenticateService(service_name, signed_payload, signature)` | 完整认证 |
| `ValidateAccessToken(access_token)` | 校验访问令牌 |
| `RefreshServiceToken(refresh_token)` | 刷新令牌 |

### 4.2 注册中心 (gvsdsdk.registry)

```python
from gvsdsdk import ServiceRegistry

node = ServiceRegistry.Register(
    Name='my-service',
    Domain='http://127.0.0.1:8080',
    Secret='shared-secret',
    Scope=['ogm:read', 'ogm:write'],
    ServiceType=0,                       # 0=Worker, 1=Master
    Metadata={'region': 'cn-east'},
)

ServiceRegistry.Heartbeat('my-service')
ServiceRegistry.Discover('my-service')
ServiceRegistry.DiscoverAll(ActiveOnly=True)
ServiceRegistry.UpdateScope('my-service', ['ogm:read'])
ServiceRegistry.RotateSecret('my-service', 'new-secret')
ServiceRegistry.Deregister('my-service')
```

### 4.3 OGM (gvsdsdk.ogm)

```python
from gvsdsdk import OGMManager

entry = OGMManager.Register(
    ObjectType='user',
    LocalID='123',
    ServiceName='my-service',
    GlobalID=None,                       # 可选，自动生成
    Metadata={'username': 'tom'},
)

info = OGMManager.Resolve(entry['GlobalID'])
local = OGMManager.ResolveLocal('user', '123', 'my-service')
by_type = OGMManager.QueryByType('user')
by_svc = OGMManager.QueryByService('my-service')
OGMManager.Update(entry['GlobalID'], metadata={'vip': True})
OGMManager.Deregister(entry['GlobalID'])
```

### 4.4 分布式事务 (gvsdsdk.transaction)

```python
from gvsdsdk import TransactionManager

dtx = TransactionManager.Create(
    Name='create-order',
    StepsData=[
        {'ServiceName': 'svc-a', 'StepName': 'create_user',  'Payload': {...}},
        {'ServiceName': 'svc-b', 'StepName': 'create_wallet', 'Payload': {...}},
    ],
    Description='创建订单事务',
    TransactionType='saga',
    InitiatorServiceName='my-service',
    TimeoutSeconds=300,
)

TransactionManager.Execute(dtx.TransactionCode, access_token=token)
TransactionManager.Compensate(dtx.TransactionCode)
TransactionManager.Cancel(dtx.TransactionCode)
TransactionManager.GetDetail(dtx.TransactionCode)
TransactionManager.RetryStep(dtx.TransactionCode, step_index=0)
TransactionManager.CheckTimeouts()
TransactionManager.QueryByStatus(status=2)  # Committed
```

### 4.5 本地事务 (gvsdsdk.local_tx)

```python
from gvsdsdk import local_tx

def create_user(payload):
    user_id = do_something(payload)
    return {'UserID': user_id}

def delete_user(payload, result):
    undo_something(result['UserID'])

local_tx.register('create_user', action=create_user, compensate=delete_user)
```

### 4.6 服务客户端 (gvsdsdk.client)

```python
from gvsdsdk import ServiceClient

client = ServiceClient(
    ServiceName='my-service',
    ServiceSecret='shared-secret',
    MasterURL='https://api.gvsds.com',
)

client.Authenticate()
info = client.DiscoverService('gerp')
ogm = client.ResolveOGM('user:abc123')
resp = client.Request('GET', 'https://gerp.gvsds.com/api/x', scope=['ogm:read'])
```

## 5. Socket.IO 事件协议（PascalCase）

> **重要**：所有事件的请求/响应字段全部使用 **PascalCase**。

| 事件 | 请求字段 | 响应字段 |
|---|---|---|
| `msync:register` | `ServiceName`, `Domain`, `Secret`, `Scope`, `ServiceType`, `Metadata` | `ServiceNode` 序列化 |
| `msync:auth` | `ServiceName`, `Payload`, `Signature` | `AccessToken`, `RefreshToken`, `Scope`, `ExpiresAt` |
| `msync:auth:refresh` | `RefreshToken` | 同上 |
| `msync:heartbeat` | — | — |
| `msync:deregister` | `ServiceName` | — |
| `msync:discover` | `ActiveOnly` | `services[]` |
| `msync:discover:one` | `ServiceName` | `service` |
| `msync:ogm:register` | `ObjectType`, `LocalID`, `ServiceName`, `GlobalID`, `Metadata` | `ogm` |
| `msync:ogm:batch` | `Items[]`, `ServiceName` | `ogms[]` |
| `msync:ogm:resolve` | `GlobalID` | `ogm` |
| `msync:ogm:query` | `ObjectType`/`ServiceName`/`LocalID` | `results` |
| `msync:ogm:update` | `GlobalID`, `Metadata` | `ogm` |
| `msync:ogm:deregister` | `GlobalID` | — |
| `msync:tx:create` | `Name`, `Steps[]`, `Description`, `TransactionType`, `InitiatorServiceName`, `ReferenceOGMUUID`, `Payload`, `TimeoutSeconds` | `dtx` |
| `msync:tx:execute` | `TransactionCode`, `AccessToken` | `dtx` |
| `msync:tx:compensate` | 同上 | `dtx` |
| `msync:tx:cancel` | `TransactionCode` | — |
| `msync:tx:detail` | `TransactionCode` | `dtx` |
| `msync:tx:retry` | `TransactionCode`, `StepIndex`, `AccessToken` | `step` |
| `msync:tx:timeout_check` | — | `TimedOut[]` |
| `msync:tx:query` | `Status` | `dtxs[]` |

服务端推送：

- `msync:service:online` — `{Service, Domain}`
- `msync:service:offline` — `{Service}`

## 6. 快速上手

### 6.1 子服务器（ATestCServer）使用 SDK

[ATestCServer/msync_client/sio_client.py](file:///d:/SMANAGE/ATestCServer/msync_client/sio_client.py) 提供了 `MSyncSocketClient`，**已全部 PascalCase**：

```python
from msync_client.sio_client import MSyncSocketClient

msync = MSyncSocketClient(
    master_url='https://www.gvsds.com',
    service_name='atest-cserver',
    service_domain='http://127.0.0.1:25080',
    service_secret='shared-secret',
    scope=['ogm:read', 'ogm:write', 'registry:read'],
)
msync.Connect()

# OGM
ogm = msync.OGMRegister('user', '123', 'atest-cserver')
resolved = msync.OGMResolve(ogm['GlobalID'])

# 服务发现
for svc in msync.DiscoverAll():
    print(svc['ServiceName'], '->', svc['Domain'])

# 状态
print('已认证:', msync.IsAuthenticated)
print('AccessToken:', msync.AccessToken[:20], '...')

msync.Disconnect()
```

### 6.2 直接使用 SDK 类

```python
from gvsdsdk import ServiceRegistry, OGMManager, TransactionManager

# 注册中心
ServiceRegistry.Register(
    Name='my-service',
    Domain='http://localhost:8080',
    Secret='secret',
)

# OGM
entry = OGMManager.Register('user', '1', 'my-service')

# 事务
dtx = TransactionManager.Create(
    Name='test-tx',
    StepsData=[{'ServiceName': 'svc-a', 'StepName': 'step1'}],
)
```

## 7. 架构示意

```
┌──────────────────┐         ┌──────────────────┐
│  ATestCServer    │         │  gvsds (master)  │
│                  │         │                  │
│ MSyncSocketClient│  ←──┐   │  Socket.IO       │
│   (PascalCase)   │     │   │  Server          │
│                  │     │   │  (PascalCase)    │
│  ┌────────────┐  │     │   │                  │
│  │ gvsdsdk    │  │     │   │  ┌────────────┐  │
│  │  .auth     │◄─┼─────┼───┼──│ msync      │  │
│  │  .ogm      │  │     │   │  │  .auth     │  │
│  │  .registry │  │     │   │  │  .ogm      │  │
│  │  .tx       │  │     │   │  │  .registry │  │
│  │  .client   │  │     │   │  │  .tx       │  │
│  └─────┬──────┘  │     │   │  └─────┬──────┘  │
│        │         │     │   │        │         │
│   薄封装        │     │   │   原生层          │
│        ▼         │     │   │        ▼         │
│  ┌────────────┐  │     │   │  ┌────────────┐  │
│  │ gvsds      │  │     │   │  │ gvsds      │  │
│  │ .apps      │◄─┼─────┘   │  │ .apps      │  │
│  │  .msync    │  │         │  │  .msync    │  │
│  └────────────┘  │         │  └────────────┘  │
└──────────────────┘         └──────────────────┘
```

`gvsdsdk` 与 `gvsds.apps.msync` 中的类是**完全同一个对象**——`from gvsdsdk import ServiceRegistry` 与 `from gvsds.apps.msync.registry import ServiceRegistry` 拿到的是同一个类。

## 8. 与项目原生层的集成

### 8.1 ATestCServer

[ATestCServer/manage.py:5](file:///d:/SMANAGE/ATestCServer/manage.py#L5) 在启动时设置 `sys.path`：

```python
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# → D:\SMANAGE  被加入 sys.path
# → import gvsdsdk 即可工作
```

子服务器**不直接 import** `gvsds.apps.msync`（避免 Django settings 依赖），仅通过 SDK 引用。

### 8.2 gvsds 主服务器

[gvsds/settings.py](file:///d:/SMANAGE/gvsds/settings.py) 启动时同样将 `gvsdsdk` 加入 `sys.path`：

```python
# 在 wsgi.py / asgi.py 中
sys.path.append(BASE_DIR)
```

`gvsds.apps.msync` 内部模块（如 [sio.py](file:///d:/SMANAGE/gvsds/apps/msync/sio.py)）会**直接 import** 原生层 `gvsds.apps.msync.auth/registry/ogm/transaction`，**协议 100% 兼容**。

## 9. 测试与验证

```python
import django; django.setup()
import gvsdsdk

# 验证 SDK 类与原生层是同一对象
from gvsdsdk import ServiceRegistry, OGMManager, TransactionManager
from gvsds.apps.msync.registry import ServiceRegistry as NativeSR
from gvsds.apps.msync.ogm import OGMManager as NativeOGM
from gvsds.apps.msync.transaction import TransactionManager as NativeTM

assert ServiceRegistry is NativeSR
assert OGMManager is NativeOGM
assert TransactionManager is NativeTM
print('SDK 薄封装校验通过')
```

## 10. 版本与兼容性

| 项目 | 版本 |
|---|---|
| **gvsdsdk** | 1.0.0 |
| **协议版本** | v1.0 (PascalCase 严格模式) |
| **Python** | >= 3.8 |
| **兼容 gvsds** | >= 1.0 |
| **兼容 ATestCServer** | >= 1.0 |

## 11. 错误码速查

| 错误码 | 含义 | 建议处理 |
|---|---|---|
| 400 | 参数错误 | 检查请求体字段名（PascalCase） |
| 401 | 认证失败 | 检查 `ServiceSecret` |
| 404 | 资源不存在 | 重新注册服务 |
| 409 | 冲突 | OGM 已注册、服务重名等 |
| 500 | 服务器内部错误 | 查看服务器日志 |

## 12. 维护

- **主服务器**：`gvsds/apps/msync/`（业务实现）
- **SDK 仓库**：`gvsdsdk/`（薄封装）
- **子服务器示例**：`ATestCServer/msync_client/`

修改原生层时，**SDK 无需改动**——所有类/函数会通过 `from … import …` 自动反映最新行为。
