filemgr/README.md

74 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# filemgr — 文件/目录管理模块
## 模块定位
通用文件与目录管理包:提供**目录树 + 文件上传/删除**的基础能力,核心是 `filemgr/filemgr.py` 里的 `FileMgr` 类(配 `FileMgrResult` 结果对象与 `get_file_hash` sha256 工具函数)。物理文件走 ahserver 的 `FileStorage`(上传落在 `conf/config.json``filesroot`,即 `$[workdir]$/files`),元数据落 MySQL 的 `folder`/`file` 两张表。
两种运行形态(仓库中都有实物):
1. **独立 app 演示**`app/fileMGR.py``load_appbase → load_rbac → load_filemgr → load_bricks`,监听 9080`mediadb`,见 `conf/config.json`
2. **宿主模块**`filemgr/init.py``load_filemgr()` 把 **FileMgr 类本身**注册到 `ServerEnv``env.FileMgr = FileMgr`宿主sage / pipeline-app在 dspy 里取用。库名经 `get_module_dbname('filemgr')` 解析,禁写死。
在账号/存储计费链路中filemgr 是**容量事实的来源**`file.filesize` 汇总即机构占用量,`storage_resource` 的容量采样(`record_storage_sample`)可由扫这些记录得到 `used_gb`。filemgr 自身**不直接调用** product_management / accounting。
## 表清单models/*.json另有同名 .xlsx 源)
| 表 | 用途 | 关键字段 |
|---|---|---|
| `folder` | 目录(树) | `id`(pk32), `parentid`(父目录,自关联), `fiid`(目录信息id), `name`(500), `ownerid`(所属机构 → organization.id) |
| `file` | 文件元数据 | `id`(pk32), `folderid`(→folder.id), `fiid`, `name`(200), `webpath`(相对路径400), `realpath`(绝对路径400), `filetype`(扩展名≤10), `filesize`(long,字节), `ownerid`(属主机构), `orgperm`(同事权限 char3), `otherperm`(其他人权限), `hashvalue`(sha256,65) |
⚠️ models 的 `codes` 段引用了 `folderinfo``organization` 两张表——**本仓库未定义**需宿主提供organization 属 appbase/组织模块)。`folderinfo`fiid 指向的"目录信息")在仓库内无建表定义,接入新宿主前需确认其来源。
## 对外 API / 页面端点wwwroot/,挂载在 `/filemgr/` 下)
| 端点 | 类型 | 说明 |
|---|---|---|
| `/filemgr/get_folder_subs.dspy` | dspy | 取某目录的直接子项(子目录+文件),供树控件 `get_data_url` |
| `/filemgr/delete_folder_or_file.dspy` | dspy | 删除入口:目录有子项时返回 `UiComform` 二次确认(跳 conformed_delete否则直接删 |
| `/filemgr/conformed_delete.dspy` | dspy | 确认后的目录递归删除 |
| `/filemgr/upload_file.ui` | Bricks Form | 上传弹窗folderid/fiid 隐藏域 + upfile 文件域submit 绑定 upload_file.dspy |
| `/filemgr/upload_file.dspy` | dspy | 调 `FileMgr.add_file(request, params_kw)` 落库 |
| `/filemgr/getallfiles.dspy` | dspy | 递归列出目录下全部文件id+realpath |
| `json/folder.json` | Bricks tree widget | 目录树页面定义xls2ui 生成,`json/build.sh`: `xls2ui -m ../models -o ../wwwroot filemgr *.json`):文件类型图标映射(pdf/docx/xlsx/epub/csv…)、工具栏"上传文件"弹 `upload_file.ui` |
`conf/config.json` 另注册了 startswith 路由 `/idfile → registerfunction 'idfile'`(独立演示形态用;该注册函数不在本仓库实现)。
## FileMgr 关键方法(实测签名)
```python
FileMgr(fiid) # 构造需要 fiid见"已知问题"
await add_file(request, params_kw) # 上传: 登录校验→属主校验→配额校验→sha256去重→落file表
await del_folder(request, id) / _del_folder(sor, id, ownerid) # 递归删目录(属主校验)
await del_file(request, fid) / _del_file(sor, fid, ownerid) # 删文件(物理文件按引用计数unlink)
await has_sub(sor, folderid) # 目录是否有子项(union子目录+文件)
await get_subs(id) # 直接子项列表(自建连接)
await sor_get_subfolder(sor, fid) / sor_get_subfile(sor, fid)
await get_quota_used(sor, orgid) # sum(file.filesize)/1e6单位 MB
await get_organization_quota(sor, orgid) # 基类桩: 返回 (0, '9999-12-31'),子类必须覆写
get_file_hash(filepath, algo='sha256', chunk_size=8192)
```
上传语义add_file`hashvalue`+同属主 → 拒收"file exists";同 `hashvalue` 不同属主 → **复用已有物理文件路径**(物理去重,只加一条 file 记录)。删除时物理文件仅在**无任何记录再引用该 hashvalue** 时才 unlink。配额判断 `quota_used + filesize/1e6 >= quota` 即拒绝并删除已上传的临时文件。
## 与产品/计费链路product_management、accounting的集成关系
- filemgr **不注册** `env.product_interface`,不参与 resolve_ppid/定价映射——它只是资源事实层。
- 链路位置:`file.filesize`机构维度汇总MB`storage_resource.record_storage_sample(...)``storres_sample.used_gb/quota_gb/over_gb``summarize_samples` 按订购周期求均值/峰值 → `storage_charging``storres_usage`accounting_status=pending→ accounting 记账。
- `get_organization_quota` 是配额挂钩点:接入 storage_resource 产品后,应由子类/宿主把机构已购容量(订购权益 `workspace_gb`/`storage_gb`)返回给 filemgr 做上传前置校验(当前基类返回 0见下
## 部署注意事项
1. **库名动态解析**`get_dbname()``get_serverenv('get_module_dbname')('filemgr')`;独立演示 app 里写死返回 `'sage'`pipeline-app 宿主返回 `'pipeline'`——复用模块时勿改死。
2. **建表**models/file.json、models/folder.json 交宿主 create_tables/xls2ddl 流程;注意 `file` 表的 `codes` 外联 `folderinfo`/`organization` 必须先存在。
3. **文件存储根**`conf/config.json``filesroot=$[workdir]$/files`,上传大小受 `client_max_size: 20000` 限制;生产需确认磁盘与 FileStorage 配置一致。
4. **i18n**i18n/{zh,en,jp,ko}/msg.txt 已含表字段词条commit 1461430
5. **pyproject/setup.cfg**:包名 filemgr 0.0.1,依赖 apppublic/sqlor/ahserver`packages = find:` 会把 `app/``json/` 一并扫描,安装时注意。
## 已知问题(实测代码,未修改——修前必读)
- `FileMgr.__init__(self, fiid)` **要求 fiid**,但 wwwroot 各 dspy 均以 `FileMgr()` 无参构造 → 直接运行会 TypeErrorgetallfiles.dspy 更是 `filemgr = FileMgr` 拿了类当实例,且调用了不存在的 `get_subfiles`)。
- `conformed_delete.dspy` 用小写 `Filemgr()``_del_folder(sor, params_kw.id)` 少传 `ownerid`
- `folder_files()` 有笔误 `dbanme = get_dbname()`(下一行用 `dbname` → NameError`_folder_files` 递归调用漏传 `sor``del_folder``self.file_deleted(...)` 未 await。
- `get_folder_ownerid`/`file_uploaded`/`file_deleted` 基类均为空实现pass`is_folder_ownerid` 因此恒 False → **add_file 属主校验必失败**;基类 `get_organization_quota` 返回 quota=0commit 5a81ea2 特意 revert 回 0→ 未覆写的子类**任何上传都触发配额溢出**。本模块设计为被业务子类继承覆写这三个钩子后使用。