# typescript-boot 完整使用手册

本文档面向 `typescript-boot` 的使用者，说明如何把它作为一个后端脚手架来搭建、运行和发布自己的服务。

如果你当前最关心的是“接口Service / 接口方法 / WebSocket 服务”本身，建议先读：

- [service-websocket-manual.md](./service-websocket-manual.md)

## 1. 项目定位

`typescript-boot` 不是一个特别重的全家桶框架，它更像是一套面向中小型后端项目的 TypeScript 脚手架和运行时约定。

它主要提供：

- HTTP 接口发布
- WebSocket 服务发布
- 在线接口文档
- Session 与权限管理
- Redis 接入
- 数据库统一访问抽象
- MySQL 实现
- 文件上传与下载
- 邮件发送
- 行为日志

它的目标是：

- 少写样板代码
- 用装饰器和约定统一接口定义
- 让项目从“能开发”更快进入“能发布”

## 2. 安装与项目初始化

### 2.1 安装

```bash
npm install typescript-boot reflect-metadata
```

### 2.2 TypeScript 配置

至少需要：

```json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
```

### 2.3 入口文件要求

入口文件最先执行：

```ts
import 'reflect-metadata';
```

### 2.4 推荐脚本

`typescript-boot` 本身不替你生成业务项目脚手架，所以建议你的项目补上最常用的 3 个脚本：

```json
{
  "scripts": {
    "dev": "ts-node src/index.ts",
    "build": "tsc -p tsconfig.json",
    "start": "node dist/index.js"
  }
}
```

### 2.5 推荐目录结构

```text
src/
  index.ts
  config/
    AppConfig.ts
  service/
    AccountService.ts
    OrderService.ts
  socket/
    ChatSocketService.ts
  dao/
    UserDao.ts
  manager/
    AccountManager.ts
  vo/
    UserVO.ts
    PageQuery.ts
```

推荐职责：

- `service/`：HTTP 接口服务
- `socket/`：WebSocket 服务
- `dao/`：数据库访问
- `manager/`：业务编排和领域逻辑
- `vo/`：接口入参、返回对象、数据库映射对象

## 3. 最小可运行项目

```ts
import 'reflect-metadata';
import {
  BaseService,
  SeeksWebServer,
  apiDoc,
  apiPath,
  apiReturn
} from 'typescript-boot';

@apiDoc('测试服务')
@apiPath('/demo')
class DemoService extends BaseService {
  @apiDoc('健康检查')
  @apiReturn('ok')
  async ping() {
    return this.success('ok');
  }
}

const server = new SeeksWebServer(3333);
server.publishService(new DemoService());
server.start();
```

访问：

```text
GET http://localhost:3333/demo/ping
```

## 4. SeeksWebServer 启动方式

`SeeksWebServer` 是整个脚手架的运行入口。

### 4.1 初始化

```ts
const server = new SeeksWebServer(3333);
```

### 4.2 常用 options

```ts
const server = new SeeksWebServer(3333, {
  disableDoc: false,
  jsonBodyLimit: '20mb',
  frontRootPath: './dist'
});
```

常用项：

- `disableDoc`：是否关闭在线文档
- `jsonBodyLimit`：JSON 请求体大小限制
- `frontRootPath`：静态前端目录
- `proxyTable`：代理扩展点

### 4.3 常用实例方法

- `publishService(service)`
- `publishSocketService(service)`
- `setFrontRootPath(path)`
- `setFontRootPath(path)`
- `setProxyTable(proxyTable)`
- `getAppService()`
- `getServer()`
- `start()`

### 4.4 在线文档

默认情况下，框架会自动发布在线文档：

```text
http://localhost:3333/typescript-boot
```

文档数据接口：

```text
http://localhost:3333/typescript-boot/docs/getServiceList
```

如果不需要：

```ts
const server = new SeeksWebServer(3333, {
  disableDoc: true
});
```

### 4.5 静态前端发布

如果你的后端还要直接托管 SPA 前端：

```ts
server.setFrontRootPath('./dist');
```

当前实现会在找不到路由时，对 `GET` 和 `OPTIONS` 请求回退到 `index.html`，适合常见 SPA 部署。

如果生产环境已经有 Nginx/Caddy，一般也可以把静态资源直接交给反向代理层处理。

## 5. HTTP 接口服务与 WebSocket 服务

这两块是 `typescript-boot` 的核心能力。

### 5.1 HTTP 接口服务

