读取Excel 进行汇总
This commit is contained in:
194
docs/数据库与架构设计-多类型数据.md
Normal file
194
docs/数据库与架构设计-多类型数据.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# 多公司、多类型数据存储与展示 — 数据库与程序设计
|
||||
|
||||
## 一、需求归纳
|
||||
|
||||
| 数据类型 | 示例 | 结构特点 | 公司覆盖 |
|
||||
| -------- | -------------------------------- | ---------------------------------------- | ---------- |
|
||||
| 三大报表 | 资产负债表、利润表、现金流量表 | 报告期 × 项目 → 数值,项目由 config 决定 | 所有公司 |
|
||||
| 业务构成 | 主营业务构成(产品/地区/业务线) | 报告期 × 维度(产品、地区等) → 数值/占比 | 部分公司 |
|
||||
| 业务指标 | 分众梯媒数量、门店数、产能等 | 报告期 × 指标名(或细分维度) → 数值 | 某公司特有 |
|
||||
|
||||
共性:**不同公司、不同类型的数据,维度与指标各异,且类型会持续增加**。
|
||||
目标:**一套程序 + 一套库表** 支持所有类型,便于落库与图表展示。
|
||||
|
||||
---
|
||||
|
||||
## 二、设计原则
|
||||
|
||||
1. **类型可扩展**:新增一种数据(如「门店数量」)不新增表、尽量不迁库。
|
||||
2. **公司可扩展**:新公司只需配置「用哪些数据类型 + 解析/展示配置」。
|
||||
3. **存储统一**:所有「公司 + 数据类型 + 时间 + 维度 → 数值」走同一套存储模型。
|
||||
4. **查询与图表友好**:能按公司、类型、时间范围取出结构化数据,方便 ECharts 等前端作图。
|
||||
|
||||
---
|
||||
|
||||
## 三、数据库选型结论
|
||||
|
||||
- **仍推荐 PostgreSQL**。
|
||||
- 原因:需要按公司/类型/时间筛选、聚合、多公司对比;维度用 **JSONB** 表达,兼顾灵活性与查询(GIN 索引、jsonb 运算符);同一套表即可覆盖三大表 + 主营业务构成 + 梯媒数量等所有当前与未来类型。
|
||||
|
||||
---
|
||||
|
||||
## 四、核心表结构设计
|
||||
|
||||
### 4.1 概念模型(统一抽象)
|
||||
|
||||
所有「公司 × 数据类型 × 时间」下的数据,统一视为:
|
||||
|
||||
- **数据集类型**(dataset type):如 balance_sheet、income_statement、main_business_composition、elevator_media_count …
|
||||
- **维度**(dimensions):因类型而异,用键值对表示,例如
|
||||
- 三大表:`{ "category": "类现金", "item": "货币资金" }` 或仅 `{ "item": "资产总计" }`
|
||||
- 主营业务构成:`{ "segment": "产品", "name": "空调", "region": "国内" }`
|
||||
- 梯媒数量:`{ "metric": "梯媒数量", "sub_type": "电梯电视" }` 或仅 `{ "metric": "梯媒数量" }`
|
||||
- **数值**(value):统一为数值型(金额亿、数量、占比等)。
|
||||
|
||||
即:**一条记录 = 某公司、某数据集类型、某报告期、若干维度、一个数值**。
|
||||
|
||||
### 4.2 表结构(PostgreSQL)
|
||||
|
||||
```sql
|
||||
-- 公司
|
||||
CREATE TABLE companies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(200) NOT NULL,
|
||||
code VARCHAR(50) UNIQUE, -- 可选:股票/内部编码
|
||||
created_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- 数据集类型(注册所有「可用的数据类型」)
|
||||
CREATE TABLE dataset_types (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
code VARCHAR(80) UNIQUE NOT NULL, -- balance_sheet, main_business_composition, elevator_media_count
|
||||
name VARCHAR(200) NOT NULL, -- 资产负债表、主营业务构成、梯媒数量
|
||||
description TEXT,
|
||||
-- 用于解析与展示的默认结构(可选)
|
||||
default_dimension_keys JSONB, -- ["category","item"] 或 ["segment","region","metric"]
|
||||
created_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- 某公司对某数据集类型的「解析/展示」配置(对应现在的 config/公司名/*.json)
|
||||
CREATE TABLE company_dataset_configs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
||||
dataset_type_id UUID NOT NULL REFERENCES dataset_types(id),
|
||||
config JSONB NOT NULL, -- 与现有 JSON 一致:categories / rows_to_delete / columns_to_keep / 自定义映射
|
||||
UNIQUE(company_id, dataset_type_id),
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- 统一数据表:所有类型的数据都落在这里
|
||||
CREATE TABLE company_data (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
|
||||
dataset_type_id UUID NOT NULL REFERENCES dataset_types(id),
|
||||
report_period DATE NOT NULL, -- 报告期(季末/年末等)
|
||||
dimensions JSONB NOT NULL, -- 维度键值对,如 {"category":"类现金","item":"货币资金"} 或 {"metric":"梯媒数量"}
|
||||
value NUMERIC(20,4), -- 数值(亿、数量、比率等)
|
||||
created_at TIMESTAMPTZ DEFAULT now(),
|
||||
UNIQUE(company_id, dataset_type_id, report_period, dimensions)
|
||||
);
|
||||
|
||||
-- 便于按公司+类型+时间查询
|
||||
CREATE INDEX idx_company_data_lookup
|
||||
ON company_data(company_id, dataset_type_id, report_period);
|
||||
|
||||
-- 便于按 dimensions 内键查询(如按 item / metric 筛选)
|
||||
CREATE INDEX idx_company_data_dimensions ON company_data USING GIN (dimensions);
|
||||
```
|
||||
|
||||
- **不同公司、不同数据类型的「字段差异」**:全部体现在 `dimensions` 的键值里,无需为每种类型或每个公司加列或建新表。
|
||||
- 三大表:`dimensions` 用 `item`(及可选的 `category`)。
|
||||
- 主营业务构成:用 `segment`、`name`、`region` 等。
|
||||
- 梯媒数量:用 `metric`、`sub_type` 等。
|
||||
- 以后新增类型:只需在 `dataset_types` 增加一行,并在解析逻辑里往 `dimensions` 填对应键即可。
|
||||
|
||||
### 4.3 示例数据
|
||||
|
||||
**资产负债表(美的集团,2024-12-31)**
|
||||
|
||||
| company_id | dataset_type_id | report_period | dimensions | value |
|
||||
| ---------- | --------------- | ------------- | --------------------------------------- | ------- |
|
||||
| 美的-uuid | balance_sheet | 2024-12-31 | {"category":"类现金","item":"货币资金"} | 140.41 |
|
||||
| 美的-uuid | balance_sheet | 2024-12-31 | {"item":"资产总计"} | 6043.52 |
|
||||
|
||||
**主营业务构成(某公司,2024-12-31)**
|
||||
|
||||
| company_id | dataset_type_id | report_period | dimensions | value |
|
||||
| ----------- | ------------------------- | ------------- | ------------------------------------------------ | ------ |
|
||||
| 某公司-uuid | main_business_composition | 2024-12-31 | {"segment":"产品","name":"空调","region":"国内"} | 1200.5 |
|
||||
|
||||
**梯媒数量(分众传媒)**
|
||||
|
||||
| company_id | dataset_type_id | report_period | dimensions | value |
|
||||
| ---------- | -------------------- | ------------- | ------------------------------------------- | ----- |
|
||||
| 分众-uuid | elevator_media_count | 2024-12-31 | {"metric":"梯媒数量","sub_type":"电梯电视"} | 80.2 |
|
||||
|
||||
---
|
||||
|
||||
## 五、程序架构设计(NestJS)
|
||||
|
||||
### 5.1 分层与职责
|
||||
|
||||
- **数据集类型**:只做「类型注册」和「默认维度/展示提示」,不承载具体解析逻辑。
|
||||
- **公司 + 配置**:每个公司对每种类型有一份 config(对应现有 JSON),决定如何从 Excel 解析出 (report_period, dimensions, value)。
|
||||
- **解析与写入**:按「类型 + 公司 config」驱动,输出统一格式 (company_id, dataset_type_id, report_period, dimensions, value),写入 `company_data`。
|
||||
- **查询与展示**:按 company / dataset_type / 时间范围查 `company_data`,转成前端/ECharts 需要的结构(如按 item 的时序、按 segment 的饼图等)。
|
||||
|
||||
### 5.2 模块划分建议
|
||||
|
||||
```
|
||||
src/
|
||||
├── companies/ # 公司 CRUD
|
||||
├── dataset-types/ # 数据集类型注册、列表
|
||||
├── company-configs/ # 公司 × 类型的 config CRUD(对应 config/公司名/*.json)
|
||||
├── import/ # 上传 Excel + 解析 + 落库
|
||||
│ ├── parsers/ # 按类型分:balance-sheet.parser, income-statement.parser, cash-flow.parser,
|
||||
│ │ # main-business-composition.parser, elevator-media.parser, ...
|
||||
│ ├── import.service # 选公司、类型、文件 → 调对应 parser → 写 company_data
|
||||
│ └── import.controller
|
||||
├── data/ # 查询接口:按公司/类型/时间范围返回数据,供图表用
|
||||
└── chart-presets/ # 可选:按 dataset_type 的图表预设(折线、饼图、柱状等)
|
||||
```
|
||||
|
||||
### 5.3 解析层设计(应对「类型持续增加」)
|
||||
|
||||
- **策略模式**:每种 `dataset_types.code` 对应一个 Parser(如 `BalanceSheetParser`, `MainBusinessParser`, `ElevatorMediaParser`)。
|
||||
- Parser 输入:**文件流 + 该公司该类型的 config(JSONB)**。
|
||||
- Parser 输出:**数组 of { report_period, dimensions, value }**,由 `ImportService` 统一写入 `company_data`。
|
||||
- 新增类型时:在 `dataset_types` 插入一条;新增一个 Parser 并注册到工厂/映射表即可,无需改表结构。
|
||||
|
||||
### 5.4 配置与维度的约定(程序侧)
|
||||
|
||||
- 三大表:沿用现有 config 结构(categories / rows_to_delete / columns_to_keep),解析后
|
||||
- `dimensions = { "item": "货币资金" }` 或 `{ "category": "类现金", "item": "货币资金" }`。
|
||||
- 主营业务构成、梯媒数量等:在 `company_dataset_configs.config` 里约定字段,例如
|
||||
- 主营业务:`{ "sheet_name": "主营业务构成", "row_header": "产品", "col_period": true, "value_key": "收入" }`
|
||||
- 梯媒:`{ "sheet_name": "业务数据", "metrics": ["梯媒数量","电梯电视","电梯海报"], "period_col": "报告期" }`
|
||||
解析器按 config 读 Excel,组装出 `dimensions` 和 `value`。
|
||||
|
||||
这样,**程序与数据库都只依赖「公司 + 数据集类型 + config」**,不依赖具体有哪些列名,能适应不同公司、不同指标。
|
||||
|
||||
---
|
||||
|
||||
## 六、查询与图表展示
|
||||
|
||||
- **按公司 + 类型 + 时间范围**:
|
||||
`SELECT report_period, dimensions, value FROM company_data WHERE company_id = ? AND dataset_type_id = ? AND report_period BETWEEN ? AND ? ORDER BY report_period, dimensions;`
|
||||
- 前端或后端将结果按 `dimensions` 转成 ECharts 所需格式,例如:
|
||||
- 折线图:dimensions.item 或 dimensions.metric 为系列,report_period 为 X 轴;
|
||||
- 饼图:某报告期的 dimensions.segment / dimensions.name 为扇区,value 为数值;
|
||||
- 可为每种 `dataset_type` 配置「图表预设」(折线/饼/柱状、默认维度),减少前端重复逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 七、小结
|
||||
|
||||
| 维度 | 选择与设计 |
|
||||
| -------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| 数据库 | PostgreSQL;用「公司 + 数据集类型 + 报告期 + dimensions(JSONB) + value」统一存所有类型。 |
|
||||
| 表设计 | companies / dataset_types / company_dataset_configs / company_data;不按公司或按数据类型拆表。 |
|
||||
| 程序 | NestJS;按 dataset_type 的 Parser 策略 + 统一 Import 写 company_data;config 按公司×类型存 JSONB。 |
|
||||
| 扩展方式 | 新数据类型:新增 dataset_types 一行 + 新 Parser;新公司:新增 companies + 若干 company_dataset_configs。 |
|
||||
|
||||
这样即可在**不增加新表、不迁库**的前提下,支持三大表、主营业务构成、分众梯媒数量以及未来更多公司、更多类型的数据存储与图表展示。
|
||||
Reference in New Issue
Block a user