拆分顺序:数据模型 → 接口协议 → 底层实现 → 上层编排 → 入口层 每个任务 = 可独立提交的原子变更(3-5 个文件) 每个任务必须精确到文件路径和函数签名
| 任务编号 | 任务名称 | 优先级 | 预估工作量 | 依赖 |
|---|---|---|---|---|
| Task 1 | 数据库表结构变更 | P0 | 1h | 无 |
| Task 2 | FlowModel 实体修改 | P0 | 0.5h | Task 1 |
| Task 3 | FlowModelVersion 实体创建 | P0 | 0.5h | Task 1 |
| Task 4 | 字典数据新增 | P1 | 0.5h | 无 |
| Task 5 | FlowModelVersionMapper 创建 | P0 | 1h | Task 3 |
| Task 6 | 版本相关 DTO/VO 创建 | P1 | 1h | Task 3 |
| Task 7 | FlowModelVersionService 创建 | P0 | 3h | Task 5, Task 6 |
| Task 8 | FlowModelServiceImpl 修改 | P0 | 2h | Task 7 |
| Task 9 | FlowModelVersionController 创建 | P0 | 2h | Task 7 |
| Task 10 | 前端 API 接口创建 | P1 | 1h | Task 9 |
| Task 11 | 前端版本历史组件创建 | P1 | 3h | Task 10 |
| Task 12 | 前端版本对比组件创建 | P1 | 4h | Task 10, Task 11 |
| Task 13 | 前端 model.vue 修改 | P0 | 2h | Task 11 |
| Task 14 | 权限配置和菜单新增 | P1 | 1h | Task 9 |
总预估工作量:22h(约 3 个工作日)
创建版本历史表 sys_flow_model_version,修改模型表新增重要性等级字段
forge/forge-admin-server/src/main/resources/sql/flow_version_init.sql — 新增,创建表和修改表结构forge/forge-admin-server/src/main/resources/sql/forge.sql — 修改,合并 SQL 脚本到初始化脚本-- 新增版本历史表
CREATE TABLE `sys_flow_model_version` (
`id` VARCHAR(64) PRIMARY KEY COMMENT '主键',
`model_id` VARCHAR(64) NOT NULL COMMENT '模型ID',
`version` INT NOT NULL COMMENT '版本号',
`version_name` VARCHAR(100) NOT NULL COMMENT '版本名称',
`version_tag` VARCHAR(20) NOT NULL DEFAULT 'draft' COMMENT '版本标记',
`bpmn_xml` TEXT NOT NULL COMMENT 'BPMN XML',
`form_json` TEXT COMMENT '表单配置',
`change_description` VARCHAR(500) COMMENT '变更说明',
`deployment_id` VARCHAR(64) COMMENT '部署ID',
`process_definition_id` VARCHAR(64) COMMENT '流程定义ID',
`publish_by` VARCHAR(64) NOT NULL COMMENT '发布人',
`publish_time` DATETIME NOT NULL COMMENT '发布时间',
`tenant_id` BIGINT NOT NULL DEFAULT 1 COMMENT '租户ID',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`del_flag` INT NOT NULL DEFAULT 0 COMMENT '删除标志',
UNIQUE KEY `uk_model_version` (`model_id`, `version`),
INDEX `idx_model_id` (`model_id`)
) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='流程模型版本历史表';
-- 修改模型表新增重要性等级字段
ALTER TABLE `sys_flow_model`
ADD COLUMN `importance_level` INT NOT NULL DEFAULT 1 COMMENT '重要性等级(1-普通/2-重要)' AFTER `status`;
修改 FlowModel 实体类,新增 importanceLevel 字段
forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/entity/FlowModel.java — 修改,新增字段/**
* 重要性等级(1-普通/2-重要)
*/
private Integer importanceLevel;
创建 FlowModelVersion 实体类,映射 sys_flow_model_version 表
forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/entity/FlowModelVersion.java — 新增,创建版本历史实体package com.mdframe.forge.starter.flow.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("sys_flow_model_version")
public class FlowModelVersion {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String modelId;
private Integer version;
private String versionName;
private String versionTag;
private String bpmnXml;
private String formJson;
private String changeDescription;
private String deploymentId;
private String processDefinitionId;
private String publishBy;
private LocalDateTime publishTime;
private Long tenantId;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableLogic
private Integer delFlag;
}
新增版本标记和重要性等级两个字典类型及字典数据
forge/forge-admin-server/src/main/resources/sql/flow_version_init.sql — 修改,新增字典数据 SQLsys_dict_type、sys_dict_data — 新增记录-- 新增字典类型:版本标记
INSERT INTO `sys_dict_type`
VALUES ('flow_version_tag', '流程版本标记', '0', 1, NOW(), NOW(), NULL, NULL, 'admin', 1);
INSERT INTO `sys_dict_data` VALUES
('draft', 'flow_version_tag', '草稿', 'draft', '0', 1, NOW(), NOW(), NULL, NULL, 'admin', 1),
('test', 'flow_version_tag', '测试', 'test', '0', 2, NOW(), NOW(), NULL, NULL, 'admin', 1),
('release', 'flow_version_tag', '正式发布', 'release', '0', 3, NOW(), NOW(), NULL, NULL, 'admin', 1),
('deprecated', 'flow_version_tag', '已废弃', 'deprecated', '0', 4, NOW(), NOW(), NULL, NULL, 'admin', 1);
-- 新增字典类型:流程重要性等级
INSERT INTO `sys_dict_type`
VALUES ('flow_importance_level', '流程重要性等级', '0', 1, NOW(), NOW(), NULL, NULL, 'admin', 1);
INSERT INTO `sys_dict_data` VALUES
('1', 'flow_importance_level', '普通流程', '1', '0', 1, NOW(), NOW(), NULL, NULL, 'admin', 1),
('2', 'flow_importance_level', '重要流程', '2', '0', 2, NOW(), NOW(), NULL, NULL, 'admin', 1);
创建 FlowModelVersionMapper 接口和 XML 映射文件
forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/mapper/FlowModelVersionMapper.java — 新增,创建 Mapper 接口forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/resources/mapper/FlowModelVersionMapper.xml — 新增,创建 XML 映射文件package com.mdframe.forge.starter.flow.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mdframe.forge.starter.flow.entity.FlowModelVersion;
import org.apache.ibatis.annotations.Param;
public interface FlowModelVersionMapper extends BaseMapper<FlowModelVersion> {
/**
* 分页查询版本历史
*/
IPage<FlowModelVersion> pageByVersion(Page<FlowModelVersion> page, @Param("modelId") String modelId);
/**
* 查询模型的最大版本号
*/
Integer getMaxVersion(@Param("modelId") String modelId);
/**
* 查询版本详情(包含 BPMN XML 和表单配置)
*/
FlowModelVersion getVersionDetail(@Param("versionId") String versionId);
}
创建版本管理相关的请求 DTO 和响应 VO 类
forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/dto/VersionCompareDTO.java — 新增,版本对比请求 DTOforge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/dto/VersionRevertDTO.java — 新增,版本回退请求 DTOforge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/dto/VersionTagUpdateDTO.java — 新增,版本标记更新 DTOforge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/vo/VersionCompareVO.java — 新增,版本对比响应 VOforge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/vo/VersionRevertVO.java — 新增,版本回退响应 VOforge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/vo/VersionDetailVO.java — 新增,版本详情响应 VO// VersionCompareDTO.java
@Data
public class VersionCompareDTO {
private String modelId;
private Integer version1;
private Integer version2;
}
// VersionRevertDTO.java
@Data
public class VersionRevertDTO {
private String modelId;
private Integer targetVersion;
private String changeDescription;
}
// VersionCompareVO.java
@Data
public class VersionCompareVO {
private List<NodeDiff> addedNodes;
private List<NodeDiff> modifiedNodes;
private List<NodeDiff> deletedNodes;
private List<FlowDiff> addedFlows;
private List<FlowDiff> modifiedFlows;
private List<FlowDiff> deletedFlows;
}
// VersionRevertVO.java
@Data
public class VersionRevertVO {
private String newVersionId;
private Integer newVersion;
private String deploymentId;
private Integer runningInstances;
}
创建 FlowModelVersionService 接口和实现类,实现版本管理的核心业务逻辑
forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/service/FlowModelVersionService.java — 新增,创建 Service 接口forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/service/impl/FlowModelVersionServiceImpl.java — 新增,创建 Service 实现类// FlowModelVersionService.java
public interface FlowModelVersionService extends IService<FlowModelVersion> {
/**
* 分页查询版本历史
*/
IPage<FlowModelVersion> pageVersionList(Page<FlowModelVersion> page, String modelId);
/**
* 查询版本详情
*/
VersionDetailVO getVersionDetail(String versionId);
/**
* 版本对比
*/
VersionCompareVO compareVersions(VersionCompareDTO dto);
/**
* 版本回退
*/
VersionRevertVO revertVersion(VersionRevertDTO dto);
/**
* 更新版本标记
*/
void updateVersionTag(String versionId, String versionTag);
/**
* 删除版本(仅允许删除草稿/测试版本)
*/
void deleteVersion(String versionId);
/**
* 发布时插入版本历史记录
*/
void insertVersionOnPublish(FlowModel model, String changeDescription);
}
// FlowModelVersionServiceImpl.java 核心方法签名
@Service
@RequiredArgsConstructor
public class FlowModelVersionServiceImpl extends ServiceImpl<FlowModelVersionMapper, FlowModelVersion> implements FlowModelVersionService {
@Override
public VersionRevertVO revertVersion(VersionRevertDTO dto) {
// 1. 查询目标版本
FlowModelVersion targetVersion = getTargetVersion(dto.getModelId(), dto.getTargetVersion());
// 2. 权限校验(检查流程重要性等级)
checkPermission(dto.getModelId());
// 3. 查询模型当前版本号
FlowModel model = flowModelService.getById(dto.getModelId());
Integer newVersion = model.getVersion() + 1;
// 4. 复制目标版本内容并发布为新版本
String deploymentId = deployToFlowable(model.getModelKey(), targetVersion.getBpmnXml(), newVersion);
// 5. 插入新版本历史记录
insertNewVersion(model, targetVersion, newVersion, deploymentId, dto.getChangeDescription());
// 6. 更新模型当前版本号和 deploymentId
updateModelVersion(model, newVersion, deploymentId);
// 7. 查询正在运行的实例数量
Integer runningInstances = getRunningInstancesCount(model.getProcessDefinitionId());
return buildRevertVO(newVersionId, newVersion, deploymentId, runningInstances);
}
private void checkPermission(String modelId) {
FlowModel model = flowModelService.getById(modelId);
if (model.getImportanceLevel() == 2) {
// 重要流程需超级管理员权限
// 权限校验逻辑
}
}
}
修改 FlowModelServiceImpl 的 deployModel 方法,发布时自动插入版本历史记录
forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/service/impl/FlowModelServiceImpl.java — 修改,新增版本插入逻辑forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/service/FlowModelService.java — 修改,deployModel 方法新增 changeDescription 参数// FlowModelService.java
String deployModel(String id, String changeDescription);
// FlowModelServiceImpl.java
@Override
@Transactional(rollbackFor = Exception.class)
public String deployModel(String id, String changeDescription) {
// ... 原有发布逻辑 ...
// 新增:发布成功后插入版本历史记录
flowModelVersionService.insertVersionOnPublish(model, changeDescription);
return deployment.getId();
}
创建 FlowModelVersionController,提供版本管理的 REST API
forge/forge-framework/forge-plugin-parent/forge-plugin-flow/src/main/java/com/mdframe/forge/starter/flow/controller/FlowModelVersionController.java — 新增,创建版本管理 Controllerpackage com.mdframe.forge.starter.flow.controller;
import com.mdframe.forge.starter.core.domain.RespInfo;
import com.mdframe.forge.starter.flow.dto.*;
import com.mdframe.forge.starter.flow.vo.*;
import com.mdframe.forge.starter.flow.service.FlowModelVersionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
@Tag(name = "流程模型版本管理")
@RestController
@RequestMapping("/flow/model/version")
@RequiredArgsConstructor
public class FlowModelVersionController {
private final FlowModelVersionService flowModelVersionService;
@Operation(summary = "分页查询版本历史")
@GetMapping("/list")
public RespInfo<IPage<FlowModelVersion>> pageVersionList(
@RequestParam String modelId,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
Page<FlowModelVersion> page = new Page<>(pageNum, pageSize);
return RespInfo.success(flowModelVersionService.pageVersionList(page, modelId));
}
@Operation(summary = "查询版本详情")
@GetMapping("/{versionId}")
public RespInfo<VersionDetailVO> getVersionDetail(@PathVariable String versionId) {
return RespInfo.success(flowModelVersionService.getVersionDetail(versionId));
}
@Operation(summary = "版本对比")
@PostMapping("/compare")
public RespInfo<VersionCompareVO> compareVersions(@RequestBody VersionCompareDTO dto) {
return RespInfo.success(flowModelVersionService.compareVersions(dto));
}
@Operation(summary = "版本回退")
@PostMapping("/revert")
@SaCheckPermission("flow:model:revert")
@OperationLog(module = "流程管理", operation = "版本回退")
public RespInfo<VersionRevertVO> revertVersion(@RequestBody VersionRevertDTO dto) {
return RespInfo.success(flowModelVersionService.revertVersion(dto));
}
@Operation(summary = "更新版本标记")
@PutMapping("/{versionId}/tag")
public RespInfo<Void> updateVersionTag(
@PathVariable String versionId,
@RequestParam String versionTag) {
flowModelVersionService.updateVersionTag(versionId, versionTag);
return RespInfo.success();
}
@Operation(summary = "删除版本")
@DeleteMapping("/{versionId}")
@SaCheckPermission("flow:model:version:delete")
public RespInfo<Void> deleteVersion(@PathVariable String versionId) {
flowModelVersionService.deleteVersion(versionId);
return RespInfo.success();
}
@Operation(summary = "下载版本 BPMN XML")
@GetMapping("/download/{versionId}")
public void downloadVersion(@PathVariable String versionId, HttpServletResponse response) {
// 下载逻辑
}
}
创建前端版本管理 API 接口文件
forge-admin-ui/src/api/flow/version.ts — 新增,创建版本管理 API 接口import { request } from '@/utils/request'
// 版本历史查询
export function getVersionList(modelId: string, pageNum: number = 1, pageSize: number = 20) {
return request.get('/flow/model/version/list', { params: { modelId, pageNum, pageSize } })
}
// 版本详情查询
export function getVersionDetail(versionId: string) {
return request.get(`/flow/model/version/${versionId}`)
}
// 版本对比
export function compareVersions(modelId: string, version1: number, version2: number) {
return request.post('/flow/model/version/compare', { modelId, version1, version2 })
}
// 版本回退
export function revertVersion(modelId: string, targetVersion: number, changeDescription?: string) {
return request.post('/flow/model/version/revert', { modelId, targetVersion, changeDescription })
}
// 更新版本标记
export function updateVersionTag(versionId: string, versionTag: string) {
return request.put(`/flow/model/version/${versionId}/tag`, null, { params: { versionTag } })
}
// 删除版本
export function deleteVersion(versionId: string) {
return request.delete(`/flow/model/version/${versionId}`)
}
// 下载版本 BPMN XML
export function downloadVersion(versionId: string) {
return request.get(`/flow/model/version/download/${versionId}`, { responseType: 'blob' })
}
创建前端版本历史列表页面组件(弹窗或独立页面)
forge-admin-ui/src/views/flow/version.vue — 新增,创建版本历史页面<template>
<div class="version-page">
<!-- 版本历史列表 -->
<n-data-table
:columns="columns"
:data="versionList"
:pagination="pagination"
:loading="loading"
/>
<!-- 操作按钮 -->
<template #action="{ row }">
<a class="text-primary" @click="handleDetail(row)">详情</a>
<a class="text-primary" @click="handleCompare(row)">对比</a>
<a class="text-warning" @click="handleRevert(row)">回退</a>
<a class="text-info" @click="handleUpdateTag(row)">标记</a>
<a class="text-error" @click="handleDelete(row)">删除</a>
<a class="text-info" @click="handleDownload(row)">下载</a>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getVersionList, deleteVersion, updateVersionTag } from '@/api/flow/version'
import { useDialog, useMessage } from 'naive-ui'
const versionList = ref([])
const loading = ref(false)
const modelId = ref('')
const columns = [
{ title: '版本号', key: 'version' },
{ title: '版本名称', key: 'versionName' },
{ title: '版本标记', key: 'versionTag' },
{ title: '变更说明', key: 'changeDescription' },
{ title: '发布人', key: 'publishBy' },
{ title: '发布时间', key: 'publishTime' },
{ title: '操作', key: 'action' }
]
const loadVersionList = async () => {
loading.value = true
const res = await getVersionList(modelId.value, 1, 20)
versionList.value = res.data.records
loading.value = false
}
const handleRevert = (row) => {
// 弹窗确认回退
}
const handleDelete = async (row) => {
// 确认删除
await deleteVersion(row.id)
loadVersionList()
}
</script>
创建前端版本对比页面组件,展示两个版本的 BPMN XML 差异
forge-admin-ui/src/views/flow/versionCompare.vue — 新增,创建版本对比页面<template>
<div class="version-compare-page">
<!-- 版本选择 -->
<div class="version-selector">
<n-select v-model:value="version1" :options="versionOptions" placeholder="选择版本1" />
<n-select v-model:value="version2" :options="versionOptions" placeholder="选择版本2" />
<n-button type="primary" @click="handleCompare">对比</n-button>
</div>
<!-- 差异展示 -->
<div class="diff-display">
<!-- 新增节点 -->
<div v-if="diffResult.addedNodes.length > 0">
<h3>新增节点</h3>
<n-list>
<n-list-item v-for="node in diffResult.addedNodes">
{{ node.name }} ({{ node.id }})
</n-list-item>
</n-list>
</div>
<!-- 修改节点 -->
<div v-if="diffResult.modifiedNodes.length > 0">
<h3>修改节点</h3>
<n-list>
<n-list-item v-for="node in diffResult.modifiedNodes">
{{ node.oldName }} → {{ node.newName }}
</n-list-item>
</n-list>
</div>
<!-- 删除节点 -->
<!-- 新增连线 -->
<!-- 修改连线 -->
<!-- 删除连线 -->
</div>
<!-- Side-by-side 流程图对比(可选) -->
<div class="bpmn-compare">
<div class="bpmn-left">
<div ref="bpmnViewer1" class="bpmn-container"></div>
</div>
<div class="bpmn-right">
<div ref="bpmnViewer2" class="bpmn-container"></div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { compareVersions, getVersionDetail } from '@/api/flow/version'
import BpmnViewer from 'bpmn-js/lib/NavigatedViewer'
const version1 = ref(null)
const version2 = ref(null)
const diffResult = ref({})
const bpmnViewer1 = ref(null)
const bpmnViewer2 = ref(null)
const handleCompare = async () => {
const res = await compareVersions(modelId, version1.value, version2.value)
diffResult.value = res.data
// 加载 BPMN 流程图
const detail1 = await getVersionDetail(version1Id)
const detail2 = await getVersionDetail(version2Id)
renderBpmn(bpmnViewer1.value, detail1.data.bpmnXml)
renderBpmn(bpmnViewer2.value, detail2.data.bpmnXml)
}
const renderBpmn = (container, bpmnXml) => {
const viewer = new BpmnViewer({ container })
viewer.importXML(bpmnXml)
}
</script>
修改前端流程模型管理页面,新增"版本历史"按钮和版本管理入口
forge-admin-ui/src/views/flow/model.vue — 修改,新增版本历史按钮forge-admin-ui/src/views/flow/design.vue — 修改,新增发布时填写变更说明弹窗(可选)<!-- model.vue 卡片操作按钮新增 -->
<div class="card-actions">
<a class="text-primary" @click="handleDesign(item)">设计</a>
<a class="text-primary" @click="handleDeploy(item)">发布</a>
<a class="text-info" @click="handleVersionHistory(item)">版本历史</a> <!-- 新增 -->
<a class="text-warning" @click="handleSuspend(item)">挂起</a>
<a class="text-error" @click="handleDelete(item)">删除</a>
</div>
<script setup lang="ts">
// 新增版本历史弹窗
const showVersionHistory = ref(false)
const currentModelId = ref('')
const handleVersionHistory = (item) => {
currentModelId.value = item.id
showVersionHistory.value = true
}
</script>
<!-- 版本历史弹窗 -->
<n-modal v-model:show="showVersionHistory" preset="card" title="版本历史" style="width: 800px">
<VersionHistory :model-id="currentModelId" />
</n-modal>
在系统菜单管理中新增版本管理权限标识和菜单项
sys_resource — 新增菜单和权限记录-- 新增版本管理菜单(在流程模型菜单下)
INSERT INTO `sys_resource` VALUES
('flow_model_version', '流程模型', 'flow_model', 'version', '版本历史', 3, 'menu', NULL, '/flow/model/version', 'document', 0, 1, NOW(), NOW(), NULL, NULL, 'admin', 1);
-- 新增版本回退权限
INSERT INTO `sys_resource` VALUES
('flow_model_revert', '流程模型', 'flow_model_version', 'revert', '版本回退', 4, 'button', 'flow:model:revert', NULL, NULL, 0, 1, NOW(), NOW(), NULL, NULL, 'admin', 1);
-- 新增版本删除权限
INSERT INTO `sys_resource` VALUES
('flow_model_version_delete', '流程模型', 'flow_model_version', 'delete', '版本删除', 5, 'button', 'flow:model:version:delete', NULL, NULL, 0, 1, NOW(), NOW(), NULL, NULL, 'admin', 1);
Task 1 (数据库表)
↓
Task 2 (FlowModel 实体修改) + Task 3 (FlowModelVersion 实体创建)
↓
Task 5 (FlowModelVersionMapper)
↓
Task 6 (DTO/VO)
↓
Task 7 (FlowModelVersionService)
↓
Task 8 (FlowModelServiceImpl 修改) + Task 9 (FlowModelVersionController)
↓
Task 10 (前端 API)
↓
Task 11 (版本历史组件) + Task 13 (model.vue 修改)
↓
Task 12 (版本对比组件)
Task 4 (字典数据) — 独立任务,可并行执行
Task 14 (权限配置) — 依赖 Task 9,最后执行
⚠️ 高风险任务:
⚠️ 建议测试顺序:
每个任务完成后需满足:
[Task N] 任务名称 - 具体改动)