- 基类是 `BaseService`
- class 上通过 `@apiPath('/account')` 定义 service-path
- 方法上通过 `@apiPath('login')` 定义 method-path
- 如果方法没写 `@apiPath()`，默认使用方法名
- 可以用参数装饰器从 body/query/header 中取值

### 5.2 WebSocket 服务

- 基类是 `BaseSocketService`
- class 上通过 `@apiPath('/chat')` 定义连接路径
- 方法上通过 `@apiPath('room/join')` 定义 action
- 可以实现 `authorizeConnection()`、`onConnected()` 等生命周期

### 5.3 深入阅读

详细规则、代码示例和常见坑位请直接看：

- [service-websocket-manual.md](./service-websocket-manual.md)
- [api-path-rules.md](./api-path-rules.md)

## 6. 数据库

数据库能力主要由下面几层组成：

- `DataBaseClient`：数据库访问统一抽象
- `MysqlClient`：MySQL 实现
- `BaseDao`：项目默认数据库客户端的快捷入口

### 6.1 创建独立数据库客户端

```ts
import {DataBaseClient, MysqlClient} from 'typescript-boot';

const dbClient = new DataBaseClient(new MysqlClient({
  host: '127.0.0.1',
  port: 3306,
  user: 'root',
  password: '123456',
  database: 'demo',
  timezone: '+08:00'
}));
```

适合：

- 某个模块自己维护数据库连接
- 一个项目里要接多个数据库

### 6.2 注册为全局默认数据库

如果你的 DAO 都希望统一走同一个默认数据库，建议在项目启动阶段注册：

```ts
import {MysqlClient, setupDataBaseClient} from 'typescript-boot';

setupDataBaseClient(new MysqlClient({
  host: '127.0.0.1',
  port: 3306,
  user: 'root',
  password: '123456',
  database: 'demo',
  timezone: '+08:00'
}));
```

这里传给 `setupDataBaseClient()` 的是底层数据库实现，例如 `new MysqlClient(...)`，不是 `new DataBaseClient(...)`。

### 6.3 BaseDao 的用法

```ts
import {BaseDao} from 'typescript-boot';

export class UserDao extends BaseDao {
  async getById(id: number) {
    return this.dbClient.getObject(null, 'select * from sys_user where id = ?', [id]);
  }
}
```

注意：

- `BaseDao` 在实例化时会立即读取全局数据库客户端
- 所以请在创建 DAO 之前先执行 `setupDataBaseClient(...)`

### 6.4 常用数据库方法

最常用的方法有：

- `getList()`
- `getObject()`
- `getPageData()`
- `getCount()`
- `update()`
- `insertRow()`
- `updateRow()`
- `deleteRow()`
- `getObjectByColumns()`
- `executeSql()`

说明：

- `update()` / `insertRow()` / `updateRow()` / `deleteRow()` 返回值是受影响行数
- `executeSql()` 返回底层数据库原始结果，结果结构依赖具体数据库实现

### 6.5 VO 与数据库字段映射

如果你希望把数据库行映射为类对象，可以在类属性上补充元数据：

```ts
import {apiDoc, dbColumnName, dbValueConvert} from 'typescript-boot';

class UserVO {
  @apiDoc('用户ID')
  @dbColumnName('user_id')
  userId: string;

  @apiDoc('创建时间')
  @dbValueConvert((value) => String(value))
  createdAt: string;
}
```

映射规则要点：

- 如果用了 class 映射，建议把需要映射的属性都加上注解
- 当前实现只会稳定处理带元数据的属性
- 数据库列名默认可由属性名自动转下划线形式

### 6.6 其他数据库工具

- `encodeConfigPassword()`：加密数据库配置密码
- `passwordDecode()`：解密数据库配置密码
- `ColumnOrignValue`：插入或更新时使用原始 SQL 值，例如 `NOW()`
- `fileToBinary()`：把文件读成 `Buffer`

### 6.7 MysqlClient 的额外能力

`MysqlClient` 还额外提供：

- `enablePrintSql()` / `disablePrintSql()`：控制 SQL 打印
- `getTableMeta(tableName)`：读取表结构
- `batchUpdate(batchUpdates)`：事务批量更新

## 7. Session 与权限管理

权限体系围绕下面这些能力展开：

- `createToken()`
- `parseToken()`
- `setupSessionManager()`
- `getSessionManager()`
- `SessionManager` 接口
- `SessionManagerByFS`
- `SessionManagerByRedis`

