diff --git a/User/utils.py b/User/utils.py index 26b63fc..d784558 100644 --- a/User/utils.py +++ b/User/utils.py @@ -340,43 +340,63 @@ def parse_approvers(approvers): approvers: 审核人列表,可以是: - 数组格式(ID列表,推荐):[1, 2, 3] 或 ["1", "2", "3"] - 数组格式(用户名列表,兼容):["张三", "李四", "王五"] - - JSON字符串格式(ID列表):"[1,2,3]" 或 '"[1,2,3]"' + - JSON字符串格式(ID列表):"[1,2,3]" 或 "[5]" 或 '"[1,2,3]"' + - JSON字符串格式(带空格):"[ 5 ]" 或 "[1, 2, 3]" - JSON字符串格式(用户名列表,兼容):'["张三","李四","王五"]' - - 字符串格式(逗号分隔的ID):"1,2,3" + - 字符串格式(逗号分隔的ID):"1,2,3" 或 "5" - 字符串格式(逗号分隔的用户名,兼容):"张三,李四,王五" - None: 返回空列表 Returns: list: 审核人用户名列表,如 ["张三", "李四", "王五"] """ + import json + if not approvers: return [] approvers_list = [] if isinstance(approvers, str): - # 尝试解析 JSON 字符串格式(如 "[1,2,3]" 或 '"[1,2,3]"') + # 去除首尾空格 + approvers = approvers.strip() + + # 如果字符串为空,返回空列表 + if not approvers: + return [] + + # 尝试解析 JSON 字符串格式(如 "[1,2,3]" 或 "[5]" 或 '"[1,2,3]"') try: - import json # 先尝试直接解析 JSON parsed = json.loads(approvers) if isinstance(parsed, list): - approvers_list = [str(a).strip() for a in parsed if a] - else: - # 如果不是数组,可能是被双重编码的字符串,再解析一次 - if isinstance(parsed, str): - try: - parsed2 = json.loads(parsed) - if isinstance(parsed2, list): - approvers_list = [str(a).strip() for a in parsed2 if a] - except: - pass + approvers_list = [str(a).strip() for a in parsed if a is not None and str(a).strip()] + elif isinstance(parsed, str): + # 如果是字符串,可能是被双重编码的JSON字符串,再解析一次 + # 例如:'"[5]"' -> "[5]" -> [5] + try: + parsed2 = json.loads(parsed) + if isinstance(parsed2, list): + approvers_list = [str(a).strip() for a in parsed2 if a is not None and str(a).strip()] + elif isinstance(parsed2, (int, str)): + # 单个值,转换为列表 + approvers_list = [str(parsed2).strip()] + except (json.JSONDecodeError, ValueError, TypeError): + # 双重解析失败,可能是普通字符串,按逗号分隔处理 + approvers_list = [parsed.strip()] if parsed.strip() else [] + elif isinstance(parsed, (int, str)): + # 单个值(数字或字符串),转换为列表 + approvers_list = [str(parsed).strip()] except (json.JSONDecodeError, ValueError, TypeError): # JSON 解析失败,尝试按逗号分隔处理 + # 例如:"1,2,3" 或 "5" 或 "张三,李四" approvers_list = [a.strip() for a in approvers.split(',') if a.strip()] elif isinstance(approvers, list): # 数组格式(推荐) - approvers_list = [str(a).strip() for a in approvers if a] + approvers_list = [str(a).strip() for a in approvers if a is not None and str(a).strip()] + elif isinstance(approvers, (int, float)): + # 单个数字,转换为列表 + approvers_list = [str(int(approvers))] else: return [] diff --git a/立项审核逻辑解析.md b/立项审核逻辑解析.md new file mode 100644 index 0000000..654f4b5 --- /dev/null +++ b/立项审核逻辑解析.md @@ -0,0 +1,356 @@ +# 立项审核逻辑完整解析 + +## 一、审核流程概述 + +立项审核采用**基于团队类型的多级审核机制**,根据负责人所属团队的类型,决定是否需要审核人以及审核流程。 + +## 二、核心审核逻辑 + +### 1. 团队类型判断 + +系统根据**负责人(responsible_person)**所属的团队类型来决定审核流程: + +- **个人团队(personal)**:直接抄送财务,无需审核人 +- **团队(team)**:需要指定审核人,按顺序审核,最后抄送财务 +- **无团队**:直接抄送财务 + +### 2. 审核流程函数 + +核心函数:`create_approval_with_team_logic`(位于 `User/utils.py`) + +#### 2.1 个人团队流程 +```python +# 个人团队:直接抄送财务 +approval = Approval.objects.create( + personincharge="财务", + state="已抄送财务", + type="立项登记", + user_id=项目ID +) +# 项目状态:待财务处理 +``` + +#### 2.2 团队类型流程 +```python +# 团队类型:需要审核人审核(按顺序) +# 1. 解析审核人列表(从参数或团队配置获取) +# 2. 验证审核人有效性 +# 3. 将审核人顺序存储到 approvers_order 字段(JSON格式) +# 4. 创建审批记录,第一个审核人开始审核 +approval = Approval.objects.create( + personincharge=第一个审核人, + state="审核中", + content="审批流程:张三 → 李四 → 王五 → 财务(按顺序审批),当前审批人:张三" +) +# 项目状态:审核中 +``` + +## 三、立项创建流程(Project类) + +### 1. 创建步骤 + +**接口路径**:`POST /business/project` + +**关键步骤**: + +1. **参数验证** + - 必填字段:type(项目类型)、ContractNo(合同编号)、times(立项日期)、client_info(委托人信息)、party_info(相对方信息)、description(项目简述)、charge(收费情况) + - 负责人信息(responsiblefor)必须是字典格式,且 `responsible_person` 字段必填 + - 检查合同编号是否已存在 + +2. **获取团队信息** + ```python + # 从负责人信息中获取负责人姓名 + responsible_person = responsiblefor_dict.get('responsible_person') + # 查询负责人的团队 + responsible_user = User.objects.get(username=responsible_person) + team_name = responsible_user.team + ``` + +3. **创建项目记录** + ```python + pro = ProjectRegistration.objects.create( + state="审核中", # 初始状态 + # ... 其他字段 + ) + ``` + +4. **创建审核流程** + ```python + approval, approvers_order_json, needs_approval = create_approval_with_team_logic( + team_name=team_name, + approvers=approvers, # 可选,推荐格式:[1,2,3](用户ID数组) + title="负责人姓名立项登记", + content="负责人信息 + 项目信息", + approval_type="立项登记", + user_id=pro.id, + business_record=pro, + today=当前日期 + ) + ``` + +5. **错误处理** + - 如果 `approval is None and needs_approval`,说明缺少审核人,返回详细错误信息 + +### 2. 审核人参数格式 + +支持多种格式(推荐使用用户ID数组): +- **推荐**:`[1, 2, 3]` 或 `["1", "2", "3"]`(用户ID数组) +- **兼容**:`["张三", "李四", "王五"]`(用户名数组) +- **字符串**:`"1,2,3"` 或 `"张三,李四,王五"`(逗号分隔) +- **JSON字符串**:`"[1,2,3]"` 或 `'["张三","李四","王五"]'` + +### 3. 审核人获取优先级 + +1. 如果传入了 `approvers` 参数,使用传入的审核人 +2. 如果未传入,从团队配置中获取审核人(`team.approvers`) +3. 如果团队没有配置审核人,返回错误 + +## 四、立项编辑流程(EditProject类) + +### 1. 编辑步骤 + +**接口路径**:`POST /business/editproject` + +**关键步骤**: + +1. **参数验证** + - 不允许修改的字段:type(项目类型)、ContractNo(合同编号) + - 其他字段可以修改 + +2. **更新项目记录** + ```python + pro.state = "审核中" # 重新进入审核流程 + pro.save() + ``` + +3. **重新创建审核流程** + ```python + # 重新获取团队信息 + team_name = get_team_name_from_responsiblefor(responsiblefor_dict) + + # 重新创建审核流程(与创建流程相同) + approval, approvers_order_json, needs_approval = create_approval_with_team_logic(...) + ``` + +## 五、审核处理流程(approvalProcessing类) + +### 1. 审核处理接口 + +**接口路径**:`POST /User/approvalProcessing` + +**参数**: +- `state`: "已通过" 或 "未通过" +- `type`: "立项登记" +- `id`: 审批记录ID + +### 2. 审核处理逻辑 + +核心函数:`process_approval_flow`(位于 `User/utils.py`) + +#### 2.1 审核不通过 +```python +if state == "未通过": + approval.state = "未通过" + project.state = "未通过" + # 审核流程结束 + return True # 已完成 +``` + +#### 2.2 财务审核 +```python +if approval.personincharge == "财务" and approval.state == "已抄送财务": + if state == "已通过": + approval.state = "已通过" + project.state = "已通过" + # 审核流程结束 + return True # 已完成 +``` + +#### 2.3 团队审核流程(按顺序流转) + +1. **获取审核人列表** + ```python + # 从项目的 approvers_order 字段读取(JSON格式) + approvers_list = json.loads(project.approvers_order) + # 例如:["张三", "李四", "王五"] + ``` + +2. **找到当前审核人位置** + ```python + current_index = approvers_list.index(current_approver) + ``` + +3. **判断是否还有下一个审核人** + ```python + if current_index < len(approvers_list) - 1: + # 不是最后一个,流转到下一个审核人 + next_approver = approvers_list[current_index + 1] + approval.personincharge = next_approver + approval.state = "审核中" + # 更新审批内容中的当前审批人 + else: + # 最后一个审核人,抄送财务 + approval.personincharge = "财务" + approval.state = "已抄送财务" + project.state = "待财务处理" + ``` + +### 3. 审核流程状态流转 + +``` +创建立项 + ↓ +审核中(第一个审核人) + ↓ +审核中(第二个审核人) + ↓ +... + ↓ +审核中(最后一个审核人) + ↓ +已抄送财务(财务审核) + ↓ +已通过 / 未通过 +``` + +## 六、审核人顺序存储 + +### 1. 存储位置 + +- **字段**:`ProjectRegistration.approvers_order` +- **格式**:JSON字符串,如 `'["张三", "李四", "王五"]'` + +### 2. 存储时机 + +在 `create_approval_with_team_logic` 函数中,当团队类型为 `team` 时: +```python +approvers_order_json = json.dumps(approvers_list, ensure_ascii=False) +project.approvers_order = approvers_order_json +project.save() +``` + +### 3. 读取方式 + +在 `process_approval_flow` 函数中: +```python +approvers_list = json.loads(project.approvers_order) +``` + +## 七、审核记录(Approval模型) + +### 1. 关键字段 + +- `title`: 审批标题,如 "张三立项登记" +- `content`: 审批内容,包含审批流程和当前审批人 +- `personincharge`: 当前负责人/审批人 + - 审核人:用户名,如 "张三" + - 财务审核:固定为 "财务" +- `state`: 审批状态 + - "审核中":正在审核 + - "已抄送财务":已到财务审核阶段 + - "已通过":审核通过 + - "未通过":审核不通过 +- `type`: 审批类型,固定为 "立项登记" +- `user_id`: 关联的项目ID(字符串格式) + +### 2. 审批内容格式 + +**团队类型**: +``` +负责人姓名在日期办理立项登记,项目类型:xxx,合同编号:xxx,负责人信息,收费情况:xxx,审批流程:张三 → 李四 → 王五 → 财务(按顺序审批),当前审批人:张三 +``` + +**个人团队**: +``` +负责人姓名在日期办理立项登记,项目类型:xxx,合同编号:xxx,负责人信息,收费情况:xxx +``` + +## 八、项目状态流转 + +``` +审核中 → 待财务处理 → 已通过 / 未通过 +``` + +- **审核中**:正在团队审核阶段 +- **待财务处理**:已到财务审核阶段 +- **已通过**:审核通过 +- **未通过**:审核不通过 + +## 九、关键函数说明 + +### 1. `create_approval_with_team_logic` +- **位置**:`User/utils.py` +- **功能**:根据团队类型创建审批记录 +- **返回**:`(approval对象, approvers_order_json, 是否需要审核)` + +### 2. `process_approval_flow` +- **位置**:`User/utils.py` +- **功能**:处理审核流程流转 +- **返回**:`(是否完成, 错误信息)` + +### 3. `parse_approvers` +- **位置**:`User/utils.py` +- **功能**:解析审核人列表(支持多种格式) +- **返回**:用户名列表 + +### 4. `get_approvers_from_record` +- **位置**:`User/utils.py` +- **功能**:从业务记录中获取审核人列表 +- **优先级**:`approvers_order` 字段 > `Approval.content` 字段解析 + +## 十、注意事项 + +1. **审核人验证**:系统会自动验证审核人是否存在,过滤掉无效的审核人 +2. **审核顺序**:严格按照 `approvers_order` 中的顺序进行审核 +3. **财务审核**:财务部只需要一个人审核即可完成 +4. **编辑重新审核**:编辑立项会重新进入审核流程,状态重置为"审核中" +5. **审核不通过**:任何阶段审核不通过,流程立即结束,项目状态变为"未通过" +6. **团队配置**:如果团队类型为 `team` 但没有配置审核人,创建时会返回错误 + +## 十一、数据模型关系 + +``` +User (负责人) + ↓ (team字段) +Team (团队) + ↓ (team_type: personal/team) + ↓ (approvers: 多对多关系) +User (审核人列表) + +ProjectRegistration (项目) + ↓ (approvers_order: JSON字符串) + ↓ (state: 审核状态) + ↓ (id → Approval.user_id) +Approval (审批记录) + ↓ (personincharge: 当前审核人) + ↓ (state: 审批状态) +``` + +## 十二、示例流程 + +### 示例1:个人团队 + +1. 创建立项:负责人属于个人团队 +2. 直接创建审批记录:`personincharge="财务"`, `state="已抄送财务"` +3. 项目状态:`state="待财务处理"` +4. 财务审核通过:项目状态变为 `"已通过"` + +### 示例2:团队类型(3个审核人) + +1. 创建立项:负责人属于团队类型,传入审核人 `[1, 2, 3]`(对应张三、李四、王五) +2. 创建审批记录: + - `personincharge="张三"`(第一个审核人) + - `state="审核中"` + - `approvers_order='["张三", "李四", "王五"]'` +3. 张三审核通过: + - `personincharge="李四"`(流转到下一个) + - `state="审核中"` +4. 李四审核通过: + - `personincharge="王五"`(流转到下一个) + - `state="审核中"` +5. 王五审核通过: + - `personincharge="财务"`(最后一个,抄送财务) + - `state="已抄送财务"` + - 项目状态:`state="待财务处理"` +6. 财务审核通过:项目状态变为 `"已通过"`