我设计了一个系统,内部有如下实体: 1. **文章**:以文本为主体,可能包含少量表格、代码、文头文尾等内容,包含大量需提取的关键信息。一个典型的示例: ``` 2026年6月1日晚8点,好友们决定为王若飞(男)组织一场生日派对。这次小范围聚会共有5人参加,刘阳(女)作为总策划,除寿星和总策划外,还有李四(女)、王五(男)和赵六(男)参加。活动总预算为3000元,且已完成备案,现场安排了掷骰子、扔飞镖和打桌球等破冰游戏,确保大家玩得尽兴。 ``` 2. **关键信息定义**:使用Json定义的树状结构,每个节点以`node_label`为key,包含`metadata`字段和`subnodes`字段,`metadata`字段中包含`node_label`(取关键信息名称的拼音首字母、`node_name`(关键信息名称)、`node_type`(取值为`concept`时表示分类节点,`property`时表示属性节点)、`value_type`(仅当`node_type`为`property`时生效,取值为`integer`、`float`、`string`、`boolean`、`date_time`、`class`,其中`class`表示关键信息为复合结构,包含子属性)、`is_list`(仅当`node_type`为`property`时生效,标识关键信息是否为列表类型,即是否可以有多个实例)字段。一个典型的示例: ```json { "metadata": { "node_label": "PDJH", "node_name": "派对计划", "node_type": "concept" }, "subnodes": { "JBXX": { "metadata": { "node_label": "JBXX", "node_name": "基本信息", "node_type": "concept" }, "subnodes": { "PDMC": { "metadata": { "node_label": "PDMC", "node_name": "派对名称", "node_type": "property", "value_type": "string", "is_list": false } }, "PDKSSJ": { "metadata": { "node_label": "PDKSSJ", "node_name": "派对开始时间", "node_type": "property", "value_type": "date_time", "is_list": false } }, "PDZRS": { "metadata": { "node_label": "PDZRS", "node_name": "派对总人数", "node_type": "property", "value_type": "integer", "is_list": false } }, "SFBA": { "metadata": { "node_label": "SFBA", "node_name": "是否备案", "node_type": "property", "value_type": "boolean", "is_list": false } } } }, "RYXX": { "metadata": { "node_label": "RYXX", "node_name": "人员信息", "node_type": "concept" }, "subnodes": { "ZCH": { "metadata": { "node_label": "ZCH", "node_name": "总策划", "node_type": "property", "value_type": "class", "is_list": false }, "subnodes": { "XM": { "metadata": { "node_label": "XM", "node_name": "姓名", "node_type": "property", "value_type": "string", "is_list": false } }, "SFZH": { "metadata": { "node_label": "SFZH", "node_name": "身份证号", "node_type": "property", "value_type": "string", "is_list": false } } } }, "RYQD": { "metadata": { "node_label": "RYQD", "node_name": "人员清单", "node_type": "property", "value_type": "class", "is_list": true }, "subnodes": { "XM": { "metadata": { "node_label": "XM", "node_name": "姓名", "node_type": "property", "value_type": "string", "is_list": false } }, "SFZH": { "metadata": { "node_label": "SFZH", "node_name": "身份证号", "node_type": "property", "value_type": "string", "is_list": false } } } } } }, "HDXX": { "metadata": { "node_label": "HDXX", "node_name": "活动信息", "node_type": "concept" }, "subnodes": { "PBYXLB": { "metadata": { "node_label": "PBYXLB", "node_name": "破冰游戏清单", "node_type": "property", "value_type": "string", "is_list": true } } } } } } ``` 3. **关键信息实例**:根据**关键信息定义**,对其中的节点进行赋值而得到的实例,以Json形式表示。其中,有如下注意事项: 1. 节点以`node_label`为key; 2. 在**关键信息定义**中,`node_type`为`concept`的,value为Json对象,包含其子节点或子属性的值; 3. 在**关键信息定义**中,`value_type`为`integer`、`float`、`string`、`boolean`、`date_time`时,如果`is_list`为`false`,value为对应类型;如果`is_list`为`true`,value为对应类型列表。特别地,`date_time`类型的值设置为字符串,但格式固定为`XXXX-XX-XXTXX:XX:XX.XXX`; 4. 在**关键信息定义**中,`value_type`为`class`的,如果`is_list`为`false`,value为Json对象,包含其子属性的值;如果`is_list`为`true`,value为Json对象列表,每个元素包含其子属性的值。 针对**文章**示例,根据上述**关键信息定义**,生成对应的**关键信息实例**为: ```json { "PDJH": { "JBXX": { "PDMC": "王若飞生日派对", "PDKSSJ": "2026-06-01T20:00:00.000", "PDZRS": 5, "PDZYS": 3000.0, "SFBA": true }, "RYXX": { "ZCH": { "XM": "刘阳", "XB": "女" }, "RYQD": [ { "XM": "刘阳", "XB": "女" }, { "XM": "王若飞", "XB": "男" }, { "XM": "李四", "XB": "女" }, { "XM": "王五", "XB": "男" }, { "XM": "赵六", "XB": "男" } ] }, "HDXX": { "PBYXQD": [ "掷骰子", "扔飞镖", "打桌球" ] } } } ``` 现在,我需要依托大模型,根据输入的**文章**和**关键信息定义**,从文章中将所有关键信息提取出来;请帮我设计一个prompt,使得大模型能够准确、高效、稳定地完成这个task。如果有必要,请使用langchain等框架,分步骤进行,以达到最好的效果。 -------- OK,现在,我需要将原始文本中相关的关键信息区域也标识出来。例如,针对“2026年6月1日晚8点,好友们决定为王若飞(男)组织一场生日派对,活动已完成备案。”这个文本,我希望生成类似于“{[PDJH.JBXX.PDKSSJ],2026-06-01T20:00:00.000,2026年6月1日晚8点,2026年1月1日~2026年12月31日,YYYY年M月D日(上午/下午/晚)H点[m分[s秒]]},好友们决定为{[PDJH.RYXX.RYQD[0].XM],王若飞,王若飞,2~4个汉字,不作转换}({[PDJH.RYXX.RYQD[0].XB],男,男,男/女,不作转换})组织一场生日派对,活动{[PDJH.JBXX.SFBA],true,已完成备案,已完成备案/未完成备案,true->已完成备案/false->未完成备案}”。每块关键信息区域包括{节点路径,提取值,原始文本,提取值的约束(以自然语言表示,供大模型理解),提取值到原始文本的转换规则(以自然语言表示,供大模型理解)}。请先帮我思考这个标识相关的符号体系是否合理,是否会与原文符号产生歧义,如果会,则不一定使用这套符号体系,改进该体系。 -------- 1. 值约束来源是关键信息定义Json,部分关键信息会带一个constraints属性,里面会带{"raw_constraints":{"min_value":[0],"max_value":[100]}}、{"raw_constraints":{"enum_string":[["男", "女"]]}}这样的约束,确保将其精准地转换为自然语言,也可以在值约束里将原始的Json约束附带上。如果关键信息没有自带该属性,那么对于显而易见的关键信息约束,如“身份证号(18位数字,最后一位可以是X)”、“性别(男/女)”、“年龄(0-110)”等,你要进行推断,给出约束;对于你无法确定的约束,不要随意给出。 转换规则由你自行给出。 2. 标注以一个独立字段输出。 -------- 有几个提取的原则需要微调一下: 1. 如果原文中某处同时对应关键信息定义中的多个关键信息,如“刘阳”即对应“总策划”,又对应“人员清单”中的一员,那么标注时,将多处同时标注,并在中间使用“||”分隔开,如:`{{PDJH.RYXX.ZCH.XM||刘阳||刘阳|| ||不作转换}}||{{PDJH.RYXX.RYQD[1].XM||刘阳||刘阳|| ||不作转换}}`; 2. 如果原文中不存在关键信息实例中的某处关键信息原文,例如原文为“为王若飞(男)组织一场生日派对”,而关键信息实例中为“王若飞生日派对”,那么在原文中找到最相关的部分,如“生日派对”进行标注,并将转换规则写为“根据原文进行微调”,结果为`{{PDJH.JBXX.PDMC||王若飞生日派对||生日派对|| ||根据原文进行微调}}`; 3. 对于时间、布尔等类型的值,你原来的做法是在代码里写死,我需要你根据原文内容,利用大模型(为确保快速处理,可不使用reasoning推理)给出明确的转换规则,例如`ISO 8601 → YYYY年M月D日(上午/下午/晚)H点[m分[s秒]],根据是否为整点,可视情省略[]中的内容`; 4. 你现在预置了一些关键信息的约束,我同样要求你不要写死,根据不同关键信息定义中的内容,利用大模型(为确保快速处理,可不使用reasoning推理)给出相应的约束。 -------- 针对某个提取值在原文中拥有多个对应文本的情况(如“王五”这个人在文中出现了多次),我新增了一条提取规则:“10. **同一提取值可多次标注**:如果某个提取值在原文中拥有多个对应文本,对这些文本分别进行标注,各标注的“节点路径”、“提取值”、“值约束”需保持一致,“原始文本”、“转换规则”可以不同,视原文决定”。但是经过测试,该规则未生效,多个对应文本只有一个被标注。请帮我检查并使之生效。 -------- 请为我增加调试日志,在每一次与LLM交互前,打印本次交互是整个处理流程中的哪个分步骤,同时打印给LLM发送的prompt,以便我了解发送的内容。同时,在最外层函数调用处增加debug变量,只有当它为True时才打印上述日志。 -------- 目前,在转换规则生成过程中,由于直接把整篇文章给了LLM,导致LLM识别提取值对应的原文不清晰(例如提取值“3000.0”对应文中的“3000”,但LLM认为是“3000元”)。所以,我想将“Step 2: LLM辅助生成约束和转换规则”拆分成两步,第一步将提取值对应的原文提取出来,第二步仅根据对应的原文进行约束和转换规则的生成。为了速度快,两步均采用大模型的轻量级调用,不调用reasoning推理。 -------- 现在,我希望做一个界面,左侧为菜单栏,目前包含两个菜单:文档解析和信息管理。 1. 文档解析:分为两栏,左栏为文档展示区域,如未选择文档,显示“请选择文档”;上传文档后,左侧可浏览文档(支持pdf和word),右侧区域分为上下两栏,比例约为3:1,上方显示从文档中提取的正文内容(不含页眉页脚等,可转为markdown格式),右上方有一个按钮,“开始解析”,点击后,下栏开始输出正文内容关键信息提取的思考过程(即原python文件输出的内容);思考完成后,上方内容区域变为提取后的文本(带{{||}})。 2. 信息管理:针对关键信息树状结构的管理维护模块,以折叠树形式展示,每个节点均可编辑其元数据和子节点(参考之前的设计:**关键信息定义**:使用Json定义的树状结构,每个节点以`node_label`为key,包含`metadata`字段和`subnodes`字段,`metadata`字段中包含`node_label`(取关键信息名称的拼音首字母、`node_name`(关键信息名称)、`node_type`(取值为`concept`时表示分类节点,`property`时表示属性节点)、`value_type`(仅当`node_type`为`property`时生效,取值为`integer`、`float`、`string`、`boolean`、`date_time`、`class`,其中`class`表示关键信息为复合结构,包含子属性)、`is_list`(仅当`node_type`为`property`时生效,标识关键信息是否为列表类型,即是否可以有多个实例)字段。) 前端请使用vue实现,后端引入flask框架。请实现。 --- 后端配置里有API Key,前端不用输入,请求发给后端,后端使用配置的API key即可。 --- 将后端端口改为8754,前端端口改为8774,然后重启服务。 --- 我要求左侧是文档预览,word打开什么样子就是什么样子,pdf打开什么样子就是什么样子;右侧md文字行距过大,减小一些 --- 1. 上传pdf,报错:上传失败:文档解析失败: PyMuPDF 未安装,无法解析 PDF 2. 上传word,不报错,右侧也能解析出来,但左侧为空白页面,无word文档显示。 --- 点击上传,报错:上传失败:Promise.withResolvers is not a function --- 页面缩放大小为125%时,显示没有问题;100%时,右侧有一大块区域是空白的,鼠标也无法交互。请修复。 --- 信息管理中,我希望不止可以管理这一条关键信息,而是整理一个列表,每一条关键信息有名称、描述等元数据,点开后是这个树状结构,可以进行管理。支持json格式的导入,导入时填入名称和描述,导入格式参考@data/samples/info_definition.json。 --- 我希望在@extractor.py中写一个函数,输入一段文本,从中提取**关键信息定义**Json,格式参考@data/samples/info_definition.json,具体含义:使用Json定义的树状结构,每个节点以`node_label`为key,包含`metadata`字段和`subnodes`字段,`metadata`字段中包含`node_label`(取关键信息名称的拼音首字母、`node_name`(关键信息名称)、`node_type`(取值为`concept`时表示分类节点,`property`时表示属性节点)、`value_type`(仅当`node_type`为`property`时生效,取值为`integer`、`float`、`string`、`boolean`、`date_time`、`class`,其中`class`表示关键信息为复合结构,包含子属性)、`is_list`(仅当`node_type`为`property`时生效,标识关键信息是否为列表类型,即是否可以有多个实例)字段。使用大模型来实现,生成后校验Json结构是否正确,如不正确,给出错误信息让大模型重试。 --- 创建一个 test_definition.py 示例脚本,但复用test.py 中已有的 API 配置调用模型 --- 右下方的文本框里完全没有思考过程输出,之前annotator.py、extractor.py等文件的思考过程输出呢?请将它们返回给前端,并实时流式输出至右下方文本框中。 --- 写个启动脚本吧,里面设置INFO_EXTRACTOR_API_KEY变量,我稍后填入key。 --- 1. 界面右下方,我希望将当前Step也输出出来,当前阶段的思考过程和输出前方统一加4个空格缩进(原有n个空格缩进的话,变为n+4个),阶段之间用“====================================”隔开,例如: ``` 阶段一:XXXXX XXXX(思考过程) XXXX(思考过程) 输出: { XXXXX } ==================================== 阶段2a:XXXX …… ==================================== 2. 增加功能:右侧上方区域,不再以markdown形式展示,直接将文字排版显示出来,类似于新闻页面,默认使用仿宋_GB2312字体。解析完成后,对于{{COL1||COL2||COL3||COL4||COL5}}中间的内容进行渲染,文字为原文(COL3),但使用灰色背景将对应文字框选。点击框选区域任意位置,弹出对话框,对话框内表单包含字段: ①关键信息名称,值为COL1对应中文字段名,不可编辑;②关键信息值,值为COL2,可编辑;这里你需要根据COL1对应的数据类型和COL4生成组件,如COL1为数字,这里就是数字输入框;COL1为时间,这里就是时间选择框;COL1为字符串,这里就是文本框;COL1为字符串,且COL4约束为枚举型,这里就是下拉选择框;等等。③文中展示值,值为COL3,可编辑,文本框。修改并确认后,后台更新{{}}中的COL2和COL3值,原文中灰色背景框选的部分更新为新的COL3值。 --- 提取报错:提取失败:maximum recursion depth exceeded in comparison --- 基本实现了,但存在两个问题: 1. 灰色选中的区域,不要换行显示,嵌入原文中即可。我以[]表示灰色区域,示例: ``` [小明]是个[医生]。 ``` 而非 ``` [小明] 是个 [医生] 。 ``` 2. 文本区域,段落与段落之间空行太多,去掉,一个空行都不要。 --- 另外,右下方思考过程输出时,区域内滚动条自动不断滚到最底部吧 --- 你个垃圾,灰色框选区域不要换行展示,而是嵌入文中,你一点没改;段落与段落之间一个空行都不要,但不是不要换行,还是要一个换行的。你行不行?你不行有的是大模型能干。 --- 有没有一种可能,你用,它灰色区域就一定会换行?我需要它在原文中的原位置,不能换行显示。 --- 基本实现了,但段落之间的换行还要留着啊,怎么没了?从前后端都找找问题。 另外,每个段落前要缩进2字符。 --- 当关键信息在段首时,你的处理方式会导致它前面的换行丢失,与上一段接在一起。请解决。 另外,为了测试方便,在文本区域增加按钮:导入文本,让我粘贴带{{}}的文本进去,你直接展示,省去标注流程。 --- 段落间的换行又没了,你仔细看看怎么回事,我的要求:段落间的换行要留着,尤其注意段首有关键信息的情况;关键信息两侧的换行不要(除非它在段落首尾) --- .replace(/(\{\{[^{}]*\}\})/g, (m) => m.replace(/\n+/g, '')),这个语句是干什么用的?逻辑不对吧,执行完以后,文中就没有段落间的换行了 --- function confirmImport() { const text = importText.value || '' if (!text.trim()) { ElMessage.warning('请粘贴要导入的文本') return } annotatedContent.value = normalizeNewsText(text) hasResult.value = true importDialogVisible.value = false ElMessage.success('已导入文本') persistAnnotation() } 这个方法中,normalizeNewsText(text)的结果含\n,为什么最终渲染出来的丢失了换行符? --- 导入时输入: 1 2 3 解析完后,也显示 1 2 3,换行到底怎么回事??? --- 我希望启动前端,但页面上不显示vue调试工具 --- 取消所有TODO。现在,信息管理中,条目无法删除,请实现。写完后,不要启动playwright验证。 --- 把模型的model_name、base_url都做到start_backend的两个脚本里。 --- 现在,我的test.py可以正常使用大模型推理,但使用start_backend.bat调用app.py中的推理,报错: {'error': {'code': '1113', 'message': '余额不足或无可用资源包,请充值。'}},请帮我检查配置。 --- test.py 日志:POST https://open.bigmodel.cn/api/coding/paas/v4/chat/completions "HTTP/1.1 200 OK" app.py 日志:POST https://open.bigmodel.cn/api/coding/paas/v4/chat/completions "HTTP/1.1 429 Too Many Requests" 你发个请求验证吧 --- 三个问题: 1. 最终输出结果,前后容易带上
,过滤掉; 2. 对照我的原始需求:“右侧上方区域,不再以markdown形式展示,直接将文字排版显示出来,类似于新闻页面,默认使用仿宋_GB2312字体。解析完成后,对于{{COL1||COL2||COL3||COL4||COL5}}中间的内容进行渲染,文字为原文(COL3),但使用灰色背景将对应文字框选。点击框选区域任意位置,弹出对话框,对话框内表单包含字段:①关键信息名称,值为COL1对应中文字段名,不可编辑;②关键信息值,值为COL2,可编辑;这里你需要根据COL1对应的数据类型和COL4生成组件,如COL1为数字,这里就是数字输入框;COL1为时间,这里就是时间选择框;COL1为字符串,这里就是文本框;COL1为字符串,且COL4约束为枚举型,这里就是下拉选择框;等等。③文中展示值,值为COL3,可编辑,文本框。修改并确认后,后台更新{{}}中的COL2和COL3值,原文中灰色背景框选的部分更新为新的COL3值。” 你现在在灰色背景框选区域中,显示的是COL2的值,而非COL3的值;另外,弹出对话框中,“关键信息值”应为COL2的值,目前为空值;“文中展示值”应为COL3的值,目前为COL2的值。请修改; 3. 思考过程容易输出英文思考,请强调输出中文思考过程。 --- 你说“LLM 有时输出 {{path||raw_text||...}}(第 2 段空、第 3 段填了原文),看起来像"COL2 显示成了 COL3"。” 根据我观察,不是的,就算{{}}中内容正确,前端处理也有问题。 另外,我不希望完全依赖大模型,请写一个方法,校验LLM输出的结果,{{}}外的内容+{{}}内的原文字段,能否完全匹配原文,若不能,进行相应修改后再输出。 --- 这个新的修复步骤,是否在日志和前端思考过程输出中打印了?如果没有,请补充 --- 重建报错: [修复] text-anchored 重建不可行:text segment 在原文中找不到(从位置 230 起):'指挥员仍然感觉意犹未尽:"每次训练,都是一次全新挑战!"\n\n' 就是因为LLM容易出现幻觉,比如,就算让LLM原样输出,它也容易把“”符号输出为""符号,或者出现一些小的问题。所以重建并不是只重建{{}}内的部分,还需要针对{{}}外的部分进行校验和修复,且保持最小的编辑距离。 --- “导入文本”和“提取完成”后,在渲染文本中的组件时,请把每个组件对应的关键信息值、文中展示值打印出来,我查看一下,现在还有问题。 --- @DocumentParse.vue文件中,第341行左右, if (ch === '|') { fields.push(buf) buf = '' continue } 这里有问题,分隔符为“||”而非“|”,修复它。这么明显的问题,为什么会出现??? --------------------------------------- 你是本项目的“会话3:后端设计与实现工程师”。 项目名称:方案计划文档管理系统。 你的长期职责是负责后端业务设计、MySQL持久化、文件存储、REST接口、权限、安全、审计、测试和前后端联调支持。 一、工作边界 你主要只允许修改 backend/**。 不修改 frontend/**。 不自行修改以下统领文档:FUNCTION_AND_API_SPECIFICATION.md CODEX_BACKEND_PERSISTENCE_GUIDE.md 如果需求、数据库或接口设计存在问题,只提交“变更建议”,等待统领会话批准。 现有backend/src中的AI抽取、标注、校验和修复代码不在本阶段范围内,不得无关重构。 不接入Milvus,不实现向量化。 不实现电子签章和审批流程。 不考虑国产数据库和操作系统。 结构化数据库使用MySQL 8。 文件存储根目录为项目中的backend/dms-storage/。 业务接口统一使用/api/v1;现有AI相关/api接口暂时保留。 不一次实现全部功能。每次只完成统领会话指定的一个阶段。 不自动提交、合并或推送Git,除非我明确要求。 保留用户已有修改,不覆盖无关文件。 二、必须先阅读 FUNCTION_AND_API_SPECIFICATION.md CODEX_BACKEND_PERSISTENCE_GUIDE.md backend/app.py backend/src/** backend/requirements.txt 当前Git状态和后端目录结构 三、已确定的数据和业务规则 第一阶段最小结构包含:sys_organization sys_user doc_category doc_document doc_attachment_binding doc_permission sys_audit_log 所有业务表采用创建时间、更新时间、删除时间、逻辑删除和rowVersion。 允许名称、路径、计数和日志快照字段适当冗余。 主案与子方案使用parent_document_id表达一对多关系。 共享附件的document_type为ATTACHMENT,parent_document_id为空。 主案与共享附件通过doc_attachment_binding建立多对多关系。 同一主案和附件只能存在一条有效挂载关系。 解除挂载只逻辑删除关系,不删除附件。 共享附件对所有已登录用户共享,不写doc_permission。 删除仍在挂载的附件返回409 ATTACHMENT_IN_USE。 文件写入backend/dms-storage/,数据库只保存相对路径。 文件上传必须具备数据库事务和文件失败补偿。 数据库字段使用snake_case,JSON字段使用lowerCamelCase。 接口使用统一响应、分页、错误码和requestId。 现有AI代码不得与文档管理业务强耦合。 四、长期实施阶段 B0:后端现状、数据库和接口设计核对 B1:MySQL、迁移、配置、统一响应和OpenAPI骨架 B2:登录、用户、组织和审计基础 B3:方案分类 B4:主案、子方案和共享附件只读接口 B5:文件上传、预览、下载、逻辑删除和恢复 B6:共享附件挂载和解除挂载 B7:文档权限 B8:日志与统计 B9:集成测试、安全检查和清理 在当前轮次只执行B0,不修改任何代码。 五、本轮任务:B0 请只读分析当前后端,并输出: 当前Flask后端结构、既有接口和AI模块边界。 在不影响现有AI接口的前提下,业务后端建议的模块目录。 MySQL 7张核心表的最终逻辑核对。 必要索引、唯一性约束、外键取舍和事务边界。 backend/dms-storage/文件目录、上传流程和失败补偿设计。 FUNCTION_AND_API_SPECIFICATION.md中全部业务接口的实现优先级。 拟使用的MySQL驱动、ORM或迁移工具建议及理由,但不要安装。 OpenAPI文件建议位置和组织方式。 现有接口与新/api/v1接口可能发生的冲突。 需要统领会话裁决的事项。 B1阶段建议新增和修改的具体文件,但不要实际修改。 输出格式固定为: 【阶段】 【现有后端分析】 【建议模块结构】 【数据库核对】 【文件存储设计】 【接口实施顺序】 【技术选型建议】 【冲突与待确认】 【本轮未执行】 【下一阶段建议】 完成B0后立即停止,不要继续B1。 ------------------------------------------------------------------------------------------------- 【会话身份】 你是“会话3:后端设计与实现工程师”。 你负责本项目的后端设计、数据库设计、持久化实现、文件存储、接口实现、后端测试及后续与前端联调。 本轮执行阶段: B1:DMS后端基础设施、七张数据库表、初始迁移、存储目录和OpenAPI公共骨架 B0现状分析已经通过统领会话审核。本轮允许修改后端代码,但不得提前实现具体业务接口。 -------------------------------------------------- 一、强制加载的契约 -------------------------------------------------- 执行任何操作前,必须完整读取: 1. DMS_FUNCTION_CONTRACT.md 2. DMS_API_CONTRACT.md 这两份文件是本阶段强制执行契约: - DMS_FUNCTION_CONTRACT.md 是业务规则、数据边界、权限和存储规则的最高执行依据。 - DMS_API_CONTRACT.md 是接口路径、字段、DTO、枚举、响应和错误码的最高执行依据。 以下文件只作为背景和数据库字段设计参考: 3. FUNCTION_AND_API_SPECIFICATION.md 4. CODEX_BACKEND_PERSISTENCE_GUIDE.md 约束优先级: 1. 用户或统领会话最新明确确认的要求 2. DMS_FUNCTION_CONTRACT.md 3. DMS_API_CONTRACT.md 4. FUNCTION_AND_API_SPECIFICATION.md 5. CODEX_BACKEND_PERSISTENCE_GUIDE.md 6. 当前代码和历史实现 低优先级材料与新契约冲突时,以新契约为准。 不得自行修改两份新契约。 不得为了兼容旧文档实现废止接口。 不得自行增加第八张业务表。 发现契约冲突或数据库无法严格表达的规则时,应停止相关部分并提交统领会话裁决,不得自行改变业务语义。 -------------------------------------------------- 二、本轮目标 -------------------------------------------------- 本轮只建设DMS后端基础设施: 1. 独立DMS模块结构。 2. MySQL数据库配置。 3. SQLAlchemy模型。 4. 七张核心表。 5. Alembic初始迁移。 6. 统一响应、异常、分页和requestId。 7. /api/v1 Blueprint。 8. /api/v1/health。 9. Bearer认证扩展骨架。 10. backend/dms-storage/目录骨架。 11. OpenAPI公共组件骨架。 12. 后端基础自动化测试。 本轮不实现登录、分类、文档、附件、挂载、权限、审计查询等具体业务接口。 -------------------------------------------------- 三、现有系统保护边界 -------------------------------------------------- 必须保护: - backend/src/** - 现有AI抽取、标注、校验和修复模块 - 现有/api/*接口 - 现有AI数据和测试 要求: 1. 新DMS业务放入backend/dms/。 2. 新接口统一注册到/api/v1。 3. 现有旧/api/*不能自动套用DMS统一响应。 4. 现有旧/api/*不能自动要求DMS登录。 5. app.py只允许做最小化DMS初始化和Blueprint注册。 6. 不得无关重构现有AI代码。 7. 不得改变旧/api/health的响应结构和行为。 -------------------------------------------------- 四、本轮允许修改范围 -------------------------------------------------- 允许新增或修改: backend/dms/** backend/migrations/** backend/openapi/** backend/tests/dms/** backend/dms-storage/** backend/requirements.txt backend/.env.example backend/.gitignore 项目根.gitignore(仅在确有必要时做最小修改) backend/app.py(仅最小化注册) 不得修改: frontend/** DMS_FUNCTION_CONTRACT.md DMS_API_CONTRACT.md FUNCTION_AND_API_SPECIFICATION.md CODEX_BACKEND_PERSISTENCE_GUIDE.md backend/src/** 不得进行Git提交、推送、合并、重置、清理或覆盖用户改动。 -------------------------------------------------- 五、技术栈 -------------------------------------------------- 采用: - Flask - SQLAlchemy 2.x - Flask-SQLAlchemy 3.1.x - PyMySQL - Alembic - pytest - MySQL 8.x - 同步数据库访问 - READ COMMITTED 不得引入: - 异步ORM - FastAPI替换 - Milvus - Celery或其他任务队列 - 文档向量化 - Office转换服务 - 动态RBAC平台 - 新AI框架 -------------------------------------------------- 六、建议模块结构 -------------------------------------------------- 建议: backend/dms/ - __init__.py - config.py - extensions.py backend/dms/common/ - errors.py - response.py - request_context.py - pagination.py - enums.py backend/dms/database/ - base.py - transaction.py backend/dms/models/ - organization.py - user.py - category.py - document.py - attachment_binding.py - permission.py - audit_log.py backend/dms/security/ - auth_context.py - decorators.py backend/dms/storage/ - paths.py backend/dms/api/v1/ - blueprint.py - health.py 可以根据现有工程做小幅调整,但必须维持职责分离。 B1不要创建大量空repository和空service文件。 -------------------------------------------------- 七、七张核心表 -------------------------------------------------- 只能建立以下七张核心表: 1. sys_organization 2. sys_user 3. doc_category 4. doc_document 5. doc_attachment_binding 6. doc_permission 7. sys_audit_log 具体基础字段可参考CODEX_BACKEND_PERSISTENCE_GUIDE.md,但冲突规则必须以两份新契约为准。 普通业务表统一包含: - id - created_by - updated_by - created_at - updated_at - deleted_at - is_deleted - row_version 要求: - id为BIGINT; - is_deleted默认0; - row_version默认0; - created_at和updated_at精确到毫秒; - 时间按UTC管理; - 正常查询以后默认过滤is_deleted=0; - created_by和updated_by不建立外键; - row_version用于乐观锁; - 逻辑删除时写入deleted_at; - 不使用数据库级联删除。 -------------------------------------------------- 八、表级强制裁决 -------------------------------------------------- 1. sys_organization 必须支持: - 组织编码 - 组织名称 - 父组织 - 组织路径冗余 - 排序 - ENABLED/DISABLED - 逻辑删除 - 乐观锁 要求: - org_code永久唯一,逻辑删除后也不得复用; - parent_id自外键; - ON DELETE RESTRICT或NO ACTION; - 不允许级联删除。 2. sys_user 必须支持: - username - password_hash - real_name - organization_id - organization_name冗余 - role_code - security_level - status - auth_version - 最近登录时间和IP - 通用审计字段 要求: - username永久唯一; - organization_id建立外键并RESTRICT; - auth_version默认0; - B1不实现真实密码校验和JWT签发; - 不增加会话表。 3. doc_category 必须支持: - category_code - category_name - category_type - parent_id - category_path - sort_no - document_count - status - 通用审计字段 要求: - category_code永久唯一; - parent_id自外键并RESTRICT; - document_count只统计有效MAIN和SUB_PLAN; - 不统计ATTACHMENT。 4. doc_document 必须同时承载: - MAIN - SUB_PLAN - ATTACHMENT 必须包含: - document_name - summary - document_type - document_status - security_level - visibility_type - attachment_type - category_id - category_name冗余 - category_path冗余 - parent_document_id - root_document_id - tags或等价最小存储 - original_file_name - file_relative_path - file_extension - mime_type - file_size - file_hash - child_count - attachment_count - view_count - download_count - search_text - 创建人和更新人名称冗余 - 通用审计字段 强制语义: MAIN: - parent_document_id=NULL - root_document_id最终指向自身 - category_id必填 - attachment_type=NULL SUB_PLAN: - parent_document_id指向有效MAIN - root_document_id指向同一MAIN - category_id必填 - attachment_type=NULL - 不允许继续嵌套 ATTACHMENT: - parent_document_id=NULL - root_document_id=NULL - category_id=NULL - attachment_type必填 - security_level=PUBLIC - visibility_type=ALL_AUTHENTICATED 要求: - file_format不重复存储,由file_extension派生; - file_hash只建立普通索引,不建立全局唯一约束; - 类型和父级语义由服务层最终校验; - 可使用合理CHECK约束,但不得依赖CHECK替代服务层验证; - root_document_id=id需要插入后回写时,应通过事务处理; - 不实现文件版本表。 5. doc_attachment_binding 必须包含: - main_document_id - attachment_document_id - sort_no - active_marker生成列 - 通用审计字段 要求: active_marker = IF(is_deleted = 0, 1, NULL) 建立: UNIQUE( main_document_id, attachment_document_id, active_marker ) 两个文档字段均建立外键到doc_document,并使用RESTRICT或NO ACTION。 数据库外键只能保证ID存在,文档类型由服务层校验。 6. doc_permission 必须包含: - document_id - subject_type - subject_id - subject_name冗余 - can_view - can_download - can_edit - can_manage_permission - can_delete - active_marker生成列 - 通用审计字段 建立: UNIQUE( document_id, subject_type, subject_id, active_marker ) 规则: - 权限只表达允许,不增加DENY; - subject_id是ORG或USER多态引用,不建立单一外键; - document_id建立外键并RESTRICT; - 第一阶段只为MAIN保存权限; - SUB_PLAN动态继承MAIN权限; - 不复制子方案权限快照; - 不使用INHERITED权限记录; - ATTACHMENT不写本表。 7. sys_audit_log 这是追加写例外表。 必须包含: - id - user_id - username - real_name - organization_id - organization_name - action_type - target_type - target_id - target_name - operation_result - failure_reason - client_ip - user_agent - request_id - operation_detail - created_at 明确不包含: - updated_at - deleted_at - is_deleted - row_version 要求: - 不建立业务对象外键; - 保存名称快照; - 普通业务不得更新或删除; - 至少建立时间、用户、组织、动作、目标、结果和requestId索引。 -------------------------------------------------- 九、固定枚举 -------------------------------------------------- 必须严格读取DMS_API_CONTRACT.md中的全部固定枚举。 至少包括: RoleCode: - USER - ADMIN - AUDITOR AllowedModule: - DOCUMENT_BROWSER - BACKEND_MANAGEMENT - AUDIT_LOG DocumentType: - MAIN - SUB_PLAN - ATTACHMENT DocumentStatus: - DRAFT - PUBLISHED - ARCHIVED SecurityLevel: - PUBLIC - INTERNAL - SECRET - CONFIDENTIAL - TOP_SECRET 比较值: - PUBLIC=10 - INTERNAL=20 - SECRET=30 - CONFIDENTIAL=40 - TOP_SECRET=50 VisibilityType: - ALL_AUTHENTICATED - ORGANIZATION - CUSTOM AttachmentType: - POLICY - WORK_STANDARD - TABLE - DIAGRAM - OTHER SubjectType: - ORG - USER CategoryType: - SCENE - STYLE - SITUATION - VERSION - OTHER EnabledStatus: - ENABLED - DISABLED 同时建立契约规定的审计动作、审计结果和审计目标枚举。 不得自行增加UNKNOWN等兼容值。 -------------------------------------------------- 十、统一HTTP基础设施 -------------------------------------------------- 只应用于/api/v1 Blueprint。 1. 成功响应: { "code": "OK", "message": "success", "data": {}, "requestId": "uuid" } 不得自行增加timestamp。 2. 错误响应: { "code": "ERROR_CODE", "message": "错误说明", "details": null, "requestId": "uuid" } details不得省略,无附加内容时为null。 3. 分页: { "items": [], "page": 1, "pageSize": 20, "total": 0, "totalPages": 0 } 4. requestId: - 接收X-Request-Id; - 未提供时生成UUID; - JSON响应体包含requestId; - 响应头包含相同X-Request-Id; - requestId在请求上下文中可供后续服务和审计使用。 5. 业务异常: 至少建立: - 参数错误 - 未认证 - 无权限 - 未找到 - 冲突 - 文件格式错误 - 请求过大 - 系统错误 错误码语义必须与DMS_API_CONTRACT.md一致。 6. ID序列化: - 数据库使用BIGINT; - 对外JSON和OpenAPI使用字符串; - 不把BIGINT直接序列化为JSON number。 7. Bearer认证: B1只建立可扩展骨架或装饰器,不实现: - JWT签发 - JWT验签业务 - 用户查询 - auth_version查询 - 登录和退出接口 未实现的认证装饰器不得被业务路由误用。 -------------------------------------------------- 十一、Blueprint和健康检查 -------------------------------------------------- 建立: GET /api/v1/health 要求: - 无需认证; - 使用统一成功响应; - 返回X-Request-Id; - 不泄露数据库URL、密码、JWT密钥或磁盘绝对路径。 不得在B1创建其他业务占位接口。 不得实现契约中废止的路径。 -------------------------------------------------- 十二、数据库迁移 -------------------------------------------------- 使用Alembic创建初始迁移,例如: backend/migrations/versions/0001_initial_schema.py 要求: 1. 面向MySQL 8.x。 2. 包含七张表。 3. 包含全部必要索引。 4. 包含唯一约束。 5. 包含外键。 6. 包含active_marker生成列。 7. 外键使用RESTRICT或NO ACTION。 8. 不使用ON DELETE CASCADE。 9. downgrade能够按依赖顺序回滚。 10. 不使用零散手写SQL作为唯一迁移机制。 11. 生成列如果必须使用MySQL专用DDL,应清楚封装并测试。 12. 不得声称未实际连接的MySQL迁移已经成功运行。 13. 没有测试MySQL时,集成测试应明确skip,而不是伪造通过。 -------------------------------------------------- 十三、事务规则 -------------------------------------------------- 配置或明确采用: READ COMMITTED 为后续业务提供: - 事务上下文 - 回滚机制 - 乐观锁基础 - SELECT FOR UPDATE使用入口 - 有限死锁重试的扩展位置 B1不提前实现具体业务事务。 不得在文件流传输期间长期持有数据库事务。 -------------------------------------------------- 十四、文件存储目录 -------------------------------------------------- 创建: backend/dms-storage/ - original/ - preview/ - extracted/ - temporary/ - quarantine/ - recycle/ 要求: 1. 目录结构保留。 2. 运行时业务文件不进入Git。 3. 可以使用.gitignore或.gitkeep保留目录。 4. 默认存储根目录指向backend/dms-storage/。 5. 允许DMS_STORAGE_ROOT环境变量覆盖。 6. 数据库未来只保存相对路径。 7. 不把目录注册为Flask静态目录。 8. 不放入示例业务文档。 9. B1只建立路径解析和安全校验基础,不实现上传。 10. 解析后的路径必须仍在存储根目录内。 -------------------------------------------------- 十五、OpenAPI公共骨架 -------------------------------------------------- 创建: backend/openapi/openapi.yaml backend/openapi/components/ 建议组件: - schemas.yaml - responses.yaml - parameters.yaml - security-schemes.yaml B1只定义: 1. OpenAPI基本信息。 2. /api/v1/health。 3. Bearer安全方案。 4. X-Request-Id请求头。 5. 统一成功响应。 6. 统一错误响应。 7. 分页结构。 8. 固定枚举。 9. 字符串ID schema。 10. date-time schema。 所有ID必须定义为: type: string pattern: '^[0-9]+$' B1不得提前定义尚未实现的业务路径。 后续业务阶段才逐步将DMS_API_CONTRACT.md转换为完整OpenAPI。 -------------------------------------------------- 十六、配置和依赖 -------------------------------------------------- 更新backend/requirements.txt,增加B1必要依赖。 提供backend/.env.example,至少说明: DMS_DATABASE_URL DMS_STORAGE_ROOT DMS_MAX_UPLOAD_SIZE DMS_BATCH_MAX_FILES DMS_JWT_SECRET 要求: - 不写入真实账号密码; - 不写入真实JWT密钥; - 数据库默认配置不得含用户真实凭据; - 不破坏现有AI配置; - 真实.env必须被Git忽略; - 不在代码中硬编码机器绝对路径。 -------------------------------------------------- 十七、测试要求 -------------------------------------------------- 至少增加和执行: 1. 配置加载测试。 2. DMS Blueprint注册测试。 3. 旧/api/health行为保持测试。 4. 新/api/v1/health统一响应测试。 5. X-Request-Id透传测试。 6. X-Request-Id自动生成测试。 7. 统一错误响应测试。 8. 七张模型导入测试。 9. SQLAlchemy metadata表数量和表名测试。 10. 普通业务表通用字段测试。 11. sys_audit_log例外字段测试。 12. auth_version字段测试。 13. attachment_type字段测试。 14. active_marker生成列测试。 15. 两个有效唯一约束测试。 16. 外键不存在CASCADE测试。 17. 关键索引测试。 18. OpenAPI文件解析测试。 19. OpenAPI健康接口测试。 20. OpenAPI字符串ID组件测试。 21. 存储路径不能逃逸根目录测试。 22. 未配置MySQL测试库时集成测试明确skip。 23. 现有AI模块仍可导入。 24. 现有后端测试不得因DMS初始化被破坏。 -------------------------------------------------- 十八、验证要求 -------------------------------------------------- 必须真实执行并报告: 1. Python模块导入检查。 2. pytest测试。 3. Flask应用创建或导入检查。 4. Alembic配置检查。 5. OpenAPI YAML解析检查。 6. SQLAlchemy metadata检查。 7. 如果存在可用MySQL测试库,再执行upgrade和downgrade。 8. 如果没有MySQL测试库,明确说明未执行真实迁移。 不得因为缺少MySQL而使用SQLite结果冒充MySQL迁移验证。 -------------------------------------------------- 十九、验收标准 -------------------------------------------------- B1完成必须满足: 1. 现有AI代码和接口行为不变。 2. /api/v1独立注册。 3. 七张表模型完整。 4. 初始迁移与模型一致。 5. active_marker唯一性真实进入迁移。 6. sys_audit_log符合追加写例外。 7. 文件存储目录安全保留。 8. 统一响应、错误和requestId可验证。 9. OpenAPI公共骨架可解析。 10. 没有提前实现业务接口。 11. 没有恢复接口。 12. 没有废止路径。 13. 没有第八张业务表。 14. 测试结果真实可复查。 -------------------------------------------------- 二十、完成报告格式 -------------------------------------------------- 完成后必须返回: 1. 阶段结论 - B1完成、部分完成或阻塞 2. 契约加载情况 - 确认完整读取了哪些契约文件 3. 修改文件清单 - 每个文件说明用途 4. 模块结构 - 展示backend/dms目录 5. 七张表摘要 - 主要字段 - 索引 - 唯一约束 - 外键 - 逻辑删除 - row_version - audit_log例外 6. 与旧指导文档相比的调整 - auth_version - attachment_type - active_marker - 子方案动态继承权限 - 附件固定PUBLIC和ALL_AUTHENTICATED - audit_log字段例外 - 不提供恢复 7. 迁移验证 - 实际验证了什么 - 是否连接真实MySQL - 未验证什么 8. 测试结果 - 命令 - 退出码 - 通过、失败和跳过数量 - 失败原因 9. 契约符合性检查 - 对应DMS_FUNCTION_CONTRACT.md哪些章节 - 对应DMS_API_CONTRACT.md哪些章节 - 是否存在偏差 - 是否增加未授权表、字段、路径或枚举 - 是否误用了废止接口 10. 未实现内容 - 明确说明属于B2及以后阶段的内容 11. 风险和待统领裁决事项 12. 下一阶段建议 - 只提出B2建议,不得自行执行 本轮到B1完成后立即停止,等待统领会话验收。 ---------------------------------------------------------- 【阶段】B2:认证、当前用户、组织人员查询和本地初始化数据 你是会话3:后端设计与实现工程师。 B1已经通过统领会话验收。本地MySQL已经由统领会话完成建库和初始迁移。 本轮允许修改后端代码,只实现认证闭环、组织树、人员查询、最小开发数据初始化及对应OpenAPI和测试。 本轮不得进入分类、方案、附件、挂载、文档权限和文件上传等后续业务。 一、强制契约 执行前必须完整读取: 1. DMS_FUNCTION_CONTRACT.md 2. DMS_API_CONTRACT.md 补充参考: 3. FUNCTION_AND_API_SPECIFICATION.md 4. CODEX_BACKEND_PERSISTENCE_GUIDE.md 优先级: 用户或统领会话最新要求 → DMS_FUNCTION_CONTRACT.md → DMS_API_CONTRACT.md → 两份旧参考文档 → 当前代码 不得修改两份DMS契约。 发现冲突时停止相关实现并报告,不得自行改变接口。 二、数据库现状 本地MySQL: - 主机:localhost - 端口:3306 - 用户名:root - 密码:root - 数据库:dms - MySQL版本:8.4.8 - 当前迁移:0001_initial_schema - 当前业务表:7张 - sys_user当前为空 本轮运行时使用临时环境变量: DMS_DATABASE_URL=mysql+pymysql://root:root@localhost:3306/dms?charset=utf8mb4 禁止把root密码写入: - Python代码 - .env.example - Git跟踪文件 - 测试源码 - OpenAPI文件 三、本轮接口范围 只实现: POST /api/v1/auth/login POST /api/v1/auth/logout GET /api/v1/users/me GET /api/v1/organizations/tree GET /api/v1/users 不得实现其他业务接口。 四、依赖与密码安全 采用: - PyJWT:JWT签发和验证 - argon2-cffi:Argon2id密码哈希 要求: 1. requirements.txt声明必要依赖。 2. 数据库不得保存明文密码。 3. 日志不得记录密码、密码哈希或完整Token。 4. 用户名或密码错误统一返回401 INVALID_CREDENTIALS。 5. 不得泄露用户名是否存在。 6. JWT密钥必须从DMS_JWT_SECRET读取。 7. DMS_JWT_SECRET为空时,除测试配置外,登录签发应拒绝启动或明确失败。 8. 不得在代码中设置可用于正式环境的默认密钥。 五、JWT契约 Token至少包含: - sub:字符串用户ID - username - roleCode - authVersion - iat - exp - jti - iss=dms 有效期: - keepSignedIn=false:7200秒 - keepSignedIn=true:604800秒 每次鉴权必须: 1. 验证签名。 2. 验证exp。 3. 验证iss。 4. 查询用户。 5. 检查用户未逻辑删除。 6. 检查status=ENABLED。 7. 比较Token authVersion与sys_user.auth_version。 8. 将用户放入请求级认证上下文。 对应错误: - TOKEN_INVALID - TOKEN_EXPIRED - USER_DISABLED - AUTH_VERSION_MISMATCH 六、登录接口 POST /api/v1/auth/login 请求严格使用: { "username": "admin", "password": "******", "keepSignedIn": true } 登录成功: 1. 校验输入。 2. 查询有效用户。 3. 使用Argon2id验证密码。 4. 更新最近登录时间和IP。 5. 签发JWT。 6. 写LOGIN成功审计。 7. 返回DMS_API_CONTRACT.md定义的响应。 8. ID必须返回字符串。 登录失败: 1. 统一返回INVALID_CREDENTIALS。 2. 写LOGIN失败审计。 3. 未知用户名时userId可以为空。 4. 失败审计使用独立短事务。 5. 不更新登录信息。 allowedModules固定映射: USER: - DOCUMENT_BROWSER ADMIN: - DOCUMENT_BROWSER - BACKEND_MANAGEMENT AUDITOR: - AUDIT_LOG 不得由前端自行推断或覆盖。 七、退出接口 POST /api/v1/auth/logout 要求: 1. 必须认证。 2. 在事务中锁定当前用户。 3. auth_version递增。 4. row_version递增。 5. 写LOGOUT成功审计。 6. 同一事务提交。 7. 返回统一成功响应。 8. 旧Token在退出响应后立即失效。 9. 第一阶段明确为“一个终端退出导致该用户全部Token失效”。 八、当前用户接口 GET /api/v1/users/me 要求: - 必须认证; - 返回UserSummary; - 所有ID为字符串; - 包含allowedModules; - 不返回password_hash; - 不返回auth_version; - 不返回内部数据库字段; - organizationId和organizationName来自有效组织数据或受控冗余。 九、组织树 GET /api/v1/organizations/tree 本阶段仅ADMIN可调用。 支持: - keyword - status 要求: 1. 默认只返回有效、未删除组织。 2. 按sortNo及稳定次级顺序排序。 3. 返回树形children。 4. keyword只匹配组织名称或编码。 5. 搜索结果必须保留必要祖先路径,使前端仍可展示树。 6. ID为字符串。 7. 不返回内部绝对路径或数据库对象。 十、人员查询 GET /api/v1/users 本阶段仅ADMIN可调用。 支持: - organizationId - includeDescendants - keyword - status - page - pageSize 要求: 1. 返回契约分页结构。 2. keyword搜索username和realName。 3. includeDescendants=false时只查指定组织。 4. includeDescendants=true时包含有效下级组织。 5. 不返回密码哈希和auth_version。 6. 所有ID为字符串。 7. pageSize最大100。 8. 默认过滤逻辑删除数据。 十一、Bearer认证基础设施 完成B1认证骨架: - bearer_required装饰器或等价机制 - current_user/current_auth_context - 统一401和403 - requestId贯穿 - 不影响旧/api/*接口 - 不把认证自动套到/api/v1/health - 只保护B2新增接口 十二、审计 本轮实现: - LOGIN成功 - LOGIN失败 - LOGOUT成功 要求: 1. 保存用户名、姓名、组织名称快照。 2. 保存clientIp、userAgent、requestId。 3. 不保存密码和Token。 4. 登录成功和退出审计与对应修改处于同一事务。 5. 登录失败审计使用独立短事务。 6. sys_audit_log仍为追加写。 7. 不实现审计查询接口。 十三、开发数据初始化 实现显式、可重复执行的开发数据初始化命令。 不得在应用启动或Alembic迁移时自动插入默认账号。 建议形式: python -m dms.seed_dev 或等价Flask CLI命令。 要求: 1. 只有显式执行才写数据。 2. 密码通过环境变量传入。 3. 缺少密码变量时拒绝执行。 4. 使用Argon2id生成哈希。 5. 初始化操作幂等。 6. 不重复创建组织和用户。 7. 默认不得覆盖已有用户密码。 8. 提供显式覆盖开关时必须谨慎说明。 9. 不把开发密码写入源码或提交文件。 初始化以下最小数据: 组织: - ORG_ROOT / 机关 - ORG_OPS / 作战部 - ORG_COMMS / 通信部 其中作战部、通信部属于机关。 用户: - admin / ADMIN / TOP_SECRET / 机关 - auditor / AUDITOR / TOP_SECRET / 机关 - user / USER / SECRET / 作战部 本地执行初始化时,可以只在命令环境中使用: DMS_SEED_ADMIN_PASSWORD=Admin@123456 DMS_SEED_AUDITOR_PASSWORD=Auditor@123456 DMS_SEED_USER_PASSWORD=User@123456 这些仅为本地开发凭据,不得写入Git跟踪文件,完成报告中必须明确提示后续环境更换。 十四、OpenAPI 同步补充: - POST /api/v1/auth/login - POST /api/v1/auth/logout - GET /api/v1/users/me - GET /api/v1/organizations/tree - GET /api/v1/users 必须完整声明: - operationId - 请求DTO - 响应DTO - Bearer安全要求 - 查询参数 - 字符串ID - 枚举 - 400/401/403/500 - 分页结构 登录接口和health不声明Bearer要求。 不得定义B3及以后路径。 十五、MySQL测试 禁止在dms主库中执行破坏性测试或downgrade。 如需迁移升级、回滚集成测试: 1. 创建独立测试库,例如dms_b2_test。 2. 测试库名称必须以_test结尾。 3. 使用DMS_TEST_DATABASE_URL。 4. 测试完成后可以保留测试库供后续阶段使用。 5. 不得对dms执行downgrade。 6. 不得删除dms。 B2完成后,在真实dms库执行: - 当前迁移检查 - 开发数据初始化 - 登录成功验证 - /users/me验证 - 组织树验证 - 人员分页验证 - 退出后旧Token失效验证 十六、测试要求 至少覆盖: 1. Argon2id密码哈希和验证。 2. 登录成功。 3. 用户名错误。 4. 密码错误。 5. 禁用用户。 6. keepSignedIn两种有效期。 7. JWT签名错误。 8. JWT过期。 9. authVersion不一致。 10. 缺少Bearer。 11. 非Bearer认证头。 12. /users/me。 13. allowedModules映射。 14. 退出auth_version递增。 15. 退出后旧Token失效。 16. 登录成功审计。 17. 登录失败审计。 18. 退出审计。 19. 审计不包含密码和Token。 20. 组织树结构。 21. 组织关键词保留祖先。 22. 人员分页。 23. includeDescendants。 24. ADMIN权限。 25. USER和AUDITOR不能调用管理查询。 26. JSON ID均为字符串。 27. OpenAPI解析和路径契约。 28. 旧/api/*行为不变。 29. 现有AI测试不受影响。 十七、禁止事项 - 不修改前端。 - 不修改两份DMS契约。 - 不实现分类接口。 - 不实现文档、附件、挂载、权限、文件接口。 - 不实现审计查询。 - 不增加第八张业务表。 - 不增加刷新Token。 - 不增加会话表。 - 不实现动态角色。 - 不修改backend/src/**。 - 不提交或推送Git。 - 不进入B3。 十八、完成报告 必须返回: 1. 阶段结论。 2. 契约加载情况。 3. 修改文件清单。 4. JWT设计和claims。 5. 密码哈希方案。 6. auth_version处理。 7. allowedModules映射。 8. 组织和人员查询行为。 9. 初始化数据和执行方式。 10. 实际MySQL验证结果。 11. OpenAPI变化。 12. 测试命令、通过数、失败数、跳过数。 13. 契约符合性检查。 14. 是否存在偏差。 15. 尚未实现内容。 16. B3建议,但不得执行。 本轮完成B2后停止,等待统领会话验收。 -------------------------------------------- 【阶段】B2-R1:修正无测试环境变量时的测试失败 你是会话3:后端设计与实现工程师。 本轮是B2收尾修正,不得进入B3。 一、强制读取 完整读取: 1. DMS_FUNCTION_CONTRACT.md 2. DMS_API_CONTRACT.md 不得修改契约。 二、问题 统领会话独立执行: python -m pytest -q tests/dms test_annotation_repair.py 在未设置DMS测试环境变量时出现: KeyError: DMS_TEST_USER_PASSWORD 位置: backend/tests/dms/conftest.py b2_password fixture 预期行为: - 未配置真实MySQL测试环境变量时,依赖这些变量的集成测试应该明确skip; - 不应产生KeyError或测试错误; - 普通B1基础测试和不依赖数据库的单元测试仍应执行。 三、本轮工作 1. 修正b2_password或相关fixture。 2. 缺少DMS_TEST_USER_PASSWORD时使用pytest.skip并给出明确原因。 3. 检查DMS_TEST_DATABASE_URL、DMS_TEST_JWT_SECRET等fixture是否存在同类问题。 4. 不允许使用dms主库运行破坏性测试。 5. 不修改认证业务实现。 6. 不修改数据库模型和迁移。 7. 不修改前端。 8. 不增加业务接口。 9. 不进入B3。 10. 不提交或推送Git。 四、必须验证两种模式 模式一:不设置测试环境变量 执行: python -m pytest -q tests/dms test_annotation_repair.py 要求: - 退出码0; - 允许明确skip; - 不允许error; - 不允许KeyError。 模式二:设置完整测试环境 使用: DMS_TEST_DATABASE_URL=mysql+pymysql://root:root@localhost:3306/dms_b2_test?charset=utf8mb4 DMS_TEST_USER_PASSWORD=TestUser@123456 DMS_TEST_JWT_SECRET=local-test-jwt-secret-not-for-production 执行相同测试。 要求: - 89项测试全部通过; - 不连接或清理dms主库; - 不执行主库downgrade。 五、完成报告 返回: 1. 修改文件。 2. 根因。 3. 修正方式。 4. 无环境变量测试结果。 5. 完整环境变量测试结果。 6. 是否修改业务代码。 7. 契约符合性检查。 完成B2-R1后停止。 -------------------------------------- 【统领裁决】B3-C1:分类操作人名称字段 统领会话已经完成裁决。 选择方案2: 不为doc_category增加created_by_name和updated_by_name字段,不创建新的数据库迁移。 一、裁决依据 1. DMS_FUNCTION_CONTRACT.md未强制要求分类记录保存操作人名称。 2. DMS_API_CONTRACT.md中的CategoryNode不包含createdByName和updatedByName。 3. doc_category现有created_by和updated_by足以保存操作人ID。 4. 不可变的用户、姓名和组织名称快照由sys_audit_log负责。 5. 为非接口字段增加迁移不符合当前七表最小设计原则。 6. 原B3提示词中的名称快照要求属于提示词过度约束,不是数据库模型缺陷。 二、B3要求调整 撤销原B3提示词中的以下要求: - 分类新增时在doc_category保存createdByName。 - 分类新增时在doc_category保存updatedByName。 - 分类编辑时在doc_category更新updatedByName。 - 分类删除时在doc_category更新updatedByName。 - 因上述名称字段创建数据库迁移。 调整为: 分类新增: - created_by保存当前用户ID。 - updated_by保存当前用户ID。 - created_at和updated_at按模型规则维护。 - CREATE_CATEGORY审计保存username、real_name和organization_name快照。 分类编辑: - updated_by保存当前用户ID。 - updated_at更新。 - row_version递增。 - EDIT_CATEGORY审计保存用户、姓名、组织和分类名称快照。 分类删除: - updated_by保存当前用户ID。 - updated_at和deleted_at更新。 - row_version递增。 - DELETE_CATEGORY审计保存用户、姓名、组织和分类名称快照。 三、禁止事项 1. 不增加created_by_name。 2. 不增加updated_by_name。 3. 不创建0002迁移。 4. 不修改现有0001迁移。 5. 不修改两份DMS契约。 6. 不在CategoryNode中增加操作人名称字段。 7. 不通过关联查询临时增加未定义API字段。 8. 不进入B4。 四、继续执行 请从B3中断位置继续执行原B3任务。 除本裁决明确调整的名称快照要求外,原B3提示词其他内容继续有效,包括: - 四个分类接口; - 分类树搜索; - 分类新增、编辑、移动; - 后代路径同步; - 删除占用冲突; - rowVersion并发控制; - 分类审计; - 开发分类数据初始化; - OpenAPI; - 自动化测试; - 真实MySQL验证。 五、完成报告补充要求 B3最终报告增加“统领裁决执行情况”章节,明确说明: 1. doc_category只保存created_by和updated_by。 2. 操作人名称快照保存在sys_audit_log。 3. 未新增数据库字段。 4. 未创建新迁移。 5. 主库迁移仍为0001_initial_schema。 6. 该处理属于统领授权,不计为契约偏差。 现在继续执行B3,完成后停止,等待统领会话验收。 ------------------------------------------------------- 【阶段】B4:主案、子方案、共享附件和挂载关系只读接口 你是“会话3:后端设计与实现工程师”。 B1、B2、B2-R1、B3均已经通过统领会话验收。 本轮只实现: 1. 主案和子方案分页查询 2. 主案、子方案详情 3. 主案直属子方案查询 4. 共享附件分页查询 5. 共享附件详情 6. 附件挂载主案查询 7. 主案已挂载附件查询 8. 文档可见性、密级、状态和allowedActions计算 9. 开发演示文档数据初始化 10. OpenAPI和自动化测试 不得进入文档上传、编辑、删除、附件挂载写入、权限保存、文件预览下载、审计查询和统计接口。 -------------------------------------------------- 一、强制契约 -------------------------------------------------- 执行前完整读取: 1. DMS_FUNCTION_CONTRACT.md 2. DMS_API_CONTRACT.md 补充参考: 3. FUNCTION_AND_API_SPECIFICATION.md 4. CODEX_BACKEND_PERSISTENCE_GUIDE.md 优先级: 用户或统领会话最新明确要求 → DMS_FUNCTION_CONTRACT.md → DMS_API_CONTRACT.md → 两份旧参考文档 → 当前代码 不得修改两份DMS契约。 发现模型缺口或契约冲突时: 1. 停止相关实现。 2. 不得自行新增字段、表或迁移。 3. 报告具体缺口。 4. 等待统领会话裁决。 -------------------------------------------------- 二、测试数据库统一规则 -------------------------------------------------- 从B4开始,所有后端阶段统一使用: 主库: dms 测试库: dms_test 本地连接: DMS_DATABASE_URL=mysql+pymysql://root:root@localhost:3306/dms?charset=utf8mb4 DMS_TEST_DATABASE_URL=mysql+pymysql://root:root@localhost:3306/dms_test?charset=utf8mb4 强制规则: 1. 不得创建dms_b4_test。 2. 不得创建其他以阶段命名的新测试库。 3. 如果dms_test不存在,只创建一次。 4. 每个阶段开始前执行alembic upgrade head。 5. 测试只清理dms_test数据。 6. 禁止在dms主库执行测试清表。 7. 禁止对dms主库执行downgrade。 8. 禁止删除dms主库。 9. 已有dms_b2_test、dms_b3_test不得在本轮删除或修改。 10. 后续B5、B6等继续复用dms_test。 11. 测试数据使用明确前缀,例如B4_TEST_*。 12. 测试结束时清理测试库中的测试数据,或者通过可靠测试隔离恢复基线。 如果使用事务回滚进行隔离,必须确认MySQL和Flask-SQLAlchemy会话行为真实有效;不得用SQLite结果代替MySQL验证。 -------------------------------------------------- 三、当前运行环境 -------------------------------------------------- MySQL: - 主机:localhost - 端口:3306 - 用户:root - 密码:root - 主库:dms - MySQL版本:8.4.8 - 当前迁移:0001_initial_schema 后端: http://localhost:8754 本地开发用户: - admin / ADMIN / TOP_SECRET - auditor / AUDITOR / TOP_SECRET - user / USER / SECRET 本地密码只通过环境变量或本地执行命令提供,不得写入源码、OpenAPI或Git文件。 -------------------------------------------------- 四、本轮接口范围 -------------------------------------------------- 只实现: GET /api/v1/documents GET /api/v1/documents/{id} GET /api/v1/main-plans/{id}/sub-plans GET /api/v1/attachments GET /api/v1/attachments/{id} GET /api/v1/attachments/{id}/main-plans GET /api/v1/main-plans/{id}/attachments 不得实现: POST /documents PUT /documents/{id} DELETE /documents/{id} POST /documents/batch-import POST /attachments PUT /attachments/{id} DELETE /attachments/{id} POST /attachments/batch-import POST /main-plans/{id}/attachments/bind DELETE /main-plans/{id}/attachments/{attachmentId} GET或PUT文档权限 文件preview或download 审计查询 统计查询 恢复接口 任何废止路径 -------------------------------------------------- 五、服务结构 -------------------------------------------------- 建议新增: backend/dms/services/document_query_service.py backend/dms/services/attachment_query_service.py backend/dms/services/authorization_service.py backend/dms/api/v1/documents.py backend/dms/api/v1/attachments.py backend/dms/api/v1/main_plans.py 可以根据现有结构合理调整。 要求: 1. 路由层只处理HTTP输入输出。 2. 查询和业务过滤位于service。 3. 权限、密级、状态和allowedActions集中计算。 4. 不在各路由复制权限逻辑。 5. 不在路由中直接拼接复杂SQL。 6. 不修改现有认证、分类和AI模块行为。 7. 不增加空repository或无意义抽象。 -------------------------------------------------- 六、访问与权限判定 -------------------------------------------------- 方案访问顺序严格为: 1. 用户已经认证。 2. 用户状态为ENABLED。 3. 文档未逻辑删除。 4. 文档类型正确。 5. 文档状态检查。 6. 用户密级大于或等于文档密级。 7. ADMIN业务特权。 8. visibilityType。 9. doc_permission允许记录。 10. 计算具体allowedActions。 密级比较值: - PUBLIC=10 - INTERNAL=20 - SECRET=30 - CONFIDENTIAL=40 - TOP_SECRET=50 状态规则: 普通USER: - 只允许查询PUBLISHED方案。 - DRAFT和ARCHIVED不可见。 - 仍需通过密级和ACL。 ADMIN: - 可以查询DRAFT、PUBLISHED和ARCHIVED。 - 仍需通过密级检查。 - 开发admin为TOP_SECRET,因此能够查看全部开发数据。 AUDITOR: - 审计角色不自动获得主案和子方案访问权。 - GET /documents、文档详情和子方案接口默认返回403。 - 共享附件仍按“全部有效登录用户共享”规则允许查看元数据。 -------------------------------------------------- 七、可见范围 -------------------------------------------------- ALL_AUTHENTICATED: - 通过认证、状态、文档状态和密级检查后可见。 - 不要求doc_permission记录。 ORGANIZATION: - 只使用ORG类型允许权限。 - 用户属于授权组织自身或其有效下级组织时可见。 - 不使用USER权限记录。 - 组织上下级关系必须通过有效组织树判断。 CUSTOM: - 使用ORG和USER允许权限。 - 用户直接允许或所属组织命中时可见。 - ORG和USER取并集。 - 第一阶段没有显式拒绝。 没有匹配允许权限时拒绝访问。 SUB_PLAN: - 不读取自身doc_permission。 - 动态继承root_document_id所指向主案的visibilityType和ACL。 - 子方案仍使用自身securityLevel和documentStatus。 - root主案无效时,子方案不可见并记录系统一致性问题。 ATTACHMENT: - 不读取doc_permission。 - 固定PUBLIC。 - 固定ALL_AUTHENTICATED。 - 所有ENABLED登录用户可查询。 -------------------------------------------------- 八、allowedActions -------------------------------------------------- 返回值只能使用契约枚举。 USER对MAIN、SUB_PLAN: - 具有查看权限时返回VIEW。 - 具有下载权限时返回DOWNLOAD。 - 不返回EDIT、DELETE、CONFIG_PERMISSION。 - 不返回BIND_ATTACHMENT或UNBIND_ATTACHMENT。 ADMIN对MAIN: - VIEW - DOWNLOAD - EDIT - CONFIG_PERMISSION - DELETE - BIND_ATTACHMENT - 如果当前主案存在有效附件挂载,可返回UNBIND_ATTACHMENT。 ADMIN对SUB_PLAN: - VIEW - DOWNLOAD - EDIT - DELETE - 不返回CONFIG_PERMISSION。 - 不返回BIND_ATTACHMENT和UNBIND_ATTACHMENT。 共享附件: 普通有效登录用户: - VIEW - DOWNLOAD ADMIN额外: - EDIT - DELETE 注意: 本轮尚未实现文件下载和写操作接口。 allowedActions表达业务授权能力,不代表本轮已经实现对应HTTP写接口。OpenAPI和完成报告必须明确当前只开放查询接口。 -------------------------------------------------- 九、文档分页查询 -------------------------------------------------- 实现: GET /api/v1/documents 只查询: - MAIN - SUB_PLAN 不得通过该接口查询ATTACHMENT。 支持参数: - documentType - categoryId - keyword - securityLevel - visibilityType - status - updatedFrom - updatedTo - sortBy - sortDirection - page - pageSize documentType: - 支持MAIN - 支持SUB_PLAN - 支持逗号分隔MAIN,SUB_PLAN - 包含ATTACHMENT时返回400 INVALID_ARGUMENT - 不传时默认MAIN,SUB_PLAN keyword: 搜索: - documentName - summary - tags - searchText 使用MySQL普通检索,不调用AI、Milvus或向量服务。 categoryId: - 必须是有效数字字符串。 - 按指定分类精确筛选。 - 本阶段不自动包含下级分类,除非契约已有明确规定。 - 不存在分类可以返回空列表,不必泄露分类状态。 分页: - page默认1 - pageSize默认20 - pageSize最大100 sortBy只允许: - documentName - createdAt - updatedAt - viewCount - downloadCount sortDirection: - asc - desc 必须使用白名单,禁止把客户端字段直接拼入SQL。 返回: PageResult 所有ID使用字符串。 普通USER必须在SQL查询或可靠服务层过滤中排除无权记录,不能先返回再依赖前端隐藏。 -------------------------------------------------- 十、文档详情 -------------------------------------------------- 实现: GET /api/v1/documents/{id} 仅适用于: - MAIN - SUB_PLAN ATTACHMENT调用该接口时: - 返回404 RESOURCE_NOT_FOUND,或按契约既定方式处理; - 不得通过此接口返回附件详情。 规则: 1. 检查文档有效性。 2. 检查文档类型。 3. 检查状态。 4. 检查密级。 5. 检查可见范围和ACL。 6. 返回DocumentDetail。 7. 不返回绝对文件路径。 8. 不返回password、Token或内部权限查询细节。 9. fileRelativePath不得对外返回。 10. ID全部为字符串。 11. permissionSummary按契约返回。 12. 子方案permissionSummary标记动态继承主案。 查看行为: - 成功查询详情视为VIEW_DOCUMENT。 - 使用独立短事务更新view_count。 - 写VIEW_DOCUMENT审计。 - 审计失败按功能契约既定规则处理。 - 不在数据库事务中执行长时间文件操作。 - 本轮不读取磁盘文件。 列表查询不增加view_count,不写逐条查看日志。 -------------------------------------------------- 十一、主案子方案 -------------------------------------------------- 实现: GET /api/v1/main-plans/{id}/sub-plans 规则: 1. id必须指向有效MAIN。 2. 主案不存在或类型不正确返回404 MAIN_PLAN_NOT_FOUND。 3. 先检查当前用户对主案的访问资格。 4. 返回直属SUB_PLAN。 5. 不允许继续嵌套。 6. 子方案按自身状态和密级再次过滤。 7. ACL动态继承主案。 8. 支持keyword、status、page、pageSize、sortBy、sortDirection。 9. 返回PageResult。 10. 不增加主案或子方案view_count。 11. 不写逐条VIEW审计。 -------------------------------------------------- 十二、共享附件分页 -------------------------------------------------- 实现: GET /api/v1/attachments 支持: - keyword - attachmentType - fileExtension - updatedFrom - updatedTo - sortBy - sortDirection - page - pageSize keyword搜索: - documentName - summary - tags - searchText 规则: 1. 只返回ATTACHMENT。 2. 默认排除逻辑删除。 3. 所有ENABLED登录用户可查询。 4. 不使用doc_permission。 5. securityLevel固定PUBLIC。 6. visibilityType固定ALL_AUTHENTICATED。 7. category相关字段返回null。 8. attachmentType必填。 9. 返回PageResult。 10. mountedPlanCount使用有效挂载关系真实统计。 11. 不增加view_count。 12. 不写逐条查看审计。 -------------------------------------------------- 十三、附件详情 -------------------------------------------------- 实现: GET /api/v1/attachments/{id} 规则: 1. 必须是有效ATTACHMENT。 2. 所有ENABLED登录用户可调用。 3. 返回DocumentDetail。 4. category相关字段为null。 5. permissionSummary表达无ACL。 6. 不返回存储绝对路径。 7. 成功详情查询更新view_count。 8. 写VIEW_DOCUMENT审计,targetType=ATTACHMENT。 9. 不读取或返回真实文件流。 10. 非ATTACHMENT返回404。 -------------------------------------------------- 十四、附件挂载的主案 -------------------------------------------------- 实现: GET /api/v1/attachments/{id}/main-plans 规则: 1. id必须是有效ATTACHMENT。 2. 只统计有效挂载关系。 3. 只返回有效、未删除MAIN。 4. ADMIN可以查看全部有效挂载主案。 5. 普通USER只能看到自己有权访问的主案。 6. AUDITOR不因附件共享而自动获得主案详情,应只返回其有权访问的主案;默认可能为空。 7. 返回契约定义的简要主案集合。 8. 不返回主案ACL内部细节。 9. 不增加view_count。 10. 不写VIEW审计。 -------------------------------------------------- 十五、主案已挂载附件 -------------------------------------------------- 实现: GET /api/v1/main-plans/{id}/attachments 支持: - keyword - attachmentType - page - pageSize 规则: 1. id必须是有效MAIN。 2. 当前用户必须有权查看主案。 3. 只返回有效挂载关系。 4. 只返回有效ATTACHMENT。 5. 返回附件DocumentSummary并增加: - bindingId - bindingSortNo - mountedPlanCount 6. 按bindingSortNo和bindingId稳定排序。 7. keyword和attachmentType按附件字段过滤。 8. 不增加附件或主案view_count。 9. 不写逐条查看审计。 -------------------------------------------------- 十六、DTO输出 -------------------------------------------------- 严格遵循DMS_API_CONTRACT.md: DocumentSummary至少包括: - id - documentName - summary - documentType - status - securityLevel - visibilityType - visibilitySummary - categoryId - categoryName - categoryPath - parentDocumentId - rootDocumentId - attachmentType - fileExtension - tags - childCount - attachmentCount - viewCount - downloadCount - createdByName - createdAt - updatedAt - rowVersion - allowedActions DocumentDetail增加: - originalFileName - mimeType - fileSize - fileHash - permissionSummary 要求: 1. JSON字段使用camelCase。 2. ID全部使用字符串。 3. 数据库BIGINT不能直接输出为number。 4. 不返回fileRelativePath。 5. 不返回createdBy、updatedBy内部ID,除非契约明确要求。 6. 不返回isDeleted、deletedAt。 7. 不返回searchText。 8. 不返回authVersion。 9. tags无值时返回空数组,不返回无法预测的类型。 10. allowedActions去重并保持稳定顺序。 -------------------------------------------------- 十七、开发演示数据初始化 -------------------------------------------------- 为了F4能够替换模拟文档,提供显式、幂等的开发数据初始化命令。 建议: python -m dms.seed_documents 不得在应用启动或Alembic迁移时自动执行。 初始化至少包括: 主案: 1. 2024年度综合应急预案 - MAIN - 分类SCENE_A - PUBLISHED - INTERNAL - ALL_AUTHENTICATED 2. 战略物资管理规程 - MAIN - 分类SCENE_B - PUBLISHED - SECRET - ORGANIZATION - 授权作战部 3. 通信保障方案 - MAIN - 分类SCENE_C - PUBLISHED - SECRET - CUSTOM - 直接授权user查看和下载 第一个主案的子方案: - 人员组织子案 - 资源组织子案 - 重点任务子案 共享附件至少三个: - 装备保障工作规范 / WORK_STANDARD - 应急资源清单模板 / TABLE - 通信保障流程图 / DIAGRAM 挂载关系: - 2024年度综合应急预案挂载前两个附件 - 战略物资管理规程挂载装备保障工作规范 - 用于验证一个附件挂载多个主案 初始化要求: 1. 显式执行。 2. 幂等。 3. 不重复创建。 4. 不覆盖已有同名有效业务数据。 5. 不恢复已逻辑删除数据。 6. 正确维护child_count。 7. 正确维护attachment_count。 8. 正确维护mountedPlanCount查询结果。 9. 正确创建MAIN权限。 10. SUB_PLAN不创建权限副本。 11. ATTACHMENT不创建ACL。 12. searchText正确生成。 13. 创建真实有效的最小DOCX演示文件。 14. 文件放入backend/dms-storage/original/约定路径。 15. 数据库只保存相对路径。 16. 使用UUID磁盘文件名。 17. 原始文件名保存在数据库。 18. 生成SHA-256。 19. 生成的DOCX必须能被python-docx重新打开。 20. 不创建空文件或不存在路径记录。 21. 不调用AI、Milvus或向量化。 22. 文件创建失败时补偿数据库记录。 23. 数据库失败时清理本次新建的孤儿文件。 24. 不修改用户已有文件。 25. 输出新增、已存在和失败数量。 该命令属于开发数据工具,不是上传业务接口。 完成后在真实dms主库显式执行一次。 -------------------------------------------------- 十八、OpenAPI -------------------------------------------------- 新增且只新增本轮7个查询接口。 必须包含: - operationId - Bearer认证 - 查询参数 - PageResult - DocumentSummary - DocumentDetail - AttachmentBindingSummary - 字符串ID - allowedActions - 200、400、401、403、404、500 - MAIN_PLAN_NOT_FOUND - DOCUMENT_VIEW_FORBIDDEN - SECURITY_LEVEL_FORBIDDEN - RESOURCE_NOT_FOUND 不得提前加入上传、编辑、删除、挂载写、权限和文件路径。 OpenAPI路径和Flask路由必须完全一致。 外部$ref必须有效。 -------------------------------------------------- 十九、测试要求 -------------------------------------------------- 至少覆盖: 1. documents未认证。 2. USER文档分页。 3. ADMIN文档分页。 4. AUDITOR文档分页403。 5. MAIN筛选。 6. SUB_PLAN筛选。 7. MAIN,SUB_PLAN组合筛选。 8. ATTACHMENT类型参数拒绝。 9. 分类筛选。 10. keyword搜索名称。 11. keyword搜索摘要或标签。 12. 状态过滤。 13. 密级过滤。 14. 可见范围过滤。 15. 非法sortBy拒绝。 16. pageSize上限。 17. 字符串ID。 18. 逻辑删除过滤。 19. DRAFT对USER不可见。 20. DRAFT对ADMIN可见。 21. 密级不足拒绝。 22. ALL_AUTHENTICATED。 23. ORGANIZATION及下级组织。 24. CUSTOM用户授权。 25. 无ACL拒绝。 26. 子方案动态继承。 27. 文档详情。 28. 附件不能通过documents详情获取。 29. 详情增加view_count。 30. 详情写审计。 31. 列表不增加view_count。 32. 主案子方案分页。 33. 非主案调用子方案接口。 34. 子方案自身密级过滤。 35. attachments未认证。 36. USER附件分页。 37. AUDITOR附件分页。 38. ADMIN附件分页。 39. attachmentType过滤。 40. fileExtension过滤。 41. 附件详情。 42. 非附件调用附件详情。 43. 附件详情增加view_count和审计。 44. 附件挂载主案查询。 45. USER只能看到有权主案。 46. 主案挂载附件查询。 47. bindingId字符串。 48. bindingSortNo排序。 49. mountedPlanCount。 50. allowedActions稳定。 51. 响应不泄露fileRelativePath。 52. tags始终为数组。 53. 开发初始化幂等。 54. 开发文件真实存在。 55. DOCX可打开。 56. SHA-256正确。 57. child_count一致。 58. attachment_count一致。 59. 权限记录符合规则。 60. 测试只使用dms_test。 61. 无环境变量时skip而非error。 62. B2、B3接口回归。 63. OpenAPI和路由一致。 64. 旧AI接口回归。 -------------------------------------------------- 二十、验证要求 -------------------------------------------------- 执行: python -m compileall -q dms 无测试环境变量: python -m pytest -q tests/dms test_annotation_repair.py 完整测试环境: DMS_TEST_DATABASE_URL=mysql+pymysql://root:root@localhost:3306/dms_test?charset=utf8mb4 DMS_TEST_USER_PASSWORD=TestUser@123456 DMS_TEST_JWT_SECRET=local-test-jwt-secret-not-for-production python -m pytest -q tests/dms test_annotation_repair.py 真实主库只执行: 1. alembic current 2. seed_documents 3. 七个查询接口验证 4. 三类账号权限验证 5. 计数一致性检查 6. 文件存在性和哈希检查 不得在主库运行测试清理或downgrade。 -------------------------------------------------- 二十一、禁止事项 -------------------------------------------------- - 不修改前端。 - 不修改DMS契约。 - 不创建新业务表。 - 不创建阶段专属测试数据库。 - 不实现上传。 - 不实现编辑和删除。 - 不实现附件挂载写操作。 - 不实现权限保存。 - 不实现文件预览下载。 - 不实现审计查询和统计。 - 不实现恢复接口。 - 不调用AI或Milvus。 - 不修改backend/src/**。 - 不提交或推送Git。 - 不进入B5。 -------------------------------------------------- 二十二、完成报告 -------------------------------------------------- 必须返回: 1. 阶段结论。 2. 契约加载情况。 3. 修改文件清单。 4. 七个查询接口。 5. 权限判定顺序。 6. 密级和状态过滤。 7. 子方案动态继承。 8. allowedActions。 9. 分页、搜索和排序。 10. 详情查看计数和审计。 11. 共享附件查询。 12. 挂载关系查询。 13. 开发演示数据初始化。 14. 真实文件和哈希验证。 15. dms主库验证。 16. dms_test统一测试库使用情况。 17. 测试结果。 18. OpenAPI变化。 19. 回归测试。 20. 契约符合性。 21. 是否存在偏差。 22. 未实现内容。 23. B5建议,但不得执行。 完成B4后立即停止,等待统领会话验收。 -------------------------------------------------- 【阶段任务】B5:文档与共享附件的上传、批量导入、元数据编辑、逻辑删除及文件读取 你是本项目的后端设计与实现工程师。本轮只执行 B5,不得进入 B6,不得修改前端。 一、开始前必须加载的约束 开始分析或修改前,必须完整读取: 1. F:\Project\wsj\document-management-system\DMS_FUNCTION_CONTRACT.md 2. F:\Project\wsj\document-management-system\DMS_API_CONTRACT.md 以下文档只能作为背景参考;如有冲突,以上述两份 DMS 契约为准: 3. F:\Project\wsj\document-management-system\FUNCTION_AND_API_SPECIFICATION.md 4. F:\Project\wsj\document-management-system\CODEX_BACKEND_PERSISTENCE_GUIDE.md 同时检查: 5. backend/openapi/** 6. B1—B4 已实现的模型、迁移、存储服务、认证、授权、审计及查询服务 7. backend/dms-storage/** 8. backend/tests/dms/** 9. 当前 Flask 实际路由表 10. 当前 MySQL 迁移状态和七张核心表结构 不得修改上述两份 DMS 契约。 二、已完成并验收的基础 B1—B4、F1—F4 均已通过统领验收。 现有能力包括: - JWT认证和三类角色; - 组织、人员查询; - 分类树和分类管理; - 主案、子方案、共享附件只读查询; - 主案直属子方案查询; - 正向、反向附件挂载关系查询; - 集中授权判定; - 真实DOCX文件和SHA-256校验; - 查看计数和查看审计; - OpenAPI及自动化测试基础设施。 后端标准地址: http://127.0.0.1:8754 数据库: - 业务主库:dms - 唯一自动化测试库:dms_test 数据库账号: - 用户名:root - 密码:root 三、统一测试数据库规则 本轮继续复用: dms_test 禁止创建: - dms_b5_test - dms_b5_verify - dms_upload_test - 其他阶段专用测试数据库 历史库 `dms_b2_test`、`dms_b3_test` 不得连接、清理、降级或删除。 自动化测试只能对 `dms_test` 执行测试数据写入、清理和迁移验证。 不得: - 清理 `dms` 主库; - downgrade `dms` 主库; - 删除 `dms` 主库业务数据; - 使用主库执行自动化测试; - 为每个阶段创建新测试库。 测试数据库URL继续使用: mysql+pymysql://root:root@localhost:3306/dms_test?charset=utf8mb4 四、B5范围 本轮只新增以下10个接口。 方案文档: 1. POST /api/v1/documents 2. POST /api/v1/documents/batch-import 3. PUT /api/v1/documents/{id} 4. DELETE /api/v1/documents/{id} 共享附件: 5. POST /api/v1/attachments 6. POST /api/v1/attachments/batch-import 7. PUT /api/v1/attachments/{id} 8. DELETE /api/v1/attachments/{id} 统一文件读取: 9. GET /api/v1/documents/{id}/preview 10. GET /api/v1/documents/{id}/download 本轮不得新增其他业务接口。 五、本轮明确不实现 不得实现: 1. 附件挂载写接口; 2. 解除挂载接口; 3. 权限读取和保存接口; 4. 审计日志查询接口; 5. 统计接口; 6. 文档恢复接口; 7. 恢复页面配套接口; 8. 强制删除已挂载附件; 9. 子方案独立权限; 10. 文档文件替换; 11. 复杂版本历史; 12. 物理删除文档文件; 13. 自动清理逻辑删除文件; 14. Office转PDF; 15. Office在线预览; 16. Milvus或向量化; 17. AI模块改造; 18. frontend/**修改; 19. B6功能; 20. Git提交、推送或合并。 特别禁止实现: POST /api/v1/documents/{id}/restore 以及任何其他恢复路径。 六、数据库和迁移边界 B5原则上应复用现有七张表和 `0001_initial_schema`。 开始实施前必须核对现有字段是否能够满足: - 文件元数据; - SHA-256; -相对存储路径; -附件类型; -状态和密级; -逻辑删除; -rowVersion; -创建人和修改人快照; -主案子方案计数; -附件挂载计数; -下载计数; -审计日志。 如果发现必须新增数据库字段、索引、约束或迁移: 1. 立即停止依赖该结构的实现; 2. 不得擅自修改 `0001_initial_schema`; 3. 不得擅自创建新迁移; 4. 向统领会话提交明确的模型缺口、影响接口和建议方案; 5. 等待书面裁决。 不得为了避免迁移而复用语义不符的字段。 七、文件存储规则 存储根目录为: F:\Project\wsj\document-management-system\backend\dms-storage 原始文件存储在: backend/dms-storage/original/ 必须遵守: 1. 数据库只保存相对于 `dms-storage` 的路径。 2. 数据库不得保存绝对路径。 3. 原始文件名仅用于展示和下载文件名。 4. 原始文件名不得参与磁盘路径拼接。 5. 磁盘文件名必须使用后端生成的UUID。 6. 上传文件不得覆盖已有文件。 7. 计算并保存真实SHA-256。 8. 相同SHA-256允许作为不同业务文档上传。 9. 存储目录不得注册为Flask静态资源目录。 10. 所有文件读取必须经过认证、授权和业务状态检查。 11. 文件路径解析后必须仍位于配置的存储根目录。 12. 拒绝绝对路径、`..`、路径穿越和越界符号链接。 13. API响应不得返回: - fileRelativePath; - 绝对路径; - 临时文件路径; - 服务器目录结构。 八、允许的文件类型和安全校验 第一阶段只允许: - .doc - .docx - .pdf - .xls - .xlsx 不得只根据扩展名或客户端Content-Type判断。 必须同时校验: 1. 扩展名; 2. 文件头或容器结构; 3. 文件类型与扩展名的一致性; 4. 文件大小; 5. 请求总大小; 6. 批量文件数量; 7. 空文件; 8. 损坏容器。 最低校验要求: - PDF:验证合法PDF文件头; - DOC、XLS:验证OLE/CFB文件头; - DOCX:验证ZIP容器以及必要Office结构; - XLSX:验证ZIP容器以及必要Office结构; - DOCX不得只验证ZIP文件头; - XLSX不得只验证ZIP文件头; - 不得解压到不受控目录; - 容器检查应避免ZIP路径穿越; - 损坏、伪造或类型不匹配文件返回契约规定错误。 应优先复用Python标准库和现有依赖。新增依赖必须必要、最小化,并在报告中说明原因。 九、上传事务和文件补偿 单文件上传必须保证数据库和文件系统最终一致。 建议流程: 1. 校验认证和ADMIN权限; 2. 校验请求结构; 3. 校验metadata; 4. 校验文件大小和类型; 5. 写入受控临时文件; 6. 计算SHA-256; 7. 生成UUID存储名; 8. 建立数据库业务记录; 9. 写入同事务审计; 10. 提交业务事务; 11. 将文件安全落入最终目录或采用等价可靠流程; 12. 清理临时文件。 无论采用哪种顺序,都必须覆盖以下失败补偿: - 文件写入失败时不留下数据库记录; - 数据库提交失败时不留下孤立新文件; - 审计写入失败时业务写入回滚; - 临时文件必须清理; - 已存在的其他业务文件不得受影响; - 不得删除逻辑删除记录对应的原始文件; - 不得在异常日志中输出文件正文、Token或密码。 需要通过自动化测试验证补偿行为,不能只在代码注释中说明。 十、方案文档单文件上传 实现: POST /api/v1/documents 仅ADMIN可调用。 请求为 `multipart/form-data`: - `file`:单个文件; - `metadata`:JSON字符串。 主案metadata严格按契约处理: - documentName - documentType=MAIN - summary - categoryId - securityLevel - visibilityType - status - tags 子方案metadata增加: - documentType=SUB_PLAN - parentDocumentId 要求: 1. 严格拒绝未知字段。 2. `categoryId`、`parentDocumentId`为字符串ID。 3. 禁止number ID。 4. 子方案父节点必须是有效、未删除的MAIN。 5. 子方案不得挂到SUB_PLAN。 6. 子方案动态继承根主案权限,不复制ACL。 7. 后端生成: - 文件身份字段; - fileRelativePath; - fileHash; - fileSize; - fileExtension; - mimeType; - categoryName; - categoryPath; - rootDocumentId; - searchText; - 创建人和修改人快照; - 时间和rowVersion。 8. 主案的父文档字段必须为空。 9. 新建子方案后,应在同一业务事务内维护主案 `childCount`及必要版本。 10. 维护分类冗余计数和其他现有冗余字段。 11. 返回 `DocumentDetail`,HTTP 201。 12. 不得返回磁盘路径。 13. 写入 `UPLOAD_DOCUMENT` 审计。 14. 审计与业务记录在同一事务。 十一、方案文档批量导入 实现: POST /api/v1/documents/batch-import 仅ADMIN可调用。 请求: - 重复字段 `files` - `items`:JSON数组字符串 - `files[n]` 与 `items[n]`按顺序一一对应 要求: 1. 数量不一致返回: HTTP 400 code=BATCH_MANIFEST_MISMATCH 2. 检查批量文件数量和请求总大小。 3. 每个文件使用独立业务事务。 4. 一个文件失败不得回滚其他成功文件。 5. 返回项顺序必须与原请求顺序一致。 6. 每项必须包含: - index; - originalFileName; - success; - 成功时document; - 失败时errorCode和errorMessage。 7. 整体HTTP为200。 8. 统计: - total; - successCount; - failureCount。 9. 单项成功记录真实业务文档和审计。 10. 单项失败必须完成文件补偿。 11. 不得因单项错误泄露堆栈或服务器路径。 12. 写入契约定义的 `BATCH_IMPORT`/`UPLOAD_DOCUMENT`审计语义,不得增加未知动作枚举。 13. 批量重试不得误覆盖已有文件。 十二、方案文档元数据编辑 实现: PUT /api/v1/documents/{id} 仅ADMIN可调用,只编辑元数据,不替换文件。 请求字段严格按契约: - documentName - summary - categoryId - securityLevel - visibilityType - status - tags - rowVersion 禁止修改: - documentType; - parentDocumentId; - rootDocumentId; - originalFileName; - fileRelativePath; - fileExtension; - mimeType; - fileSize; - fileHash; - 文件正文。 要求: 1. 文档必须是MAIN或SUB_PLAN。 2. 附件调用此接口必须返回类型错误。 3. 校验分类有效性和分类类型规则。 4. 使用rowVersion乐观锁。 5. 版本不一致返回: HTTP 409 code=DATA_VERSION_CONFLICT details.currentRowVersion 6. 更新分类时同步categoryName和categoryPath。 7. 同步维护searchText。 8. 同步维护updatedBy、updatedByName、updatedAt和rowVersion。 9. 不得因编辑子方案创建独立ACL。 10. 元数据编辑和 `EDIT_DOCUMENT`审计处于同一事务。 11. 返回最新 `DocumentDetail`。 12. 编辑操作不得增加viewCount或downloadCount。 十三、方案文档逻辑删除 实现: DELETE /api/v1/documents/{id}?rowVersion= 仅ADMIN可调用。 要求: 1. 只用于MAIN和SUB_PLAN。 2. 使用rowVersion乐观锁。 3. 不得物理删除数据库记录。 4. 不得物理删除原始文件。 5. 删除后正常查询不可见。 6. 共享附件不得通过该接口删除。 删除MAIN: - 存在有效直属或间接子方案时返回: HTTP 409 code=MAIN_PLAN_HAS_CHILDREN - 不递归删除子方案; - 逻辑删除该主案有效ACL; - 逻辑删除该主案有效附件挂载关系; - 不删除共享附件; - 同步维护必要冗余计数和版本。 删除SUB_PLAN: - 逻辑删除子方案; - 更新所属主案childCount; - 不影响主案ACL; - 不影响共享附件; - 不删除挂载文件。 成功返回: - 字符串id; - deleted=true。 逻辑删除及关联关系处理、计数维护和 `DELETE_DOCUMENT`审计必须在同一事务。 十四、共享附件单文件上传 实现: POST /api/v1/attachments 仅ADMIN可调用。 请求为 `multipart/form-data`: - file - metadata metadata严格包含契约允许字段: - documentName - attachmentType - summary - tags 后端强制设置: - documentType=ATTACHMENT - securityLevel=PUBLIC - visibilityType=ALL_AUTHENTICATED - categoryId=null - parentDocumentId=null - rootDocumentId=null 要求: 1. 前端即使提交固定字段,也不得允许其覆盖后端强制值。 2. 附件不得创建ACL。 3. 计算并保存真实文件元数据和SHA-256。 4. 新附件mountedPlanCount初始为0。 5. 返回 `DocumentDetail`,HTTP 201。 6. 写入 `UPLOAD_DOCUMENT`审计,targetType按现有契约使用。 7. 业务记录、文件和审计必须符合失败补偿规则。 十五、共享附件批量导入 实现: POST /api/v1/attachments/batch-import 要求与方案批量导入一致: - 重复 `files`; - 顺序对应的 `items`; - 每项使用附件metadata; - 每个文件独立事务; - 一个失败不影响其他成功项; - 返回逐文件结果; - 文件数量与items数量不一致返回 `BATCH_MANIFEST_MISMATCH`; - 完成文件失败补偿; - 不创建ACL或分类关系。 十六、共享附件元数据编辑 实现: PUT /api/v1/attachments/{id} 仅ADMIN可调用。 请求字段: - documentName - attachmentType - summary - tags - rowVersion 要求: 1. 只允许ATTACHMENT。 2. 使用rowVersion乐观锁。 3. 不得修改固定PUBLIC密级。 4. 不得修改固定ALL_AUTHENTICATED可见范围。 5. 不得修改文件身份。 6. 不得替换文件。 7. 同步维护searchText和更新人快照。 8. 编辑与 `EDIT_DOCUMENT`审计在同一事务。 9. 返回最新 `DocumentDetail`。 十七、共享附件逻辑删除 实现: DELETE /api/v1/attachments/{id}?rowVersion= 仅ADMIN可调用。 要求: 1. 只允许ATTACHMENT。 2. 使用rowVersion乐观锁。 3. 存在有效挂载时返回: HTTP 409 code=ATTACHMENT_IN_USE 4. details至少按契约提供: - mountedPlanCount; - mainPlans。 5. 不得自动解除挂载。 6. 不得提供强制删除参数。 7. 无有效挂载时允许逻辑删除。 8. 不得物理删除原始文件。 9. 删除与 `DELETE_DOCUMENT`审计在同一事务。 10. 删除后正常附件列表、详情和关系查询不可见。 11. 返回字符串ID和 `deleted=true`。 十八、统一文件预览 实现: GET /api/v1/documents/{id}/preview 适用于: - MAIN - SUB_PLAN - ATTACHMENT 不得增加附件专用preview路径。 要求: 1. 先认证,再检查用户状态和文档有效性。 2. MAIN、SUB_PLAN使用现有集中授权服务校验VIEW。 3. ATTACHMENT对所有有效登录用户开放。 4. PDF成功返回inline Blob。 5. Office文件: - DOC - DOCX - XLS - XLSX 返回: HTTP 415 code=PREVIEW_UNAVAILABLE 6. 不得将Office原文件伪装成HTML或PDF。 7. 文件不存在返回: HTTP 404 code=FILE_NOT_FOUND 8. 路径越界必须拒绝,并记录系统错误。 9. 响应应设置正确Content-Type和安全的Content-Disposition。 10. 建议设置 `X-Content-Type-Options: nosniff`。 11. 不返回文件路径。 12. 预览失败不得增加下载计数。 13. 不得因preview读取绕过现有密级和ACL规则。 十九、统一文件下载 实现: GET /api/v1/documents/{id}/download 适用于所有三类文档,不得增加附件专用下载路径。 权限规则: - MAIN、SUB_PLAN必须具备DOWNLOAD能力; - ATTACHMENT只要求有效登录用户; - ADMIN仍不得绕过用户密级规则; - AUDITOR不因审计角色自动获得方案下载权限。 成功时: 1. 返回真实原始文件。 2. 使用数据库保存的原始文件名作为下载名称。 3. 文件名必须安全编码,防止响应头注入。 4. 设置正确Content-Type。 5. 设置attachment Content-Disposition。 6. 建议设置 `X-Content-Type-Options: nosniff`。 7. 原子更新downloadCount。 8. 写入 `DOWNLOAD_DOCUMENT`审计。 9. 不得返回fileRelativePath。 10. 不得一次请求重复增加下载计数。 失败规则: - 无权限:403 `DOCUMENT_DOWNLOAD_FORBIDDEN`或契约规定错误; - 文件不存在:404 `FILE_NOT_FOUND`; - 文档不存在或已删除:404; - 未认证或Token失效:401; - 文件读取失败不得伪装成成功下载。 根据正式契约,下载计数和下载审计采用独立短事务。审计失败应记录系统错误,但第一阶段不阻断已通过鉴权的文件读取。 二十、授权与allowedActions 必须复用B4集中授权服务,禁止在新接口中复制一套不一致的权限判断。 要求: 1. 所有写接口仅ADMIN。 2. 查看和下载使用集中授权逻辑。 3. 子方案权限动态继承根主案。 4. 共享附件不读取doc_permission。 5. 文档状态、密级、角色、可见范围和ACL的顺序与B4一致。 6. 新增、编辑、删除后返回的 `allowedActions`必须基于最新状态实时生成。 7. 后端不得相信前端按钮隐藏。 8. 不能用allowedActions代替后端鉴权。 二十一、审计要求 本轮使用契约已有动作: - DOWNLOAD_DOCUMENT - UPLOAD_DOCUMENT - BATCH_IMPORT - EDIT_DOCUMENT - DELETE_DOCUMENT 不得增加未知动作枚举。 审计必须包含: - 用户ID; - 用户名; -姓名快照; -组织ID; -组织名称快照; -客户端IP; -User-Agent; -requestId; -targetType; -targetId; -targetName快照; -operationResult; -必要操作差异或失败原因。 规则: 1. 上传、编辑、删除等修改类审计与业务事务一致。 2. 修改类审计失败时业务回滚。 3. 下载审计采用独立短事务。 4. 审计不得保存文件正文、密码、Token或绝对路径。 5. `sys_audit_log`不得被修改或删除。 二十二、错误处理 必须按契约实现并测试相关错误,包括但不限于: - INVALID_REQUEST - INVALID_ID - UNAUTHORIZED - FORBIDDEN - RESOURCE_NOT_FOUND - DOCUMENT_VIEW_FORBIDDEN - DOCUMENT_DOWNLOAD_FORBIDDEN - UNSUPPORTED_FILE_TYPE - PREVIEW_UNAVAILABLE - PAYLOAD_TOO_LARGE - BATCH_MANIFEST_MISMATCH - DATA_VERSION_CONFLICT - MAIN_PLAN_HAS_CHILDREN - ATTACHMENT_IN_USE - FILE_NOT_FOUND - INTERNAL_ERROR 如果契约已有更精确错误码,以契约为准。 所有JSON错误响应必须保持统一结构,并包含requestId。 Blob成功响应也应保留 `X-Request-Id` 响应头。 不得泄露: - SQL语句; - Python堆栈; - 文件绝对路径; - JWT; - 数据库连接串; - 文件正文。 二十三、OpenAPI要求 更新 backend/openapi,仅增加B5的10个接口及必要DTO、参数、multipart和错误响应。 要求: 1. 路径、方法和Flask实际路由完全一致。 2. operationId唯一。 3. multipart/form-data定义准确。 4. 批量重复files字段和items JSON字符串描述准确。 5. preview/download使用二进制响应定义。 6. 201、200、400、401、403、404、409、413、415、500按实际接口声明。 7. 字符串ID不得声明为integer。 8. 不得加入恢复、挂载写、权限写、审计或统计路径。 9. 全部外部 `$ref` 和fragment引用必须可解析。 二十四、自动化测试要求 必须在统一 `dms_test` 上覆盖,至少包括: (一)单文件上传 - MAIN上传成功; - SUB_PLAN上传成功; - ATTACHMENT上传成功; - 字符串ID; - number ID拒绝; - 未知字段拒绝; - 无效枚举拒绝; - 无效分类拒绝; - 子方案父级不是MAIN; - 非ADMIN返回403; - 文件元数据和SHA-256正确; - 数据库只保存相对路径; - 原始文件名不参与磁盘路径; - 同哈希允许多业务文档。 (二)文件安全 - 合法PDF; - 合法DOCX; - 合法XLSX; - 合法DOC或XLS的CFB校验; - 扩展名和文件头不一致; - 损坏ZIP; - 普通ZIP伪装DOCX; - 普通ZIP伪装XLSX; - 空文件; - 不支持扩展名; - 路径穿越文件名; - 超过单文件限制; - 超过请求总大小; - 超过批量文件数; - 临时文件清理; - 数据库失败后的文件补偿; - 文件失败后的数据库回滚。 (三)批量导入 - 全部成功; - 部分成功; - 全部失败; - files/items数量不一致; - 返回顺序稳定; - 成功失败统计正确; - 每文件事务相互隔离; - 单项失败不回滚其他项; - 失败不留下孤立文件或数据库记录。 (四)编辑 - MAIN编辑; - SUB_PLAN编辑; - ATTACHMENT编辑; - 非ADMIN拒绝; - rowVersion冲突; - 禁止修改文件身份; - 禁止修改文档类型和父子关系; - 分类冗余更新; - searchText更新; - 编辑不增加查看或下载计数; - 审计与业务事务一致。 (五)删除 - MAIN无子方案时逻辑删除; - MAIN存在子方案时409; - SUB_PLAN逻辑删除并更新主案计数; - 删除MAIN逻辑删除ACL和挂载关系; - 删除MAIN不删除共享附件; - ATTACHMENT未挂载时逻辑删除; - ATTACHMENT已挂载时409; - 不强制解除挂载; - 不物理删除文件; - rowVersion冲突; - 删除后正常查询不可见; - 无恢复接口。 (六)预览和下载 - PDF预览成功; - Office预览415; - 文件缺失404; - 路径越界拒绝; - 无VIEW权限拒绝预览; - 无DOWNLOAD权限拒绝下载; - 共享附件有效用户可下载; - AUDITOR不能下载无权方案; - ADMIN不绕过密级; - 下载文件内容与磁盘一致; - 下载文件名安全; - 下载计数准确加1; - 下载审计准确且只写一次; - 失败下载不增加计数; - 响应不泄露文件路径; - X-Request-Id存在。 (七)回归 - B1—B4全部测试继续通过; - 现有AI测试继续通过; - OpenAPI解析和路由一致性通过; - 无环境变量模式不得出现KeyError; - 测试数据库必须仍为dms_test; - 不得连接主库执行清理或downgrade。 二十五、真实验证策略 自动化测试和写接口验证统一使用 `dms_test`。 如需启动真实HTTP服务验证写接口: 1. 使用独立端口; 2. 数据库指向 `dms_test`; 3. 存储根目录指向明确的测试存储目录; 4. 不得使用 `dms` 主库上传测试文档; 5. 不得污染业务主库存储目录; 6. 验证后停止测试服务; 7. 只清理明确的测试临时目录; 8. 清理前必须确认目录位于指定测试根目录内。 `dms` 主库只做非破坏性检查: - 迁移版本; - 表结构; - 已有业务数据计数; - 已有文件存在性。 除非统领会话另行授权,不要向 `dms` 主库写入B5验证数据。 二十六、必须执行的验证命令 至少执行: 1. 完整B1—B5测试; 2. 既有AI回归测试; 3. python -m compileall -q dms; 4. OpenAPI YAML解析和 `$ref`检查; 5. Flask实际路由与OpenAPI一致性检查; 6. 有测试环境变量的完整测试; 7. 无测试环境变量的安全跳过测试。 测试环境变量继续使用: DMS_TEST_DATABASE_URL=mysql+pymysql://root:root@localhost:3306/dms_test?charset=utf8mb4 DMS_TEST_USER_PASSWORD=TestUser@123456 DMS_TEST_JWT_SECRET=local-test-jwt-secret-not-for-production 测试凭据不得写入源码、OpenAPI、契约或提交文件。 二十七、阻塞规则 发生以下情况必须停止并提交统领裁决: 1. 需要新增数据库字段或迁移; 2. 正式契约与OpenAPI存在冲突; 3. 需要修改两份DMS契约; 4. 需要新增第11个B5接口; 5. 需要实现恢复; 6. 需要物理删除文件; 7. 需要实现挂载或权限写入; 8. 需要修改前端; 9. 需要修改AI模块; 10. 现有授权服务无法表达契约规则; 11. 文件事务无法在不改变契约的情况下保证补偿; 12. 需要创建新的测试数据库; 13. 真实响应需要增加未定义字段; 14. 测试必须清理主库才能继续。 不得自行兼容、猜测或扩大范围。 二十八、最终报告格式 完成后必须输出: 1. 阶段结论:B5是否完成,是否停止在B5。 2. 契约加载情况。 3. 修改文件清单。 4. 数据库和迁移检查结果。 5. 10个B5接口实现情况。 6. 文件存储结构。 7. 文件类型及安全校验方案。 8. 单文件上传事务边界。 9. 文件失败补偿机制。 10. 主案和子方案上传结果。 11. 共享附件上传结果。 12. 两类批量导入结果。 13. 文档元数据编辑规则。 14. 附件元数据编辑规则。 15. 主案、子方案和附件删除规则。 16. PDF预览和Office拒绝预览结果。 17. 下载授权、计数和审计结果。 18. 冗余字段和计数维护结果。 19. rowVersion并发控制结果。 20. 审计实现结果。 21. OpenAPI变化。 22. dms_test使用情况。 23. 是否创建了任何新测试数据库。 24. 完整测试结果。 25. 无环境变量测试结果。 26. AI和旧阶段回归结果。 27. dms主库非破坏检查结果。 28. 契约符合性检查。 29. 是否存在偏差。 30. 未实现内容。 31. B6建议,但不得执行。 再次强调: - 本轮只执行B5。 - 只复用dms_test。 - 不创建阶段测试库。 - 不向dms主库写入测试文档。 - 不修改前端。 - 不实现挂载写和权限写。 - 不实现审计查询和统计。 - 不实现任何恢复功能。 - 不物理删除原始文件。 - 不进入B6。 - 不提交或推送Git。 --------------------------------------