### 7.1 Token 工具

```ts
import {createToken, parseToken} from 'typescript-boot';

const token = createToken({
  userId: 'U001',
  account: 'zhangsan'
});

const userInfo = parseToken(token);
```

### 7.2 启用文件型 SessionManager

适合单机部署：

```ts
import {
  SessionManagerByFS,
  setupSessionManager
} from 'typescript-boot';

setupSessionManager(new SessionManagerByFS(
  1000 * 60 * 60 * 24,
  './runtime-data'
));
```

### 7.3 启用 Redis SessionManager

适合多节点部署：

```ts
import {
  SessionManagerByRedis,
  setupRedisConfig,
  setupSessionManager
} from 'typescript-boot';

setupRedisConfig({
  host: '127.0.0.1',
  port: 6379
});

setupSessionManager(new SessionManagerByRedis(
  1000 * 60 * 60 * 24
));
```

### 7.4 HTTP 接口里的权限声明

```ts
@apiPermission(NoRequiredPermission)
@apiPermission(LoginRequiredPermission)
@apiPermission({
  login: true,
  roles: ['ADMIN']
})
```

权限可以写在 service 上，也可以写在 method 上。

### 7.5 框架默认读取这些 header 作为 token

- `token`
- `authorization`
- `_s_TK`
- `x-token`

### 7.6 选择建议

- 单机项目：优先 `SessionManagerByFS`
- 多节点项目：优先 `SessionManagerByRedis`

## 8. Redis

### 8.1 初始化 Redis

```ts
import {setupRedisConfig} from 'typescript-boot';

setupRedisConfig({
  host: '127.0.0.1',
  port: 6379
});
```

### 8.2 获取 Redis 客户端

```ts
import {getRedisClient} from 'typescript-boot';

const redis = getRedisClient();
await redis.set('demo', '1');
```

### 8.3 双配置模式

`setupRedisConfig()` 也支持传入两个配置，做一个轻量的主备切换：

```ts
setupRedisConfig([
  { host: 'redis-master', port: 6379 },
  { host: 'redis-standby', port: 6379 }
]);
```

这更接近一个简单的主备切换方案，不是完整的 Redis Sentinel 或 Redis Cluster 封装。

## 9. 日志系统

### 9.1 启用全局日志管理器

```ts
import {setupServiceLogManager} from 'typescript-boot';

setupServiceLogManager({
  async insertLog(logObj) {
    console.log('[behavior-log]', logObj);
  }
});
```

### 9.2 业务中手动写日志

```ts
import {getServiceLogger} from 'typescript-boot';

await getServiceLogger().insertLog({
  dept_id: 'D001',
  user_id: 'U001',
  user_name: '张三',
  account: 'zhangsan',
  behavior_desc: '手动记录日志',
  used_time: 10,
  ip_addr: '127.0.0.1',
  success: true
});
```

### 9.3 接口自动日志

如果某个接口要由框架自动记录行为日志，可以使用：

```ts
@apiLogToDB()
```

如果你要定制内容，请注意：

- 回调参数来自该接口方法的真实入参
- 不是固定的 `req/res/sessionUser`

## 10. 文件上传与下载

### 10.1 处理 `multipart/form-data`

```ts
import {getUploadFormData} from 'typescript-boot';

async upload(@apiRequest() req) {
  const formData = await getUploadFormData(req);
  return this.success({
    fields: formData.fields,
    files: formData.files
  });
}
```

返回结构中包含：

- `fields`
- `files`

上传文件会先落到系统临时目录，默认单次总大小限制约为 `100MB`。

### 10.2 下载文件

```ts
async exportFile() {
  const buffer = Buffer.from('hello');
  return this.downloadFile('demo.txt', buffer);
}
```

如果你直接通过原始 `res` 输出流，请返回 `this.streamed()`。

## 11. 邮件发送

### 11.1 初始化邮件客户端

```ts
import {
  QQEmailClient,
  setupEmailClient
} from 'typescript-boot';

setupEmailClient(new QQEmailClient({
  service: 'QQex',
  port: 465,
  secureConnection: true,
  auth: {
    user: 'demo@qq.com',
    pass: 'smtp-password'
  }
}));
```

说明：

- 这里的 `service` / `port` / `secureConnection` 要按你的 SMTP 配置填写
- `pass` 不是邮箱登录密码，而是 SMTP 授权码

### 11.2 发送邮件

```ts
import {generateVerifyCode, getEmailClient} from 'typescript-boot';

const code = generateVerifyCode(6);

await getEmailClient().sendEmail({
  mailTo: ['user@example.com'],
  title: '验证码',
  htmlContent: `<div>您的验证码是：${code}</div>`
});
```

## 12. 内置文档、公共路由与开发工具

### 12.1 在线文档

只要没有关闭 `disableDoc`，框架会自动注册一组隐藏服务来提供在线文档。

通常你不需要手动发布 `DocService`。

### 12.2 FsService

`FsService` 是一个隐藏的公共路由，用于远程文件上传、下载和部分运维场景。

这不是普通业务项目的必需能力，只有当你明确需要：

- 远程传文件
- 在线更新资源

时再考虑使用。

### 12.3 dev-tools

框架还导出了几类偏开发辅助的工具：

- `APIServiceClient`
- `FSServiceClient`
- `TSBootTestCaseRunner`

它们更适合测试、联调或内部工具脚本，不是业务项目的核心依赖。

## 13. 推荐启动模板

下面给一个比较完整的启动模板：

```ts
import 'reflect-metadata';
import {
  MysqlClient,
  QQEmailClient,
  SeeksWebServer,
  SessionManagerByFS,
  setupDataBaseClient,
  setupEmailClient,
  setupServiceLogManager,
  setupSessionManager
} from 'typescript-boot';

import {AccountService} from './service/AccountService';
import {OrderService} from './service/OrderService';
import {ChatSocketService} from './socket/ChatSocketService';

setupDataBaseClient(new MysqlClient({
  host: '127.0.0.1',
  port: 3306,
  user: 'root',
  password: '123456',
  database: 'demo',
  timezone: '+08:00'
}));

setupSessionManager(new SessionManagerByFS(
  1000 * 60 * 60 * 24,
  './runtime-data'
));

setupEmailClient(new QQEmailClient({
  service: 'QQex',
  port: 465,
  secureConnection: true,
  auth: {
    user: 'demo@qq.com',
    pass: 'smtp-password'
  }
}));

setupServiceLogManager({
  async insertLog(logObj) {
    console.log('[behavior-log]', logObj);
  }
});

const server = new SeeksWebServer(3333, {
  jsonBodyLimit: '20mb'
});

server.publishService(new AccountService());
server.publishService(new OrderService());
server.publishSocketService(new ChatSocketService());
server.start();
```

如果你要用 Redis SessionManager，再额外补上：

```ts
setupRedisConfig(...);
setupSessionManager(new SessionManagerByRedis(...));
```

## 14. 构建、发布与部署

### 14.1 本地开发

```bash
npm run dev
```

### 14.2 构建

```bash
npm run build
```

### 14.3 生产启动

```bash
npm run start
```

### 14.4 部署建议

- 如果需要同时支持 HTTP 和 WebSocket，通常一个 `SeeksWebServer` 实例就够了
- 反向代理层记得放行 WebSocket `Upgrade`
- 单机部署优先用 `SessionManagerByFS`
- 多节点部署优先用 `SessionManagerByRedis`
- 在线文档对内网有用，对公网项目可以考虑关闭
- 如果你需要前后端同域部署，可使用 `frontRootPath` 托管前端资源

### 14.5 关于代理

框架保留了 `proxyTable` / `setProxyTable()` 这类扩展点，但生产环境中的反向代理和网关转发，通常更建议交给：

- Nginx
- Caddy
- API Gateway

去做。

## 15. 当前版本规则与限制

这是写业务时最容易踩到的几个点：

- `service-path` 只能单段，多段路径应该放在 method-path
- `BaseService` / `BaseSocketService` 的普通实例方法会被视为接口或动作，不要在里面放辅助方法
- 当前没有自动参数校验和自动类型转换，`query` 参数通常还是字符串
- 当前没有 `@apiParamFromPath()`，路径变量通过 `req.params` 获取
- 当前内置业务请求方式主要是 `GET` 和 `POST`
- 对象映射和文档展示依赖属性元数据，建议给 VO/BO 属性写上 `@apiDoc()` 等注解

## 16. 阅读顺序建议

建议按下面顺序阅读：

1. [service-websocket-manual.md](./service-websocket-manual.md)
2. [api-path-rules.md](./api-path-rules.md)
3. [from-author.md](./from-author.md)
4. [next-version-feature.md](./next-version-feature.md)
