liyong
2026-07-03 7085354cb7e6283f72e99d7eff8e73b8c7ba0cb9
产品设计-设计项目/归档台账  接口开发
已添加40个文件
已修改4个文件
3797 ■■■■■ 文件已修改
docs/archive_group_by_product.md 397 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/product_design.md 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/sql/mes_pd_init.sql 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/MesPdArchiveController.java 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveDetailRespVO.java 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveGroupRespVO.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchivePageReqVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveRespVO.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveSaveReqVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/MesPdDocumentController.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentPageReqVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentRespVO.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentSaveReqVO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/documentaudit/MesPdDocumentAuditController.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/documentaudit/vo/MesPdDocumentAuditPageReqVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/documentaudit/vo/MesPdDocumentAuditRespVO.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/documentaudit/vo/MesPdDocumentAuditSaveReqVO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/project/MesPdProjectController.java 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/project/vo/MesPdProjectPageReqVO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/project/vo/MesPdProjectRespVO.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/project/vo/MesPdProjectSaveReqVO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/archive/MesPdArchiveDO.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/document/MesPdDocumentDO.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/documentaudit/MesPdDocumentAuditDO.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/project/MesPdProjectDO.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/archive/MesPdArchiveMapper.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/document/MesPdDocumentMapper.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/documentaudit/MesPdDocumentAuditMapper.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/project/MesPdProjectMapper.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesPdStatusConstants.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pd/MesPdProjectStatusEnum.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/archive/MesPdArchiveService.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/archive/MesPdArchiveServiceImpl.java 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/document/MesPdDocumentService.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/document/MesPdDocumentServiceImpl.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/documentaudit/MesPdDocumentAuditService.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/documentaudit/MesPdDocumentAuditServiceImpl.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/project/MesPdProjectService.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/project/MesPdProjectServiceImpl.java 274 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/resources/mapper/pd/archive/MesPdArchiveMapper.xml 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/archive_group_by_product.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,397 @@
# å½’档台账(按产品聚合)前端对接文档
## æ¶‰åŠé¡µé¢
- å½’档台账列表页(修改)
- å½’档台账详情弹窗/页面(新增)
## API
### 1. åˆ†é¡µæŸ¥è¯¢ï¼ˆæŒ‰é¡¹ç›®/产品聚合)
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pd/archive/page | å½’档台账分页(按项目/产品聚合) |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| pageNo | Integer | æ˜¯ | é¡µç ï¼ˆä»Ž1开始) |
| pageSize | Integer | æ˜¯ | æ¯é¡µæ¡æ•° |
| archiveCode | String | å¦ | å½’档编号(模糊搜索) |
| projectId | Long | å¦ | è®¾è®¡é¡¹ç›®ID |
| archiveStatus | Integer | å¦ | å½’档状态(0=待归档 1=已归档) |
| productCode | String | å¦ | äº§å“ç¼–码(模糊搜索) |
| productName | String | å¦ | äº§å“åç§°ï¼ˆæ¨¡ç³Šæœç´¢ï¼‰ |
**响应结构:**
```json
{
  "code": 200,
  "data": {
    "list": [
      {
        "id": 3,
        "archiveCode": "ARC20240003",
        "projectId": 1,
        "taskCode": "PD20240001",
        "taskName": "新产品设计任务",
        "productCode": "P001",
        "productName": "注塑产品A",
        "archiveCount": 3,
        "archiver": 1,
        "archiverNickname": "王五",
        "archiveStatus": 1,
        "archiveTime": "2024-01-03 10:00:00",
        "remark": "",
        "createTime": "2024-01-01 00:00:00",
        "updater": "1",
        "updaterNickname": "张三",
        "updateTime": "2024-01-03 15:30:00"
      }
    ],
    "total": 5
  }
}
```
**字段说明:**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | æœ€æ–°å½’档记录ID |
| archiveCode | String | æœ€æ–°å½’档编号 |
| projectId | Long | è®¾è®¡é¡¹ç›®ID |
| taskCode | String | ä»»åŠ¡ç¼–ç  |
| taskName | String | ä»»åŠ¡åç§° |
| productCode | String | äº§å“ç¼–码 |
| productName | String | äº§å“åç§° |
| archiveCount | Integer | è¯¥é¡¹ç›®ä¸‹å½’档记录总数 |
| archiver | Long | å½’档人ID |
| archiverNickname | String | å½’档人名称 |
| archiveStatus | Integer | å½’档状态(字典 mes_pd_archive_status) |
| archiveTime | LocalDateTime | å½’档时间 |
| remark | String | å¤‡æ³¨ |
| createTime | LocalDateTime | åˆ›å»ºæ—¶é—´ |
| updater | String | æ›´æ–°äººID |
| updaterNickname | String | æ›´æ–°äººåç§° |
| updateTime | LocalDateTime | æ›´æ–°æ—¶é—´ |
---
### 2. è¯¦æƒ…查询(按产品编码)
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pd/archive/detail-by-product | å½’档台账详情(按产品编码查询) |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| productCode | String | æ˜¯ | äº§å“ç¼–码 |
**响应结构:**
返回 `List<MesPdArchiveDetailRespVO>`,同一产品编码可能关联多个设计项目,每个项目一条详情。
```json
{
  "code": 200,
  "data": [
    {
      "project": {
        "id": 1,
        "taskCode": "PD20240001",
        "taskName": "新产品设计任务",
        "productCode": "P001",
        "productName": "注塑产品A",
        "version": "V1.0",
        "status": 4,
        "chiefDesigner": 1,
        "chiefDesignerNickname": "张三",
        "reviewer": 2,
        "reviewerNickname": "李四",
        "planFinishTime": "2024-12-31 00:00:00",
        "remark": "",
        "createTime": "2024-01-01 00:00:00"
      },
      "archives": [
        {
          "id": 1,
          "archiveCode": "ARC20240001",
          "archiver": 1,
          "archiverNickname": "王五",
          "archiveStatus": 1,
          "archiveTime": "2024-01-01 10:00:00",
          "remark": "",
          "createTime": "2024-01-01 10:00:00"
        },
        {
          "id": 2,
          "archiveCode": "ARC20240002",
          "archiver": 1,
          "archiverNickname": "王五",
          "archiveStatus": 1,
          "archiveTime": "2024-01-02 10:00:00",
          "remark": "",
          "createTime": "2024-01-02 10:00:00"
        }
      ],
      "documents": [
        {
          "id": 1,
          "documentType": "1",
          "documentName": "产品设计图",
          "documentVersion": "V1.0",
          "fileName": "design.pdf",
          "fileUrl": "/files/design.pdf",
          "uploadUserId": 1,
          "uploadUserNickname": "赵六",
          "uploadTime": "2024-01-01 09:00:00",
          "remark": "",
          "createTime": "2024-01-01 09:00:00"
        }
      ]
    }
  ]
}
```
**ProjectInfo å­—段说明:**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | é¡¹ç›®ID |
| taskCode | String | ä»»åŠ¡ç¼–ç  |
| taskName | String | ä»»åŠ¡åç§° |
| productCode | String | äº§å“ç¼–码 |
| productName | String | äº§å“åç§° |
| version | String | ç‰ˆæœ¬å· |
| status | Integer | é¡¹ç›®çŠ¶æ€ï¼ˆå­—å…¸ mes_pd_project_status:0=待发布 1=待上传 2=待审核 3=待归档 4=已归档) |
| chiefDesigner | Long | ä¸»è®¾è®¡å¸ˆID |
| chiefDesignerNickname | String | ä¸»è®¾è®¡å¸ˆåç§° |
| reviewer | Long | å®¡æ ¸äººID |
| reviewerNickname | String | å®¡æ ¸äººåç§° |
| planFinishTime | LocalDateTime | è®¡åˆ’完成时间 |
| remark | String | å¤‡æ³¨ |
| createTime | LocalDateTime | åˆ›å»ºæ—¶é—´ |
**ArchiveInfo å­—段说明:**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | å½’æ¡£ID |
| archiveCode | String | å½’档编号 |
| archiver | Long | å½’档人ID |
| archiverNickname | String | å½’档人名称 |
| archiveStatus | Integer | å½’档状态(字典 mes_pd_archive_status) |
| archiveTime | LocalDateTime | å½’档时间 |
| remark | String | å¤‡æ³¨ |
| createTime | LocalDateTime | åˆ›å»ºæ—¶é—´ |
**DocumentInfo å­—段说明:**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | èµ„æ–™ID |
| documentType | String | èµ„料类型(字典 mes_pd_document_type) |
| documentName | String | èµ„料名称 |
| documentVersion | String | èµ„料版本 |
| fileName | String | æ–‡ä»¶å |
| fileUrl | String | æ–‡ä»¶URL |
| uploadUserId | Long | ä¸Šä¼ äººID |
| uploadUserNickname | String | ä¸Šä¼ äººåç§° |
| uploadTime | LocalDateTime | ä¸Šä¼ æ—¶é—´ |
| remark | String | å¤‡æ³¨ |
| createTime | LocalDateTime | åˆ›å»ºæ—¶é—´ |
---
## å‰ç«¯ä¿®æ”¹ç‚¹
### 1. åˆ—表页 - æœç´¢æ æ–°å¢ž
```html
<el-form :model="queryParams" ref="queryFormRef" :inline="true">
  <el-form-item label="产品编码" prop="productCode">
    <el-input v-model="queryParams.productCode" placeholder="请输入产品编码" clearable />
  </el-form-item>
  <el-form-item label="产品名称" prop="productName">
    <el-input v-model="queryParams.productName" placeholder="请输入产品名称" clearable />
  </el-form-item>
  <el-form-item label="归档状态" prop="archiveStatus">
    <el-select v-model="queryParams.archiveStatus" placeholder="请选择归档状态" clearable>
      <el-option v-for="dict in getIntDictOptions(DICT_TYPE.MES_PD_ARCHIVE_STATUS)"
        :key="dict.value" :label="dict.label" :value="dict.value" />
    </el-select>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
    <el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
  </el-form-item>
</el-form>
```
### 2. åˆ—表页 - è¡¨æ ¼æ–°å¢žåˆ—
```html
<el-table v-loading="loading" :data="list">
  <el-table-column label="产品编码" align="center" prop="productCode" />
  <el-table-column label="产品名称" align="center" prop="productName" />
  <el-table-column label="归档数量" align="center" prop="archiveCount" width="100" />
  <el-table-column label="更新人" align="center" prop="updaterNickname" />
  <el-table-column label="更新时间" align="center" prop="updateTime" width="180" />
  <el-table-column label="操作" align="center" width="100">
    <template #default="scope">
      <el-button link type="primary" @click="handleDetail(scope.row.productCode)"
        v-hasPermi="['mes:pd-archive:query']">详情</el-button>
    </template>
  </el-table-column>
</el-table>
```
### 3. è¯¦æƒ…弹窗/页面 - æ–°å¢ž
```html
<!-- æŒ‰äº§å“ç¼–码可能返回多个项目,用 tab æˆ–手风琴展示 -->
<el-tabs v-model="activeProject" type="border-card">
  <el-tab-pane v-for="(detail, index) in detailList" :key="detail.project.id"
    :label="detail.project.taskName" :name="String(index)">
    <!-- é¡¹ç›®ä¿¡æ¯ -->
    <el-descriptions :column="3" border>
      <el-descriptions-item label="任务编码">{{ detail.project.taskCode }}</el-descriptions-item>
      <el-descriptions-item label="任务名称">{{ detail.project.taskName }}</el-descriptions-item>
      <el-descriptions-item label="产品编码">{{ detail.project.productCode }}</el-descriptions-item>
      <el-descriptions-item label="产品名称">{{ detail.project.productName }}</el-descriptions-item>
      <el-descriptions-item label="版本号">{{ detail.project.version }}</el-descriptions-item>
      <el-descriptions-item label="主设计师">{{ detail.project.chiefDesignerNickname }}</el-descriptions-item>
      <el-descriptions-item label="审核人">{{ detail.project.reviewerNickname }}</el-descriptions-item>
      <el-descriptions-item label="计划完成时间">{{ detail.project.planFinishTime }}</el-descriptions-item>
    </el-descriptions>
    <!-- å½’档记录列表 -->
    <el-card header="归档记录" style="margin-top: 16px">
      <el-table :data="detail.archives" border>
        <el-table-column label="归档编号" prop="archiveCode" />
        <el-table-column label="归档人" prop="archiverNickname" />
        <el-table-column label="归档状态" prop="archiveStatus">
          <template #default="scope">
            <dict-tag :type="DICT_TYPE.MES_PD_ARCHIVE_STATUS" :value="scope.row.archiveStatus" />
          </template>
        </el-table-column>
        <el-table-column label="归档时间" prop="archiveTime" width="180" />
        <el-table-column label="备注" prop="remark" show-overflow-tooltip />
      </el-table>
    </el-card>
    <!-- è®¾è®¡èµ„料列表 -->
    <el-card header="设计资料" style="margin-top: 16px">
      <el-table :data="detail.documents" border>
        <el-table-column label="资料名称" prop="documentName" />
        <el-table-column label="资料类型" prop="documentType">
          <template #default="scope">
            <dict-tag :type="DICT_TYPE.MES_PD_DOCUMENT_TYPE" :value="scope.row.documentType" />
          </template>
        </el-table-column>
        <el-table-column label="版本" prop="documentVersion" width="80" />
        <el-table-column label="文件名" prop="fileName" />
        <el-table-column label="上传人" prop="uploadUserNickname" />
        <el-table-column label="上传时间" prop="uploadTime" width="180" />
        <el-table-column label="操作" width="80">
          <template #default="scope">
            <el-button v-if="scope.row.fileUrl" link type="primary" @click="handleDownload(scope.row)">下载</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-card>
  </el-tab-pane>
</el-tabs>
```
### 4. API æŽ¥å£å®šä¹‰
文件路径:`src/api/mes/pd/archive/index.ts`
```ts
import request from '@/config/axios'
// èŽ·å¾—å½’æ¡£å°è´¦åˆ†é¡µï¼ˆæŒ‰é¡¹ç›®/产品聚合)
export const getArchivePage = (params) => {
  return request.get({ url: '/mes/pd/archive/page', params })
}
// èŽ·å¾—å½’æ¡£å°è´¦è¯¦æƒ…ï¼ˆæŒ‰äº§å“ç¼–ç ï¼‰
export const getArchiveDetailByProduct = (productCode: string) => {
  return request.get({ url: '/mes/pd/archive/detail-by-product', params: { productCode } })
}
// åˆ›å»ºå½’档台账
export const createArchive = (data) => {
  return request.post({ url: '/mes/pd/archive/create', data })
}
// æ›´æ–°å½’档台账
export const updateArchive = (data) => {
  return request.put({ url: '/mes/pd/archive/update', data })
}
// åˆ é™¤å½’档台账
export const deleteArchive = (id: number) => {
  return request.delete({ url: '/mes/pd/archive/delete?id=' + id })
}
// èŽ·å¾—å½’æ¡£å°è´¦
export const getArchive = (id: number) => {
  return request.get({ url: '/mes/pd/archive/get?id=' + id })
}
// å¯¼å‡ºå½’档台账 Excel
export const exportArchiveExcel = (params) => {
  return request.download({ url: '/mes/pd/archive/export-excel', params })
}
```
### 5. data æ•°æ®
```js
const queryParams = reactive({
  pageNo: 1,
  pageSize: 10,
  archiveCode: undefined,
  projectId: undefined,
  archiveStatus: undefined,
  productCode: undefined,
  productName: undefined,
})
const detailList = ref([])
const detailVisible = ref(false)
const activeProject = ref('0')
```
### 6. æ–¹æ³•
```js
// æŸ¥çœ‹è¯¦æƒ…(按产品编码)
const handleDetail = async (productCode) => {
  const res = await ArchiveApi.getArchiveDetailByProduct(productCode)
  detailList.value = res
  activeProject.value = '0'
  detailVisible.value = true
}
```
## æ³¨æ„äº‹é¡¹
- **分页聚合逻辑**:`/page` æŽ¥å£æŒ‰ `project_id` åˆ†ç»„,每组取最新一条归档记录(`MAX(id)`),所以 `updater` å’Œ `updateTime` æ˜¯è¯¥ç»„最新归档的更新信息
- **详情接口参数变化**:从 `projectId` æ”¹ä¸º `productCode`,因为前端列表展示的是产品维度,点击详情时传产品编码更自然
- **详情返回列表**:`detail-by-product` è¿”回 `List`,因为同一产品编码可能关联多个设计项目(如不同版本),前端用 tab åˆ‡æ¢å±•示
- **字典类型**:
  - å½’档状态:`mes_pd_archive_status`(0=待归档 1=已归档)
  - é¡¹ç›®çŠ¶æ€ï¼š`mes_pd_project_status`(0=待发布 1=待上传 2=待审核 3=待归档 4=已归档)
  - èµ„料类型:`mes_pd_document_type`
docs/product_design.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,301 @@
# äº§å“è®¾è®¡
## æ¶‰åŠé¡µé¢
- è®¾è®¡é¡¹ç›®åˆ—表页(`mes/pd/project/index`)
- è®¾è®¡é¡¹ç›®è¯¦æƒ…/新增/编辑弹窗
- èµ„料子表管理弹窗
- èµ„料审核弹窗
- å½’档台账列表页(`mes/pd/archive/index`)
- å½’档台账详情/新增/编辑弹窗
## çŠ¶æ€æµè½¬
**待发布(0) â†’ å¾…上传(1) â†’ å¾…审核(2) â†’ å¾…å½’æ¡£(3) â†’ å·²å½’æ¡£(4)**
| çŠ¶æ€å€¼ | çŠ¶æ€å | å…è®¸æ“ä½œ |
|--------|--------|----------|
| 0 | å¾…发布 | ä¿®æ”¹ã€åˆ é™¤ã€å‘布 |
| 1 | å¾…上传 | ä¸Šä¼ èµ„æ–™ |
| 2 | å¾…审核 | å®¡æ ¸ |
| 3 | å¾…å½’æ¡£ | å½’æ¡£ |
| 4 | å·²å½’æ¡£ | æ—  |
## API
### è®¾è®¡é¡¹ç›®
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | /mes/pd/project/create | åˆ›å»ºè®¾è®¡é¡¹ç›® |
| PUT | /mes/pd/project/update | æ›´æ–°è®¾è®¡é¡¹ç›® |
| DELETE | /mes/pd/project/delete | åˆ é™¤è®¾è®¡é¡¹ç›® |
| GET | /mes/pd/project/get | èŽ·å–è®¾è®¡é¡¹ç›®è¯¦æƒ… |
| GET | /mes/pd/project/page | èŽ·å–è®¾è®¡é¡¹ç›®åˆ†é¡µ |
| GET | /mes/pd/project/export-excel | å¯¼å‡ºè®¾è®¡é¡¹ç›®Excel |
| PUT | /mes/pd/project/publish | å‘布(待发布→待上传) |
| PUT | /mes/pd/project/upload | ä¸Šä¼ èµ„料(待上传→待审核),**自动创建资料审核记录** |
| PUT | /mes/pd/project/audit | å®¡æ ¸ï¼ˆå¾…审核→待归档) |
| PUT | /mes/pd/project/archive | å½’档(待归档→已归档),**自动创建归档台账记录** |
**创建/更新请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | å¦ï¼ˆæ›´æ–°æ—¶å¿…填) | ç¼–号 |
| taskCode | String | æ˜¯ | ä»»åŠ¡ç¼–ç  |
| taskName | String | æ˜¯ | ä»»åŠ¡åç§° |
| productCode | String | å¦ | äº§å“ç¼–码 |
| productName | String | å¦ | äº§å“åç§° |
| version | String | å¦ | ç‰ˆæœ¬å· |
| chiefDesigner | Long | å¦ | ä¸»è®¾è®¡å¸ˆï¼ˆç”¨æˆ·ID) |
| reviewer | Long | å¦ | å®¡æ ¸äººï¼ˆç”¨æˆ·ID) |
| planFinishTime | LocalDateTime | å¦ | è®¡åˆ’完成时间 |
| remark | String | å¦ | å¤‡æ³¨ |
**分页请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| taskCode | String | å¦ | ä»»åŠ¡ç¼–ç ï¼ˆæ¨¡ç³Šï¼‰ |
| taskName | String | å¦ | ä»»åŠ¡åç§°ï¼ˆæ¨¡ç³Šï¼‰ |
| productCode | String | å¦ | äº§å“ç¼–码(模糊) |
| productName | String | å¦ | äº§å“åç§°ï¼ˆæ¨¡ç³Šï¼‰ |
| status | Integer | å¦ | çŠ¶æ€ |
| planFinishTime | LocalDateTime[] | å¦ | è®¡åˆ’完成时间范围 |
**响应:** åœ¨åŸºç¡€å­—段上额外包含 `chiefDesignerNickname`(主设计师名称)、`reviewerNickname`(审核人名称)
### è®¾è®¡èµ„æ–™
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | /mes/pd/document/create | åˆ›å»ºè®¾è®¡èµ„æ–™ |
| PUT | /mes/pd/document/update | æ›´æ–°è®¾è®¡èµ„æ–™ |
| DELETE | /mes/pd/document/delete | åˆ é™¤è®¾è®¡èµ„æ–™ |
| GET | /mes/pd/document/get | èŽ·å–è®¾è®¡èµ„æ–™è¯¦æƒ… |
| GET | /mes/pd/document/page | èŽ·å–è®¾è®¡èµ„æ–™åˆ†é¡µ |
| GET | /mes/pd/document/export-excel | å¯¼å‡ºè®¾è®¡èµ„æ–™Excel |
**创建/更新请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | å¦ï¼ˆæ›´æ–°æ—¶å¿…填) | ç¼–号 |
| projectId | Long | æ˜¯ | è®¾è®¡é¡¹ç›®ID |
| documentType | String | å¦ | èµ„料类型(字典 mes_pd_document_type) |
| documentName | String | æ˜¯ | èµ„料名称 |
| documentVersion | String | å¦ | ç‰ˆæœ¬ |
| fileName | String | å¦ | æ–‡ä»¶å |
| uploadUserId | Long | å¦ | ä¸Šä¼ äººï¼ˆç”¨æˆ·ID) |
| uploadTime | LocalDateTime | å¦ | ä¸Šä¼ æ—¶é—´ |
| fileUrl | String | å¦ | æ–‡ä»¶URL(通过 `/infra/file/upload` ä¸Šä¼ åŽèŽ·å–ï¼‰ |
| remark | String | å¦ | å¤‡æ³¨ |
**响应:** åœ¨åŸºç¡€å­—段上额外包含 `uploadUserNickname`(上传人名称)、`fileUrl`(文件URL)
### èµ„料审核
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | /mes/pd/document-audit/create | åˆ›å»ºèµ„料审核(**上传资料时后端自动调用,前端无需单独调用**) |
| PUT | /mes/pd/document-audit/update | æ›´æ–°èµ„料审核 |
| DELETE | /mes/pd/document-audit/delete | åˆ é™¤èµ„料审核 |
| GET | /mes/pd/document-audit/get | èŽ·å–èµ„æ–™å®¡æ ¸è¯¦æƒ… |
| GET | /mes/pd/document-audit/page | èŽ·å–èµ„æ–™å®¡æ ¸åˆ†é¡µ |
| GET | /mes/pd/document-audit/export-excel | å¯¼å‡ºèµ„料审核Excel |
**创建/更新请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | å¦ï¼ˆæ›´æ–°æ—¶å¿…填) | ç¼–号 |
| auditNo | String | æ˜¯ | å®¡æ ¸å•号(编码规则 PD_DOCUMENT_AUDIT_CODE è‡ªåŠ¨ç”Ÿæˆï¼‰ |
| documentId | Long | æ˜¯ | èµ„æ–™ID |
| documentName | String | å¦ | èµ„料名称(冗余) |
| documentVersion | String | å¦ | èµ„料版本(冗余) |
| reviewer | Long | å¦ | å®¡æ ¸äººï¼ˆç”¨æˆ·ID,自动取项目的审核人) |
| auditStatus | Integer | å¦ | å®¡æ ¸çŠ¶æ€ï¼ˆå­—å…¸ mes_pd_document_audit_status) |
| auditTime | LocalDateTime | å¦ | å®¡æ ¸æ—¶é—´ |
| remark | String | å¦ | å¤‡æ³¨ |
**响应:** åœ¨åŸºç¡€å­—段上额外包含 `reviewerNickname`(审核人名称)
### å½’档台账
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | /mes/pd/archive/create | åˆ›å»ºå½’档台账(**归档时后端自动调用,前端无需单独调用**) |
| PUT | /mes/pd/archive/update | æ›´æ–°å½’档台账 |
| DELETE | /mes/pd/archive/delete | åˆ é™¤å½’档台账 |
| GET | /mes/pd/archive/get | èŽ·å–å½’æ¡£å°è´¦è¯¦æƒ… |
| GET | /mes/pd/archive/page | èŽ·å–å½’æ¡£å°è´¦åˆ†é¡µ |
| GET | /mes/pd/archive/export-excel | å¯¼å‡ºå½’档台账Excel |
**创建/更新请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | å¦ï¼ˆæ›´æ–°æ—¶å¿…填) | ç¼–号 |
| archiveCode | String | æ˜¯ | å½’档编号(编码规则 PD_ARCHIVE_CODE è‡ªåŠ¨ç”Ÿæˆï¼‰ |
| projectId | Long | æ˜¯ | è®¾è®¡é¡¹ç›®ID |
| archiver | Long | å¦ | å½’档人(用户ID,自动取当前登录用户) |
| archiveStatus | Integer | å¦ | å½’档状态(字典 mes_pd_archive_status) |
| archiveTime | LocalDateTime | å¦ | å½’档时间(自动取当前时间) |
| remark | String | å¦ | å¤‡æ³¨ |
**响应:** åœ¨åŸºç¡€å­—段上额外包含 `archiverNickname`(归档人名称)、`taskCode`(任务编码)、`taskName`(任务名称)
## å‰ç«¯ä¿®æ”¹ç‚¹
### 1. è®¾è®¡é¡¹ç›®åˆ—表页
```html
<!-- çŠ¶æ€æ ‡ç­¾ -->
<el-tag :type="getStatusTagType(row.status)">
  {{ getStatusLabel(row.status) }}
</el-tag>
<!-- çŠ¶æ€æµè½¬æŒ‰é’® -->
<el-button v-if="row.status === 0" @click="handlePublish(row.id)" type="primary">发布</el-button>
<el-button v-if="row.status === 1" @click="handleUpload(row.id)" type="warning">上传资料</el-button>
<el-button v-if="row.status === 2" @click="handleAudit(row.id)" type="success">审核</el-button>
<el-button v-if="row.status === 3" @click="handleArchive(row.id)" type="danger">归档</el-button>
```
### 2. data æ•°æ®
```js
data() {
  return {
    // çŠ¶æ€æµè½¬API映射
    statusFlowApi: {
      0: publishProject,
      1: uploadProject,
      2: auditProject,
      3: archiveProject,
    },
  }
}
```
### 3. æ–¹æ³•
```js
// çŠ¶æ€æµè½¬
handlePublish(id) {
  publishProject(id).then(() => { this.getList() })
},
handleUpload(id) {
  uploadProject(id).then(() => { this.getList() })
},
handleAudit(id) {
  auditProject(id).then(() => { this.getList() })
},
handleArchive(id) {
  archiveProject(id).then(() => { this.getList() })
},
// èµ„料子表 - æ‰“开弹窗
handleDocument(projectId) {
  this.documentOpen = true
  this.documentProjectId = projectId
  this.getDocumentList()
},
```
### 4. API å®šä¹‰
```js
// è®¾è®¡é¡¹ç›®
export function createProject(data) { return request({ url: '/mes/pd/project/create', method: 'post', data }) }
export function updateProject(data) { return request({ url: '/mes/pd/project/update', method: 'put', data }) }
export function deleteProject(id) { return request({ url: '/mes/pd/project/delete', method: 'delete', params: { id } }) }
export function getProject(id) { return request({ url: '/mes/pd/project/get', method: 'get', params: { id } }) }
export function getProjectPage(params) { return request({ url: '/mes/pd/project/page', method: 'get', params }) }
export function exportProjectExcel(params) { return request({ url: '/mes/pd/project/export-excel', method: 'get', params, responseType: 'blob' }) }
export function publishProject(id) { return request({ url: '/mes/pd/project/publish', method: 'put', params: { id } }) }
export function uploadProject(id) { return request({ url: '/mes/pd/project/upload', method: 'put', params: { id } }) }
export function auditProject(id) { return request({ url: '/mes/pd/project/audit', method: 'put', params: { id } }) }
export function archiveProject(id) { return request({ url: '/mes/pd/project/archive', method: 'put', params: { id } }) }
// è®¾è®¡èµ„æ–™
export function createDocument(data) { return request({ url: '/mes/pd/document/create', method: 'post', data }) }
export function updateDocument(data) { return request({ url: '/mes/pd/document/update', method: 'put', data }) }
export function deleteDocument(id) { return request({ url: '/mes/pd/document/delete', method: 'delete', params: { id } }) }
export function getDocument(id) { return request({ url: '/mes/pd/document/get', method: 'get', params: { id } }) }
export function getDocumentPage(params) { return request({ url: '/mes/pd/document/page', method: 'get', params }) }
// æ–‡ä»¶ä¸Šä¼ ï¼ˆé€šç”¨æŽ¥å£ï¼Œä¸Šä¼ åŽè¿”回URL,存入资料表 fileUrl å­—段)
export function uploadFile(file, directory) { return request({ url: '/infra/file/upload', method: 'post', data: { file, directory }, headers: { 'Content-Type': 'multipart/form-data' } }) }
// èµ„料审核(前端无需调用 create,上传资料时后端自动创建)
export function updateDocumentAudit(data) { return request({ url: '/mes/pd/document-audit/update', method: 'put', data }) }
export function deleteDocumentAudit(id) { return request({ url: '/mes/pd/document-audit/delete', method: 'delete', params: { id } }) }
export function getDocumentAuditPage(params) { return request({ url: '/mes/pd/document-audit/page', method: 'get', params }) }
// å½’档台账(前端无需调用 create,归档时后端自动创建)
export function updateArchive(data) { return request({ url: '/mes/pd/archive/update', method: 'put', data }) }
export function deleteArchive(id) { return request({ url: '/mes/pd/archive/delete', method: 'delete', params: { id } }) }
export function getArchive(id) { return request({ url: '/mes/pd/archive/get', method: 'get', params: { id } }) }
export function getArchivePage(params) { return request({ url: '/mes/pd/archive/page', method: 'get', params }) }
export function exportArchiveExcel(params) { return request({ url: '/mes/pd/archive/export-excel', method: 'get', params, responseType: 'blob' }) }
```
### 5. èµ„料子表 - æ–‡ä»¶ä¸Šä¼ ç»„ä»¶
```html
<!-- è®¾è®¡èµ„料新增/编辑弹窗中添加文件上传 -->
<el-form-item label="文件" prop="fileUrl">
  <UploadFile v-model="formData.fileUrl" :limit="1" />
</el-form-item>
<!-- èµ„料列表中显示文件链接 -->
<el-table-column label="文件" prop="fileUrl">
  <template #default="{ row }">
    <el-link v-if="row.fileUrl" :href="row.fileUrl" target="_blank" type="primary">
      {{ row.fileName || '查看文件' }}
    </el-link>
    <span v-else>-</span>
  </template>
</el-table-column>
```
```js
// æ–‡ä»¶ä¸Šä¼ å›žè°ƒ
handleFileUploadSuccess(url) {
  this.formData.fileUrl = url
},
```
### 6. å­—典数据更新 SQL
```sql
UPDATE system_dict_data SET label = '待发布' WHERE dict_type = 'mes_pd_project_status' AND value = '0';
UPDATE system_dict_data SET label = '待上传' WHERE dict_type = 'mes_pd_project_status' AND value = '1';
UPDATE system_dict_data SET label = '待审核' WHERE dict_type = 'mes_pd_project_status' AND value = '2';
UPDATE system_dict_data SET label = '待归档' WHERE dict_type = 'mes_pd_project_status' AND value = '3';
UPDATE system_dict_data SET label = '已归档' WHERE dict_type = 'mes_pd_project_status' AND value = '4';
```
## ç¼–码规则配置
需要在编码规则管理中新增以下两条规则:
| è§„则代码 | è§„则名称 | è¯´æ˜Ž |
|----------|----------|------|
| PD_DOCUMENT_AUDIT_CODE | èµ„料审核单编码 | ä¸Šä¼ èµ„料时自动生成审核单号 |
| PD_ARCHIVE_CODE | å½’档台账编码 | å½’档时自动生成归档编号 |
## æ³¨æ„äº‹é¡¹
- è®¾è®¡é¡¹ç›®çŠ¶æ€æµè½¬ä¸ºçº¿æ€§5状态(0→1→2→3→4),不支持回退或取消
- åªæœ‰å¾…发布(0)状态才允许修改和删除设计项目
- åˆ é™¤è®¾è®¡é¡¹ç›®ä¼šçº§è”删除其下的所有资料记录
- èµ„料子表的 `fileUrl` å­—段存储文件URL,前端通过 `/infra/file/upload` é€šç”¨æŽ¥å£ä¸Šä¼ æ–‡ä»¶åŽèŽ·å–URL
- èµ„料审核表与资料子表为一对多关系,一条资料可以有多条审核记录
- å­—典类型需要在系统管理中配置:`mes_pd_project_status`、`mes_pd_document_type`、`mes_pd_document_audit_status`、`mes_pd_archive_status`
- `mes_pd_document_type`(资料类型)的字典数据需要根据实际业务补充
- **上传资料**时后端自动为项目下所有资料创建审核记录(审核人=项目 reviewer,审核状态=待审核),前端无需单独调用 `POST /mes/pd/document-audit/create`
- **归档**时后端自动创建归档台账记录(归档人=当前登录用户,归档时间=当前时间),前端无需单独调用 `POST /mes/pd/archive/create`
- ä¸Šä¼ èµ„料和归档操作是事务性的,状态更新和子表创建在同一个事务中,要么全部成功要么全部回滚
docs/sql/mes_pd_init.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,160 @@
-- =============================================
-- MES äº§å“è®¾è®¡æ¨¡å— - åˆå§‹åŒ–脚本
-- =============================================
-- ----------------------------
-- è®¾è®¡é¡¹ç›®
-- ----------------------------
CREATE TABLE IF NOT EXISTS `mes_pd_project` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
    `task_code` varchar(64) DEFAULT NULL COMMENT '任务编码',
    `task_name` varchar(255) DEFAULT NULL COMMENT '任务名称',
    `product_code` varchar(64) DEFAULT NULL COMMENT '产品编码',
    `product_name` varchar(255) DEFAULT NULL COMMENT '产品名称',
    `version` varchar(64) DEFAULT NULL COMMENT '版本号',
    `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态(0=待发布 1=待上传 2=待审核 3=待归档 4=已归档)',
    `chief_designer` bigint DEFAULT NULL COMMENT '主设计师',
    `reviewer` bigint DEFAULT NULL COMMENT '审核人',
    `plan_finish_time` datetime DEFAULT NULL COMMENT '计划完成时间',
    `remark` varchar(500) DEFAULT NULL COMMENT '备注',
    `creator` varchar(64) DEFAULT '' COMMENT '创建者',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updater` varchar(64) DEFAULT '' COMMENT '更新者',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='MES è®¾è®¡é¡¹ç›®';
-- ----------------------------
-- è®¾è®¡èµ„料子表
-- ----------------------------
CREATE TABLE IF NOT EXISTS `mes_pd_document` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
    `project_id` bigint NOT NULL COMMENT '设计项目ID',
    `document_type` varchar(64) DEFAULT NULL COMMENT '资料类型',
    `document_name` varchar(255) DEFAULT NULL COMMENT '资料名称',
    `document_version` varchar(64) DEFAULT NULL COMMENT '版本',
    `file_name` varchar(255) DEFAULT NULL COMMENT '文件名',
    `upload_user_id` bigint DEFAULT NULL COMMENT '上传人',
    `upload_time` datetime DEFAULT NULL COMMENT '上传时间',
    `file_url` varchar(512) DEFAULT NULL COMMENT '文件URL(预留)',
    `remark` varchar(500) DEFAULT NULL COMMENT '备注',
    `creator` varchar(64) DEFAULT '' COMMENT '创建者',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updater` varchar(64) DEFAULT '' COMMENT '更新者',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='MES è®¾è®¡èµ„æ–™';
-- ----------------------------
-- èµ„料审核表
-- ----------------------------
CREATE TABLE IF NOT EXISTS `mes_pd_document_audit` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
    `audit_no` varchar(64) DEFAULT NULL COMMENT '审核单号',
    `document_id` bigint NOT NULL COMMENT '资料ID',
    `document_name` varchar(255) DEFAULT NULL COMMENT '资料名称(冗余)',
    `document_version` varchar(64) DEFAULT NULL COMMENT '资料版本(冗余)',
    `reviewer` bigint DEFAULT NULL COMMENT '审核人',
    `audit_status` tinyint DEFAULT NULL COMMENT '审核状态',
    `audit_time` datetime DEFAULT NULL COMMENT '审核时间',
    `remark` varchar(500) DEFAULT NULL COMMENT '备注',
    `creator` varchar(64) DEFAULT '' COMMENT '创建者',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updater` varchar(64) DEFAULT '' COMMENT '更新者',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='MES èµ„料审核';
-- ----------------------------
-- å½’档台账
-- ----------------------------
CREATE TABLE IF NOT EXISTS `mes_pd_archive` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
    `archive_code` varchar(64) DEFAULT NULL COMMENT '归档编号',
    `project_id` bigint NOT NULL COMMENT '设计项目ID',
    `archiver` bigint DEFAULT NULL COMMENT '归档人',
    `archive_status` tinyint DEFAULT NULL COMMENT '归档状态',
    `archive_time` datetime DEFAULT NULL COMMENT '归档时间',
    `remark` varchar(500) DEFAULT NULL COMMENT '备注',
    `creator` varchar(64) DEFAULT '' COMMENT '创建者',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updater` varchar(64) DEFAULT '' COMMENT '更新者',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='MES å½’档台账';
-- ----------------------------
-- å­—典类型
-- ----------------------------
INSERT INTO `system_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
('设计项目状态', 'mes_pd_project_status', 0, '设计项目状态', '1', NOW(), '1', NOW(), b'0'),
('资料类型', 'mes_pd_document_type', 0, '资料类型', '1', NOW(), '1', NOW(), b'0'),
('资料审核状态', 'mes_pd_document_audit_status', 0, '资料审核状态', '1', NOW(), '1', NOW(), b'0'),
('归档状态', 'mes_pd_archive_status', 0, '归档状态', '1', NOW(), '1', NOW(), b'0');
-- ----------------------------
-- å­—典数据 - è®¾è®¡é¡¹ç›®çŠ¶æ€
-- ----------------------------
INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
(0, '待发布', '0', 'mes_pd_project_status', 0, 'info', '', '', '1', NOW(), '1', NOW(), b'0'),
(1, '待上传', '1', 'mes_pd_project_status', 0, 'primary', '', '', '1', NOW(), '1', NOW(), b'0'),
(2, '待审核', '2', 'mes_pd_project_status', 0, 'warning', '', '', '1', NOW(), '1', NOW(), b'0'),
(3, '待归档', '3', 'mes_pd_project_status', 0, 'success', '', '', '1', NOW(), '1', NOW(), b'0'),
(4, '已归档', '4', 'mes_pd_project_status', 0, 'danger', '', '', '1', NOW(), '1', NOW(), b'0');
-- ----------------------------
-- å­—典数据 - èµ„料审核状态
-- ----------------------------
INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
(0, '待审核', '0', 'mes_pd_document_audit_status', 0, 'info', '', '', '1', NOW(), '1', NOW(), b'0'),
(1, '审核通过', '1', 'mes_pd_document_audit_status', 0, 'success', '', '', '1', NOW(), '1', NOW(), b'0'),
(2, '审核不通过', '2', 'mes_pd_document_audit_status', 0, 'danger', '', '', '1', NOW(), '1', NOW(), b'0');
-- ----------------------------
-- å­—典数据 - å½’档状态
-- ----------------------------
INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
(0, '待归档', '0', 'mes_pd_archive_status', 0, 'info', '', '', '1', NOW(), '1', NOW(), b'0'),
(1, '已归档', '1', 'mes_pd_archive_status', 0, 'success', '', '', '1', NOW(), '1', NOW(), b'0');
-- ----------------------------
-- èœå• - ä¸€çº§ç›®å½•:产品设计
-- æ³¨æ„ï¼šparent_id éœ€è¦æ ¹æ®å®žé™…MES父菜单ID调整
-- ----------------------------
INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
('产品设计', '', 2, 20, 0, 'pd', '', '', 0, '1', NOW(), '1', NOW(), b'0');
SET @pd_menu_id = LAST_INSERT_ID();
-- äºŒçº§èœå•:设计项目
INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
('设计项目', '', 2, 1, @pd_menu_id, 'project', '', 'mes/pd/project/index', 0, '1', NOW(), '1', NOW(), b'0');
SET @project_menu_id = LAST_INSERT_ID();
-- è®¾è®¡é¡¹ç›®æŒ‰é’®æƒé™
INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
('设计项目查询', 'mes:pd-project:query', 3, 1, @project_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0'),
('设计项目创建', 'mes:pd-project:create', 3, 2, @project_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0'),
('设计项目更新', 'mes:pd-project:update', 3, 3, @project_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0'),
('设计项目删除', 'mes:pd-project:delete', 3, 4, @project_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0'),
('设计项目导出', 'mes:pd-project:export', 3, 5, @project_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0');
-- äºŒçº§èœå•:归档台账
INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
('归档台账', '', 2, 2, @pd_menu_id, 'archive', '', 'mes/pd/archive/index', 0, '1', NOW(), '1', NOW(), b'0');
SET @archive_menu_id = LAST_INSERT_ID();
-- å½’档台账按钮权限
INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
('归档台账查询', 'mes:pd-archive:query', 3, 1, @archive_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0'),
('归档台账创建', 'mes:pd-archive:create', 3, 2, @archive_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0'),
('归档台账更新', 'mes:pd-archive:update', 3, 3, @archive_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0'),
('归档台账删除', 'mes:pd-archive:delete', 3, 4, @archive_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0'),
('归档台账导出', 'mes:pd-archive:export', 3, 5, @archive_menu_id, '', '', '', 0, '1', NOW(), '1', NOW(), b'0');
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/MesPdArchiveController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.archive;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.*;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.archive.MesPdArchiveDO;
import cn.iocoder.yudao.module.mes.service.pd.archive.MesPdArchiveService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - MES å½’档台账")
@RestController
@RequestMapping("/mes/pd/archive")
@Validated
public class MesPdArchiveController {
    @Resource
    private MesPdArchiveService archiveService;
    @Resource
    private AdminUserApi adminUserApi;
    @PostMapping("/create")
    @Operation(summary = "创建归档台账")
    @PreAuthorize("@ss.hasPermission('mes:pd-archive:create')")
    public CommonResult<Long> createArchive(@Valid @RequestBody MesPdArchiveSaveReqVO createReqVO) {
        return success(archiveService.createArchive(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新归档台账")
    @PreAuthorize("@ss.hasPermission('mes:pd-archive:update')")
    public CommonResult<Boolean> updateArchive(@Valid @RequestBody MesPdArchiveSaveReqVO updateReqVO) {
        archiveService.updateArchive(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除归档台账")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-archive:delete')")
    public CommonResult<Boolean> deleteArchive(@RequestParam("id") Long id) {
        archiveService.deleteArchive(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得归档台账")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-archive:query')")
    public CommonResult<MesPdArchiveRespVO> getArchive(@RequestParam("id") Long id) {
        MesPdArchiveDO archive = archiveService.getArchive(id);
        if (archive == null) {
            return success(null);
        }
        return success(BeanUtils.toBean(archive, MesPdArchiveRespVO.class));
    }
    @GetMapping("/page")
    @Operation(summary = "获得归档台账分页(按项目/产品聚合)")
    @PreAuthorize("@ss.hasPermission('mes:pd-archive:query')")
    public CommonResult<PageResult<MesPdArchiveRespVO>> getArchivePage(@Valid MesPdArchivePageReqVO pageReqVO,
                                                                        Page<MesPdArchiveGroupRespVO> page) {
        IPage<MesPdArchiveGroupRespVO> archivePage = archiveService.getArchivePageGroupByProject(pageReqVO, page);
        return CommonResult.success(new PageResult<>(
                buildArchiveRespVOList(archivePage.getRecords()), archivePage.getTotal()));
    }
    @GetMapping("/detail-by-product")
    @Operation(summary = "获得归档台账详情(按产品)")
    @Parameter(name = "productCode", description = "产品编号", required = true, example = "1")
    @PreAuthorize("@ss.hasPermission('mes:pd-archive:query')")
    public CommonResult<List<MesPdArchiveDetailRespVO>> getArchiveDetailByProduct(@RequestParam("productCode") String productCode) {
        return success(archiveService.getArchiveDetailByProductCode(productCode));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出归档台账 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-archive:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportArchiveExcel(@Valid MesPdArchivePageReqVO pageReqVO,
                                   HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        IPage<MesPdArchiveGroupRespVO> archivePage = archiveService.getArchivePageGroupByProject(pageReqVO, null);
        ExcelUtils.write(response, "归档台账.xls", "数据", MesPdArchiveRespVO.class,
                buildArchiveRespVOList(archivePage.getRecords()));
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<MesPdArchiveRespVO> buildArchiveRespVOList(List<MesPdArchiveGroupRespVO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 1. æ‰¹é‡èŽ·å–æ›´æ–°äººæ˜µç§°ï¼ˆupdater æ˜¯ String ç±»åž‹çš„用户ID)
        Set<String> updaterIds = list.stream()
                .map(MesPdArchiveGroupRespVO::getUpdater)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, AdminUserRespDTO> userMap = Collections.emptyMap();
        if (!updaterIds.isEmpty()) {
            Set<Long> userIds = updaterIds.stream()
                    .filter(id -> id.matches("\\d+"))
                    .map(Long::parseLong)
                    .collect(Collectors.toSet());
            if (!userIds.isEmpty()) {
                userMap = adminUserApi.getUserMap(userIds);
            }
        }
        // 2. æ‹¼æŽ¥ VO
        Map<Long, AdminUserRespDTO> finalUserMap = userMap;
        return BeanUtils.toBean(list, MesPdArchiveRespVO.class, vo -> {
            // productCode、productName、archiveCount å·²ä»Ž XML æŸ¥è¯¢æ˜ å°„到 GroupRespVO
            // æ›´æ–°äººæ˜µç§°
            if (vo.getUpdater() != null && vo.getUpdater().matches("\\d+")) {
                MapUtils.findAndThen(finalUserMap, Long.parseLong(vo.getUpdater()),
                        user -> vo.setUpdaterNickname(user.getNickname()));
            }
        });
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveDetailRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,145 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - MES å½’档台账详情(按产品) Response VO")
@Data
public class MesPdArchiveDetailRespVO {
    @Schema(description = "项目信息")
    private ProjectInfo project;
    @Schema(description = "归档记录列表")
    private List<ArchiveInfo> archives;
    @Schema(description = "设计资料列表")
    private List<DocumentInfo> documents;
    @Schema(description = "项目信息")
    @Data
    public static class ProjectInfo {
        @Schema(description = "项目ID", example = "1")
        private Long id;
        @Schema(description = "任务编码", example = "PD2024001")
        private String taskCode;
        @Schema(description = "任务名称", example = "新产品设计任务")
        private String taskName;
        @Schema(description = "产品编码", example = "P001")
        private String productCode;
        @Schema(description = "产品名称", example = "注塑产品A")
        private String productName;
        @Schema(description = "版本号", example = "V1.0")
        private String version;
        @Schema(description = "项目状态", example = "4")
        @DictFormat(DictTypeConstants.MES_PD_PROJECT_STATUS)
        private Integer status;
        @Schema(description = "主设计师", example = "1")
        private Long chiefDesigner;
        @Schema(description = "主设计师名称", example = "张三")
        private String chiefDesignerNickname;
        @Schema(description = "审核人", example = "2")
        private Long reviewer;
        @Schema(description = "审核人名称", example = "李四")
        private String reviewerNickname;
        @Schema(description = "计划完成时间")
        private LocalDateTime planFinishTime;
        @Schema(description = "备注", example = "测试备注")
        private String remark;
        @Schema(description = "创建时间")
        private LocalDateTime createTime;
    }
    @Schema(description = "归档记录信息")
    @Data
    public static class ArchiveInfo {
        @Schema(description = "归档ID", example = "1")
        private Long id;
        @Schema(description = "归档编号", example = "ARC2024001")
        private String archiveCode;
        @Schema(description = "归档人", example = "1")
        private Long archiver;
        @Schema(description = "归档人名称", example = "王五")
        private String archiverNickname;
        @Schema(description = "归档状态", example = "1")
        @DictFormat(DictTypeConstants.MES_PD_ARCHIVE_STATUS)
        private Integer archiveStatus;
        @Schema(description = "归档时间")
        private LocalDateTime archiveTime;
        @Schema(description = "备注", example = "测试备注")
        private String remark;
        @Schema(description = "创建时间")
        private LocalDateTime createTime;
    }
    @Schema(description = "设计资料信息")
    @Data
    public static class DocumentInfo {
        @Schema(description = "资料ID", example = "1")
        private Long id;
        @Schema(description = "资料类型", example = "1")
        @DictFormat(DictTypeConstants.MES_PD_DOCUMENT_TYPE)
        private String documentType;
        @Schema(description = "资料名称", example = "产品设计图")
        private String documentName;
        @Schema(description = "资料版本", example = "V1.0")
        private String documentVersion;
        @Schema(description = "文件名", example = "design.pdf")
        private String fileName;
        @Schema(description = "文件URL", example = "/files/design.pdf")
        private String fileUrl;
        @Schema(description = "上传人", example = "1")
        private Long uploadUserId;
        @Schema(description = "上传人名称", example = "赵六")
        private String uploadUserNickname;
        @Schema(description = "上传时间")
        private LocalDateTime uploadTime;
        @Schema(description = "备注", example = "测试备注")
        private String remark;
        @Schema(description = "创建时间")
        private LocalDateTime createTime;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveGroupRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
 * å½’档台账分组查询 Response VO
 * <p>
 * åœ¨å½’档台账字段基础上,额外包含关联项目的产品编码、产品名称。
 * ç”¨äºŽ XML mapper çš„分组查询结果映射,不破坏 MesPdArchiveDO ä¸Žæ•°æ®åº“的映射关系。
 *
 * @author èŠ‹é“æºç 
 */
@Schema(description = "管理后台 - MES å½’档台账分组查询 Response VO")
@Data
public class MesPdArchiveGroupRespVO {
    // ========== å½’档台账自身字段 ==========
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "归档编号", example = "ARC2024001")
    private String archiveCode;
    @Schema(description = "设计项目ID", example = "1")
    private Long projectId;
    @Schema(description = "归档人", example = "1")
    private Long archiver;
    @Schema(description = "归档状态", example = "0")
    private Integer archiveStatus;
    @Schema(description = "归档时间")
    private LocalDateTime archiveTime;
    @Schema(description = "备注", example = "测试备注")
    private String remark;
    @Schema(description = "创建者")
    private String creator;
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
    @Schema(description = "更新人")
    private String updater;
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;
    // ========== å…³è”项目字段(XML æŸ¥è¯¢é¢å¤–返回) ==========
    @Schema(description = "产品编码", example = "P001")
    private String productCode;
    @Schema(description = "产品名称", example = "注塑产品A")
    private String productName;
    @Schema(description = "归档记录数量", example = "3")
    private Integer archiveCount;
    private String taskCode;
    private String taskName;
    private String archiverNickname;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchivePageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - MES å½’档台账分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdArchivePageReqVO extends PageParam {
    @Schema(description = "归档编号", example = "ARC2024001")
    private String archiveCode;
    @Schema(description = "设计项目ID", example = "1")
    private Long projectId;
    @Schema(description = "归档状态", example = "0")
    private Integer archiveStatus;
    @Schema(description = "产品编码", example = "P001")
    private String productCode;
    @Schema(description = "产品名称", example = "注塑产品A")
    private String productName;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES å½’档台账 Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdArchiveRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "归档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ARC2024001")
    @ExcelProperty("归档编号")
    private String archiveCode;
    @Schema(description = "设计项目ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long projectId;
    @Schema(description = "任务编码", example = "PD2024001")
    @ExcelProperty("任务编码")
    private String taskCode;
    @Schema(description = "任务名称", example = "新产品设计任务")
    @ExcelProperty("任务名称")
    private String taskName;
    @Schema(description = "产品编码", example = "P001")
    @ExcelProperty("产品编码")
    private String productCode;
    @Schema(description = "产品名称", example = "注塑产品A")
    @ExcelProperty("产品名称")
    private String productName;
    @Schema(description = "归档记录数量", example = "3")
    private Integer archiveCount;
    @Schema(description = "归档人", example = "1")
    private Long archiver;
    @Schema(description = "归档人名称", example = "王五")
    @ExcelProperty("归档人")
    private String archiverNickname;
    @Schema(description = "归档状态", example = "0")
    @ExcelProperty(value = "归档状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PD_ARCHIVE_STATUS)
    private Integer archiveStatus;
    @Schema(description = "归档时间")
    @ExcelProperty("归档时间")
    private LocalDateTime archiveTime;
    @Schema(description = "备注", example = "测试备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
    @Schema(description = "更新人", example = "1")
    private String updater;
    @Schema(description = "更新人名称", example = "李四")
    @ExcelProperty("更新人")
    private String updaterNickname;
    @Schema(description = "更新时间")
    @ExcelProperty("更新时间")
    private LocalDateTime updateTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES å½’档台账新增/修改 Request VO")
@Data
public class MesPdArchiveSaveReqVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "归档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ARC2024001")
    @NotBlank(message = "归档编号不能为空")
    private String archiveCode;
    @Schema(description = "设计项目ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "设计项目ID不能为空")
    private Long projectId;
    @Schema(description = "归档人", example = "1")
    private Long archiver;
    @Schema(description = "归档状态", example = "0")
    private Integer archiveStatus;
    @Schema(description = "归档时间")
    private LocalDateTime archiveTime;
    @Schema(description = "备注", example = "测试备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/MesPdDocumentController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,121 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.document;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo.MesPdDocumentPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo.MesPdDocumentRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo.MesPdDocumentSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.document.MesPdDocumentDO;
import cn.iocoder.yudao.module.mes.service.pd.document.MesPdDocumentService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - MES è®¾è®¡èµ„æ–™")
@RestController
@RequestMapping("/mes/pd/document")
@Validated
public class MesPdDocumentController {
    @Resource
    private MesPdDocumentService documentService;
    @Resource
    private AdminUserApi adminUserApi;
    @PostMapping("/create")
    @Operation(summary = "创建设计资料")
    @PreAuthorize("@ss.hasPermission('mes:pd-document:create')")
    public CommonResult<Long> createDocument(@Valid @RequestBody MesPdDocumentSaveReqVO createReqVO) {
        return success(documentService.createDocument(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新设计资料")
    @PreAuthorize("@ss.hasPermission('mes:pd-document:update')")
    public CommonResult<Boolean> updateDocument(@Valid @RequestBody MesPdDocumentSaveReqVO updateReqVO) {
        documentService.updateDocument(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除设计资料")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-document:delete')")
    public CommonResult<Boolean> deleteDocument(@RequestParam("id") Long id) {
        documentService.deleteDocument(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得设计资料")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-document:query')")
    public CommonResult<MesPdDocumentRespVO> getDocument(@RequestParam("id") Long id) {
        MesPdDocumentDO document = documentService.getDocument(id);
        if (document == null) {
            return success(null);
        }
        return success(buildDocumentRespVOList(Collections.singletonList(document)).get(0));
    }
    @GetMapping("/page")
    @Operation(summary = "获得设计资料分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-document:query')")
    public CommonResult<PageResult<MesPdDocumentRespVO>> getDocumentPage(@Valid MesPdDocumentPageReqVO pageReqVO) {
        PageResult<MesPdDocumentDO> pageResult = documentService.getDocumentPage(pageReqVO);
        return success(new PageResult<>(buildDocumentRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出设计资料 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-document:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportDocumentExcel(@Valid MesPdDocumentPageReqVO pageReqVO,
                                    HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<MesPdDocumentDO> list = documentService.getDocumentPage(pageReqVO).getList();
        // å¯¼å‡º Excel
        ExcelUtils.write(response, "设计资料.xls", "数据", MesPdDocumentRespVO.class,
                buildDocumentRespVOList(list));
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<MesPdDocumentRespVO> buildDocumentRespVOList(List<MesPdDocumentDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 1.1 æ‰¹é‡èŽ·å–å…³è”æ•°æ®ï¼ˆä¸Šä¼ äººï¼‰
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(list, MesPdDocumentDO::getUploadUserId));
        // 2. æ‹¼æŽ¥ VO
        return BeanUtils.toBean(list, MesPdDocumentRespVO.class, vo -> {
            MapUtils.findAndThen(userMap, vo.getUploadUserId(),
                    user -> vo.setUploadUserNickname(user.getNickname()));
        });
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - MES è®¾è®¡èµ„料分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdDocumentPageReqVO extends PageParam {
    @Schema(description = "设计项目ID", example = "1")
    private Long projectId;
    @Schema(description = "资料名称", example = "产品设计图")
    private String documentName;
    @Schema(description = "资料类型", example = "drawing")
    private String documentType;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES è®¾è®¡èµ„æ–™ Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdDocumentRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "设计项目ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long projectId;
    @Schema(description = "资料类型", example = "drawing")
    @ExcelProperty(value = "资料类型", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PD_DOCUMENT_TYPE)
    private String documentType;
    @Schema(description = "资料名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品设计图")
    @ExcelProperty("资料名称")
    private String documentName;
    @Schema(description = "版本", example = "V1.0")
    @ExcelProperty("版本")
    private String documentVersion;
    @Schema(description = "文件名", example = "design_v1.pdf")
    @ExcelProperty("文件名")
    private String fileName;
    @Schema(description = "上传人", example = "1")
    private Long uploadUserId;
    @Schema(description = "上传人名称", example = "张三")
    @ExcelProperty("上传人")
    private String uploadUserNickname;
    @Schema(description = "上传时间")
    @ExcelProperty("上传时间")
    private LocalDateTime uploadTime;
    @Schema(description = "文件URL", example = "https://xxx/file.pdf")
    @ExcelProperty("文件URL")
    private String fileUrl;
    @Schema(description = "备注", example = "测试备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES è®¾è®¡èµ„料新增/修改 Request VO")
@Data
public class MesPdDocumentSaveReqVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "设计项目ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "设计项目ID不能为空")
    private Long projectId;
    @Schema(description = "资料类型", example = "drawing")
    private String documentType;
    @Schema(description = "资料名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品设计图")
    @NotBlank(message = "资料名称不能为空")
    private String documentName;
    @Schema(description = "版本", example = "V1.0")
    private String documentVersion;
    @Schema(description = "文件名", example = "design_v1.pdf")
    private String fileName;
    @Schema(description = "上传人", example = "1")
    private Long uploadUserId;
    @Schema(description = "上传时间")
    private LocalDateTime uploadTime;
    @Schema(description = "文件URL", example = "https://xxx/file.pdf")
    private String fileUrl;
    @Schema(description = "备注", example = "测试备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/documentaudit/MesPdDocumentAuditController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,121 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo.MesPdDocumentAuditPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo.MesPdDocumentAuditRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo.MesPdDocumentAuditSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.documentaudit.MesPdDocumentAuditDO;
import cn.iocoder.yudao.module.mes.service.pd.documentaudit.MesPdDocumentAuditService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - MES èµ„料审核")
@RestController
@RequestMapping("/mes/pd/document-audit")
@Validated
public class MesPdDocumentAuditController {
    @Resource
    private MesPdDocumentAuditService documentAuditService;
    @Resource
    private AdminUserApi adminUserApi;
    @PostMapping("/create")
    @Operation(summary = "创建资料审核")
    @PreAuthorize("@ss.hasPermission('mes:pd-document-audit:create')")
    public CommonResult<Long> createDocumentAudit(@Valid @RequestBody MesPdDocumentAuditSaveReqVO createReqVO) {
        return success(documentAuditService.createDocumentAudit(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新资料审核")
    @PreAuthorize("@ss.hasPermission('mes:pd-document-audit:update')")
    public CommonResult<Boolean> updateDocumentAudit(@Valid @RequestBody MesPdDocumentAuditSaveReqVO updateReqVO) {
        documentAuditService.updateDocumentAudit(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除资料审核")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-document-audit:delete')")
    public CommonResult<Boolean> deleteDocumentAudit(@RequestParam("id") Long id) {
        documentAuditService.deleteDocumentAudit(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得资料审核")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-document-audit:query')")
    public CommonResult<MesPdDocumentAuditRespVO> getDocumentAudit(@RequestParam("id") Long id) {
        MesPdDocumentAuditDO documentAudit = documentAuditService.getDocumentAudit(id);
        if (documentAudit == null) {
            return success(null);
        }
        return success(buildDocumentAuditRespVOList(Collections.singletonList(documentAudit)).get(0));
    }
    @GetMapping("/page")
    @Operation(summary = "获得资料审核分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-document-audit:query')")
    public CommonResult<PageResult<MesPdDocumentAuditRespVO>> getDocumentAuditPage(@Valid MesPdDocumentAuditPageReqVO pageReqVO) {
        PageResult<MesPdDocumentAuditDO> pageResult = documentAuditService.getDocumentAuditPage(pageReqVO);
        return success(new PageResult<>(buildDocumentAuditRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出资料审核 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-document-audit:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportDocumentAuditExcel(@Valid MesPdDocumentAuditPageReqVO pageReqVO,
                                         HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<MesPdDocumentAuditDO> list = documentAuditService.getDocumentAuditPage(pageReqVO).getList();
        // å¯¼å‡º Excel
        ExcelUtils.write(response, "资料审核.xls", "数据", MesPdDocumentAuditRespVO.class,
                buildDocumentAuditRespVOList(list));
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<MesPdDocumentAuditRespVO> buildDocumentAuditRespVOList(List<MesPdDocumentAuditDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 1.1 æ‰¹é‡èŽ·å–å…³è”æ•°æ®ï¼ˆå®¡æ ¸äººï¼‰
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(list, MesPdDocumentAuditDO::getReviewer));
        // 2. æ‹¼æŽ¥ VO
        return BeanUtils.toBean(list, MesPdDocumentAuditRespVO.class, vo -> {
            MapUtils.findAndThen(userMap, vo.getReviewer(),
                    user -> vo.setReviewerNickname(user.getNickname()));
        });
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/documentaudit/vo/MesPdDocumentAuditPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - MES èµ„料审核分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdDocumentAuditPageReqVO extends PageParam {
    @Schema(description = "资料ID", example = "1")
    private Long documentId;
    @Schema(description = "审核单号", example = "AUD2024001")
    private String auditNo;
    @Schema(description = "审核状态", example = "0")
    private Integer auditStatus;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/documentaudit/vo/MesPdDocumentAuditRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES èµ„料审核 Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdDocumentAuditRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "审核单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "AUD2024001")
    @ExcelProperty("审核单号")
    private String auditNo;
    @Schema(description = "资料ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long documentId;
    @Schema(description = "资料名称", example = "产品设计图")
    @ExcelProperty("资料名称")
    private String documentName;
    @Schema(description = "资料版本", example = "V1.0")
    @ExcelProperty("资料版本")
    private String documentVersion;
    @Schema(description = "审核人", example = "1")
    private Long reviewer;
    @Schema(description = "审核人名称", example = "李四")
    @ExcelProperty("审核人")
    private String reviewerNickname;
    @Schema(description = "审核状态", example = "0")
    @ExcelProperty(value = "审核状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PD_DOCUMENT_AUDIT_STATUS)
    private Integer auditStatus;
    @Schema(description = "审核时间")
    @ExcelProperty("审核时间")
    private LocalDateTime auditTime;
    @Schema(description = "备注", example = "测试备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/documentaudit/vo/MesPdDocumentAuditSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES èµ„料审核新增/修改 Request VO")
@Data
public class MesPdDocumentAuditSaveReqVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "审核单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "AUD2024001")
    private String auditNo;
    @Schema(description = "资料ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long documentId;
    @Schema(description = "资料名称", example = "产品设计图")
    private String documentName;
    @Schema(description = "资料版本", example = "V1.0")
    private String documentVersion;
    @Schema(description = "审核人", example = "1")
    private Long reviewer;
    @Schema(description = "审核状态", example = "0")
    private Integer auditStatus;
    @Schema(description = "审核时间")
    private LocalDateTime auditTime;
    @Schema(description = "备注", example = "测试备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/project/MesPdProjectController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,163 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.project;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo.MesPdProjectPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo.MesPdProjectRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo.MesPdProjectSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.project.MesPdProjectDO;
import cn.iocoder.yudao.module.mes.service.pd.project.MesPdProjectService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - MES è®¾è®¡é¡¹ç›®")
@RestController
@RequestMapping("/mes/pd/project")
@Validated
public class MesPdProjectController {
    @Resource
    private MesPdProjectService projectService;
    @Resource
    private AdminUserApi adminUserApi;
    @PostMapping("/create")
    @Operation(summary = "创建设计项目")
    @PreAuthorize("@ss.hasPermission('mes:pd-project:create')")
    public CommonResult<Long> createProject(@Valid @RequestBody MesPdProjectSaveReqVO createReqVO) {
        return success(projectService.createProject(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新设计项目")
    @PreAuthorize("@ss.hasPermission('mes:pd-project:update')")
    public CommonResult<Boolean> updateProject(@Valid @RequestBody MesPdProjectSaveReqVO updateReqVO) {
        projectService.updateProject(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除设计项目")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-project:delete')")
    public CommonResult<Boolean> deleteProject(@RequestParam("id") Long id) {
        projectService.deleteProject(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得设计项目")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pd-project:query')")
    public CommonResult<MesPdProjectRespVO> getProject(@RequestParam("id") Long id) {
        MesPdProjectDO project = projectService.getProject(id);
        if (project == null) {
            return success(null);
        }
        return success(buildProjectRespVOList(Collections.singletonList(project)).get(0));
    }
    @GetMapping("/page")
    @Operation(summary = "获得设计项目分页")
    @PreAuthorize("@ss.hasPermission('mes:pd-project:query')")
    public CommonResult<PageResult<MesPdProjectRespVO>> getProjectPage(@Valid MesPdProjectPageReqVO pageReqVO) {
        PageResult<MesPdProjectDO> pageResult = projectService.getProjectPage(pageReqVO);
        return success(new PageResult<>(buildProjectRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出设计项目 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pd-project:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportProjectExcel(@Valid MesPdProjectPageReqVO pageReqVO,
                                   HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<MesPdProjectDO> list = projectService.getProjectPage(pageReqVO).getList();
        // å¯¼å‡º Excel
        ExcelUtils.write(response, "设计项目.xls", "数据", MesPdProjectRespVO.class,
                buildProjectRespVOList(list));
    }
    @PutMapping("/publish")
    @Operation(summary = "发布设计项目(待发布→待上传)")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-project:update')")
    public CommonResult<Boolean> publishProject(@RequestParam("id") Long id) {
        projectService.publishProject(id);
        return success(true);
    }
    @PutMapping("/upload")
    @Operation(summary = "上传资料(待上传→待审核),同时自动创建资料审核记录")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-project:update')")
    public CommonResult<Boolean> uploadProject(@RequestParam("id") Long id) {
        projectService.uploadProject(id, getLoginUserId());
        return success(true);
    }
    @PutMapping("/audit")
    @Operation(summary = "审核设计项目(待审核→待归档)")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-project:update')")
    public CommonResult<Boolean> auditProject(@RequestParam("id") Long id) {
        projectService.auditProject(id);
        return success(true);
    }
    @PutMapping("/archive")
    @Operation(summary = "归档设计项目(待归档→已归档),同时自动创建归档台账")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pd-project:update')")
    public CommonResult<Boolean> archiveProject(@RequestParam("id") Long id) {
        projectService.archiveProject(id, getLoginUserId());
        return success(true);
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<MesPdProjectRespVO> buildProjectRespVOList(List<MesPdProjectDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 1.1 æ‰¹é‡èŽ·å–å…³è”æ•°æ®ï¼ˆä¸»è®¾è®¡å¸ˆ + å®¡æ ¸äººï¼‰
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(list,
                project -> Stream.of(project.getChiefDesigner(), project.getReviewer())));
        // 2. æ‹¼æŽ¥ VO
        return BeanUtils.toBean(list, MesPdProjectRespVO.class, vo -> {
            MapUtils.findAndThen(userMap, vo.getChiefDesigner(),
                    user -> vo.setChiefDesignerNickname(user.getNickname()));
            MapUtils.findAndThen(userMap, vo.getReviewer(),
                    user -> vo.setReviewerNickname(user.getNickname()));
        });
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/project/vo/MesPdProjectPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - MES è®¾è®¡é¡¹ç›®åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesPdProjectPageReqVO extends PageParam {
    @Schema(description = "任务编码", example = "PD2024001")
    private String taskCode;
    @Schema(description = "任务名称", example = "新产品设计任务")
    private String taskName;
    @Schema(description = "产品编码", example = "ITEM001")
    private String productCode;
    @Schema(description = "产品名称", example = "智能传感器")
    private String productName;
    @Schema(description = "状态", example = "0")
    private Integer status;
    @Schema(description = "计划完成时间")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
    private LocalDateTime[] planFinishTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/project/vo/MesPdProjectRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES è®¾è®¡é¡¹ç›® Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesPdProjectRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "任务编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "PD2024001")
    @ExcelProperty("任务编码")
    private String taskCode;
    @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "新产品设计任务")
    @ExcelProperty("任务名称")
    private String taskName;
    @Schema(description = "产品编码", example = "ITEM001")
    @ExcelProperty("产品编码")
    private String productCode;
    @Schema(description = "产品名称", example = "智能传感器")
    @ExcelProperty("产品名称")
    private String productName;
    @Schema(description = "版本号", example = "V1.0")
    @ExcelProperty("版本号")
    private String version;
    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    @ExcelProperty(value = "状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PD_PROJECT_STATUS)
    private Integer status;
    @Schema(description = "主设计师", example = "1")
    private Long chiefDesigner;
    @Schema(description = "主设计师名称", example = "张三")
    @ExcelProperty("主设计师")
    private String chiefDesignerNickname;
    @Schema(description = "审核人", example = "1")
    private Long reviewer;
    @Schema(description = "审核人名称", example = "李四")
    @ExcelProperty("审核人")
    private String reviewerNickname;
    @Schema(description = "计划完成时间")
    @ExcelProperty("计划完成时间")
    private LocalDateTime planFinishTime;
    @Schema(description = "备注", example = "测试备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/project/vo/MesPdProjectSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES è®¾è®¡é¡¹ç›®æ–°å¢ž/修改 Request VO")
@Data
public class MesPdProjectSaveReqVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "任务编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "PD2024001")
    @NotBlank(message = "任务编码不能为空")
    private String taskCode;
    @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "新产品设计任务")
    @NotBlank(message = "任务名称不能为空")
    private String taskName;
    @Schema(description = "产品编码", example = "ITEM001")
    private String productCode;
    @Schema(description = "产品名称", example = "智能传感器")
    private String productName;
    @Schema(description = "版本号", example = "V1.0")
    private String version;
    @Schema(description = "主设计师", example = "1")
    private Long chiefDesigner;
    @Schema(description = "审核人", example = "1")
    private Long reviewer;
    @Schema(description = "计划完成时间")
    private LocalDateTime planFinishTime;
    @Schema(description = "备注", example = "测试备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/archive/MesPdArchiveDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.archive;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.project.MesPdProjectDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * MES å½’档台账 DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName("mes_pd_archive")
@KeySequence("mes_pd_archive_seq") // ç”¨äºŽ Oracle、PostgreSQL、Kingbase、DB2、H2 æ•°æ®åº“的主键自增。如果是 MySQL ç­‰æ•°æ®åº“,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdArchiveDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å½’档编号
     */
    private String archiveCode;
    /**
     * è®¾è®¡é¡¹ç›®ID
     *
     * å…³è” {@link MesPdProjectDO#getId()}
     */
    private Long projectId;
    /**
     * å½’档人
     *
     * å…³è” AdminUserDO#getId()
     */
    private Long archiver;
    /**
     * å½’档状态
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PD_ARCHIVE_STATUS}
     */
    private Integer archiveStatus;
    /**
     * å½’档时间
     */
    private LocalDateTime archiveTime;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/document/MesPdDocumentDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.document;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.project.MesPdProjectDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * MES è®¾è®¡èµ„æ–™ DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName("mes_pd_document")
@KeySequence("mes_pd_document_seq") // ç”¨äºŽ Oracle、PostgreSQL、Kingbase、DB2、H2 æ•°æ®åº“的主键自增。如果是 MySQL ç­‰æ•°æ®åº“,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdDocumentDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * è®¾è®¡é¡¹ç›®ID
     *
     * å…³è” {@link MesPdProjectDO#getId()}
     */
    private Long projectId;
    /**
     * èµ„料类型
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PD_DOCUMENT_TYPE}
     */
    private String documentType;
    /**
     * èµ„料名称
     */
    private String documentName;
    /**
     * ç‰ˆæœ¬
     */
    private String documentVersion;
    /**
     * æ–‡ä»¶å
     */
    private String fileName;
    /**
     * ä¸Šä¼ äºº
     *
     * å…³è” AdminUserDO#getId()
     */
    private Long uploadUserId;
    /**
     * ä¸Šä¼ æ—¶é—´
     */
    private LocalDateTime uploadTime;
    /**
     * æ–‡ä»¶URL
     */
    private String fileUrl;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/documentaudit/MesPdDocumentAuditDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.documentaudit;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.document.MesPdDocumentDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * MES èµ„料审核 DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName("mes_pd_document_audit")
@KeySequence("mes_pd_document_audit_seq") // ç”¨äºŽ Oracle、PostgreSQL、Kingbase、DB2、H2 æ•°æ®åº“的主键自增。如果是 MySQL ç­‰æ•°æ®åº“,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdDocumentAuditDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å®¡æ ¸å•号
     */
    private String auditNo;
    /**
     * èµ„æ–™ID
     *
     * å…³è” {@link MesPdDocumentDO#getId()}
     */
    private Long documentId;
    /**
     * èµ„料名称(冗余)
     */
    private String documentName;
    /**
     * èµ„料版本(冗余)
     */
    private String documentVersion;
    /**
     * å®¡æ ¸äºº
     *
     * å…³è” AdminUserDO#getId()
     */
    private Long reviewer;
    /**
     * å®¡æ ¸çŠ¶æ€
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PD_DOCUMENT_AUDIT_STATUS}
     */
    private Integer auditStatus;
    /**
     * å®¡æ ¸æ—¶é—´
     */
    private LocalDateTime auditTime;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/project/MesPdProjectDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pd.project;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.iocoder.yudao.module.mes.enums.pd.MesPdProjectStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * MES è®¾è®¡é¡¹ç›® DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName("mes_pd_project")
@KeySequence("mes_pd_project_seq") // ç”¨äºŽ Oracle、PostgreSQL、Kingbase、DB2、H2 æ•°æ®åº“的主键自增。如果是 MySQL ç­‰æ•°æ®åº“,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesPdProjectDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ä»»åŠ¡ç¼–ç 
     */
    private String taskCode;
    /**
     * ä»»åŠ¡åç§°
     */
    private String taskName;
    /**
     * äº§å“ç¼–码
     */
    private String productCode;
    /**
     * äº§å“åç§°
     */
    private String productName;
    /**
     * ç‰ˆæœ¬å·
     */
    private String version;
    /**
     * çŠ¶æ€
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PD_PROJECT_STATUS}
     * æžšä¸¾ {@link MesPdProjectStatusEnum}
     */
    private Integer status;
    /**
     * ä¸»è®¾è®¡å¸ˆ
     *
     * å…³è” AdminUserDO#getId()
     */
    private Long chiefDesigner;
    /**
     * å®¡æ ¸äºº
     *
     * å…³è” AdminUserDO#getId()
     */
    private Long reviewer;
    /**
     * è®¡åˆ’完成时间
     */
    private LocalDateTime planFinishTime;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/archive/MesPdArchiveMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.archive;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchiveGroupRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchivePageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.archive.MesPdArchiveDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface MesPdArchiveMapper extends BaseMapperX<MesPdArchiveDO> {
    default PageResult<MesPdArchiveDO> selectPage(MesPdArchivePageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdArchiveDO>()
                .likeIfPresent(MesPdArchiveDO::getArchiveCode, reqVO.getArchiveCode())
                .eqIfPresent(MesPdArchiveDO::getProjectId, reqVO.getProjectId())
                .eqIfPresent(MesPdArchiveDO::getArchiveStatus, reqVO.getArchiveStatus())
                .orderByDesc(MesPdArchiveDO::getId));
    }
    default MesPdArchiveDO selectByArchiveCode(String archiveCode) {
        return selectOne(MesPdArchiveDO::getArchiveCode, archiveCode);
    }
    default List<MesPdArchiveDO> selectListByProjectId(Long projectId) {
        return selectList(MesPdArchiveDO::getProjectId, projectId);
    }
    /**
     * æŸ¥è¯¢æŸé¡¹ç›®ä¸‹å½’档记录数量
     */
    default Long selectCountByProjectId(Long projectId) {
        return selectCount(MesPdArchiveDO::getProjectId, projectId);
    }
    /**
     * XML mapper å®žçŽ°çš„åˆ†ç»„åˆ†é¡µæŸ¥è¯¢
     * è¿”回 MesPdArchiveGroupRespVO(包含归档字段 + å…³è”项目的产品编码/名称)
     */
    IPage<MesPdArchiveGroupRespVO> selectPageGroupByProjectId(Page<MesPdArchiveGroupRespVO> page,
                                                              @Param("req") MesPdArchivePageReqVO reqVO);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/document/MesPdDocumentMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.document;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo.MesPdDocumentPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.document.MesPdDocumentDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface MesPdDocumentMapper extends BaseMapperX<MesPdDocumentDO> {
    default PageResult<MesPdDocumentDO> selectPage(MesPdDocumentPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdDocumentDO>()
                .eqIfPresent(MesPdDocumentDO::getProjectId, reqVO.getProjectId())
                .likeIfPresent(MesPdDocumentDO::getDocumentName, reqVO.getDocumentName())
                .eqIfPresent(MesPdDocumentDO::getDocumentType, reqVO.getDocumentType())
                .orderByDesc(MesPdDocumentDO::getId));
    }
    default List<MesPdDocumentDO> selectListByProjectId(Long projectId) {
        return selectList(MesPdDocumentDO::getProjectId, projectId);
    }
    default int deleteByProjectId(Long projectId) {
        return delete(MesPdDocumentDO::getProjectId, projectId);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/documentaudit/MesPdDocumentAuditMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.documentaudit;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo.MesPdDocumentAuditPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.documentaudit.MesPdDocumentAuditDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface MesPdDocumentAuditMapper extends BaseMapperX<MesPdDocumentAuditDO> {
    default PageResult<MesPdDocumentAuditDO> selectPage(MesPdDocumentAuditPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdDocumentAuditDO>()
                .eqIfPresent(MesPdDocumentAuditDO::getDocumentId, reqVO.getDocumentId())
                .likeIfPresent(MesPdDocumentAuditDO::getAuditNo, reqVO.getAuditNo())
                .eqIfPresent(MesPdDocumentAuditDO::getAuditStatus, reqVO.getAuditStatus())
                .orderByDesc(MesPdDocumentAuditDO::getId));
    }
    default List<MesPdDocumentAuditDO> selectListByDocumentId(Long documentId) {
        return selectList(MesPdDocumentAuditDO::getDocumentId, documentId);
    }
    default int deleteByDocumentId(Long documentId) {
        return delete(MesPdDocumentAuditDO::getDocumentId, documentId);
    }
    /**
     * æŸ¥è¯¢æŒ‡å®šèµ„æ–™ID列表中是否存在待审核的记录
     *
     * @param documentIds èµ„æ–™ID列表
     * @return æ˜¯å¦å­˜åœ¨å¾…审核记录
     */
    default boolean existsPendingAuditByDocumentIds(Collection<Long> documentIds) {
        if (documentIds == null || documentIds.isEmpty()) {
            return false;
        }
        return selectCount(new LambdaQueryWrapperX<MesPdDocumentAuditDO>()
                .in(MesPdDocumentAuditDO::getDocumentId, documentIds)
                .eq(MesPdDocumentAuditDO::getAuditStatus, 0)) > 0;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pd/project/MesPdProjectMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pd.project;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo.MesPdProjectPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.project.MesPdProjectDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface MesPdProjectMapper extends BaseMapperX<MesPdProjectDO> {
    default PageResult<MesPdProjectDO> selectPage(MesPdProjectPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesPdProjectDO>()
                .likeIfPresent(MesPdProjectDO::getTaskCode, reqVO.getTaskCode())
                .likeIfPresent(MesPdProjectDO::getTaskName, reqVO.getTaskName())
                .likeIfPresent(MesPdProjectDO::getProductCode, reqVO.getProductCode())
                .likeIfPresent(MesPdProjectDO::getProductName, reqVO.getProductName())
                .eqIfPresent(MesPdProjectDO::getStatus, reqVO.getStatus())
                .betweenIfPresent(MesPdProjectDO::getPlanFinishTime, reqVO.getPlanFinishTime())
                .orderByDesc(MesPdProjectDO::getId));
    }
    default MesPdProjectDO selectByTaskCode(String taskCode) {
        return selectOne(MesPdProjectDO::getTaskCode, taskCode);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java
@@ -100,4 +100,12 @@
    // ========== å·¥å…·ç®¡ç† (TM) ==========
    String MES_TM_TOOL_STATUS = "mes_tm_tool_status"; // MES å·¥å…·çŠ¶æ€
    String MES_TM_MAINTEN_TYPE = "mes_tm_mainten_type"; // MES ä¿å…»ç»´æŠ¤ç±»åž‹
    // ========== äº§å“è®¾è®¡ (PD) ==========
    String MES_PD_PROJECT_STATUS = "mes_pd_project_status";               // è®¾è®¡é¡¹ç›®çŠ¶æ€
    String MES_PD_DOCUMENT_TYPE = "mes_pd_document_type";                 // èµ„料类型
    String MES_PD_DOCUMENT_AUDIT_STATUS = "mes_pd_document_audit_status"; // èµ„料审核状态
    String MES_PD_ARCHIVE_STATUS = "mes_pd_archive_status";               // å½’档状态
    String MES_PD_PROCESS_PARAM_TYPE = "mes_pd_process_param_type";                   // å·¥è‰ºå‚数类型
    String MES_PD_PROCESS_PARAM_TEMPLATE_TYPE = "mes_pd_process_param_template_type"; // å·¥è‰ºå‚数模板类型
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -708,4 +708,32 @@
    ErrorCode WM_PACKAGE_HAS_CHILDREN = new ErrorCode(1_040_740_008, "存在子箱,不允许删除");
    ErrorCode WM_PACKAGE_LINE_NOT_EXISTS = new ErrorCode(1_040_740_100, "装箱明细不存在");
    // ========== MES äº§å“è®¾è®¡-设计项目(1-040-800-000) ==========
    ErrorCode PD_PROJECT_NOT_EXISTS = new ErrorCode(1_040_800_000, "设计项目不存在");
    ErrorCode PD_PROJECT_NOT_INITIATE = new ErrorCode(1_040_800_001, "设计项目不是待发布状态,不允许修改或删除");
    ErrorCode PD_PROJECT_CODE_DUPLICATE = new ErrorCode(1_040_800_002, "设计项目任务编码已存在");
    ErrorCode PD_PROJECT_NOT_INITIATE_FOR_PUBLISH = new ErrorCode(1_040_800_003, "只有待发布状态的设计项目才能发布");
    ErrorCode PD_PROJECT_NOT_PUBLISH_FOR_UPLOAD = new ErrorCode(1_040_800_004, "只有待上传状态的设计项目才能上传资料");
    ErrorCode PD_PROJECT_NOT_UPLOAD_FOR_AUDIT = new ErrorCode(1_040_800_005, "只有待审核状态的设计项目才能审核");
    ErrorCode PD_PROJECT_NOT_AUDIT_FOR_ARCHIVE = new ErrorCode(1_040_800_006, "只有待归档状态的设计项目才能归档");
    ErrorCode PD_PROJECT_AUDIT_NOT_FINISHED = new ErrorCode(1_040_800_007, "该项目下存在未审核的资料,请先完成所有资料审核");
    // ========== MES äº§å“è®¾è®¡-资料子表(1-040-801-000) ==========
    ErrorCode PD_DOCUMENT_NOT_EXISTS = new ErrorCode(1_040_801_000, "设计资料不存在");
    // ========== MES äº§å“è®¾è®¡-资料审核(1-040-802-000) ==========
    ErrorCode PD_DOCUMENT_AUDIT_NOT_EXISTS = new ErrorCode(1_040_802_000, "资料审核记录不存在");
    // ========== MES äº§å“è®¾è®¡-归档台账(1-040-803-000) ==========
    ErrorCode PD_ARCHIVE_NOT_EXISTS = new ErrorCode(1_040_803_000, "归档台账不存在");
    ErrorCode PD_ARCHIVE_CODE_DUPLICATE = new ErrorCode(1_040_803_001, "归档编号已存在");
    // ========== MES äº§å“è®¾è®¡-工艺参数(1-040-804-000) ==========
    ErrorCode PD_PROCESS_PARAM_NOT_EXISTS = new ErrorCode(1_040_804_000, "工艺参数不存在");
    ErrorCode PD_PROCESS_PARAM_CODE_DUPLICATE = new ErrorCode(1_040_804_001, "工艺参数编码已存在");
    // ========== MES äº§å“è®¾è®¡-工艺参数模板(1-040-805-000) ==========
    ErrorCode PD_PROCESS_PARAM_TEMPLATE_NOT_EXISTS = new ErrorCode(1_040_805_000, "工艺参数模板不存在");
    ErrorCode PD_PROCESS_PARAM_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_040_805_001, "工艺参数模板编码已存在");
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java
@@ -76,6 +76,15 @@
    public static final int MD_USER = 604; // äººå‘˜ï¼šç³»ç»Ÿç”¨æˆ·
    public static final int MD_CLIENT = 605; // å®¢æˆ·ï¼šMesMdClientDO
    // ========== PD äº§å“è®¾è®¡æ¨¡å— [700, 800) ==========
    public static final int PD_PROJECT = 700;          // è®¾è®¡é¡¹ç›®ï¼šMesPdProjectDO
    public static final int PD_DOCUMENT = 701;         // èµ„料子表:MesPdDocumentDO
    public static final int PD_DOCUMENT_AUDIT = 702;   // èµ„料审核:MesPdDocumentAuditDO
    public static final int PD_ARCHIVE = 703;          // å½’档台账:MesPdArchiveDO
    public static final int PD_PROCESS_PARAM = 704;    // å·¥è‰ºå‚数:MesPdProcessParamDO
    public static final int PD_PROCESS_PARAM_TEMPLATE = 705; // å·¥è‰ºå‚数模板:MesPdProcessParamTemplateDO
    // ========== ç‰¹æ®Šç±»åž‹ï¼ˆéžä¸šåŠ¡å®žä½“ï¼‰ [900, 1000) ==========
    public static final int QUALITY_STATUS = 900; // è´¨é‡çŠ¶æ€ï¼šéžå®žä½“ï¼Œç”¨äºŽç›˜ç‚¹ç­‰åœºæ™¯çš„çŠ¶æ€ç­›é€‰
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesPdStatusConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.mes.enums;
/**
 * MES äº§å“è®¾è®¡çŠ¶æ€å¸¸é‡
 *
 * é›†ä¸­ç®¡ç†äº§å“è®¾è®¡æ¨¡å—各单据状态的编号,作为状态值的中央注册中心。
 * å„枚举类引用此处常量,避免硬编码数字。
 *
 * @author èŠ‹é“æºç 
 */
public final class MesPdStatusConstants {
    private MesPdStatusConstants() {}
    /**
     * å¾…发布
     */
    public static final int INITIATE = 0;
    /**
     * å¾…上传
     */
    public static final int PUBLISH = 1;
    /**
     * å¾…审核
     */
    public static final int UPLOAD = 2;
    /**
     * å¾…å½’æ¡£
     */
    public static final int AUDIT = 3;
    /**
     * å·²å½’æ¡£
     */
    public static final int ARCHIVE = 4;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/md/autocode/MesMdAutoCodeRuleCodeEnum.java
@@ -52,7 +52,11 @@
    PRO_WORK_ORDER_CODE("PRO_WORK_ORDER_CODE", "生产工单编码"),
    QC_DEFECT_CODE("QC_DEFECT_CODE", "缺陷类型编码"),
    QC_INDICATOR_RESULT_CODE("QC_INDICATOR_RESULT_CODE", "样品检验结果编码"),
    WM_OUTSOURCE_RECEIPT_CODE("WM_OUTSOURCE_RECEIPT_CODE", "外协入库单编码");
    WM_OUTSOURCE_RECEIPT_CODE("WM_OUTSOURCE_RECEIPT_CODE", "外协入库单编码"),
    PD_DOCUMENT_AUDIT_CODE("PD_DOCUMENT_AUDIT_CODE", "资料审核单编码"),
    PD_ARCHIVE_CODE("PD_ARCHIVE_CODE", "归档台账编码"),
    PD_PROCESS_PARAM_CODE("PD_PROCESS_PARAM_CODE", "工艺参数编码"),
    PD_PROCESS_PARAM_TEMPLATE_CODE("PD_PROCESS_PARAM_TEMPLATE_CODE", "工艺参数模板编码");
    /**
     * è§„则代码
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pd/MesPdProjectStatusEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package cn.iocoder.yudao.module.mes.enums.pd;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import cn.iocoder.yudao.module.mes.enums.MesPdStatusConstants;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
 * MES è®¾è®¡é¡¹ç›®çŠ¶æ€æžšä¸¾
 *
 * çŠ¶æ€æµè½¬ï¼šå¾…å‘å¸ƒ â†’ å¾…上传 â†’ å¾…审核 â†’ å¾…å½’æ¡£ â†’ å·²å½’æ¡£
 *
 * @author èŠ‹é“æºç 
 */
@Getter
@AllArgsConstructor
public enum MesPdProjectStatusEnum implements ArrayValuable<Integer> {
    /**
     * å¾…发布
     *
     * å¯¹åº” MesPdProjectService#createProject æ–¹æ³•
     */
    INITIATE(MesPdStatusConstants.INITIATE, "待发布"),
    /**
     * å¾…上传
     *
     * å¯¹åº” MesPdProjectService#publishProject æ–¹æ³•
     */
    PUBLISH(MesPdStatusConstants.PUBLISH, "待上传"),
    /**
     * å¾…审核
     *
     * å¯¹åº” MesPdProjectService#uploadProject æ–¹æ³•
     */
    UPLOAD(MesPdStatusConstants.UPLOAD, "待审核"),
    /**
     * å¾…å½’æ¡£
     *
     * å¯¹åº” MesPdProjectService#auditProject æ–¹æ³•
     */
    AUDIT(MesPdStatusConstants.AUDIT, "待归档"),
    /**
     * å·²å½’æ¡£
     *
     * å¯¹åº” MesPdProjectService#archiveProject æ–¹æ³•
     */
    ARCHIVE(MesPdStatusConstants.ARCHIVE, "已归档");
    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesPdProjectStatusEnum::getStatus).toArray(Integer[]::new);
    /**
     * çŠ¶æ€å€¼
     */
    private final Integer status;
    /**
     * çŠ¶æ€å
     */
    private final String name;
    @Override
    public Integer[] array() {
        return ARRAYS;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/archive/MesPdArchiveService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.mes.service.pd.archive;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchiveDetailRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchiveGroupRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchivePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchiveSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.archive.MesPdArchiveDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.validation.Valid;
import java.util.List;
/**
 * MES å½’档台账 Service æŽ¥å£
 *
 * @author èŠ‹é“æºç 
 */
public interface MesPdArchiveService {
    /**
     * åˆ›å»ºå½’档台账
     */
    Long createArchive(@Valid MesPdArchiveSaveReqVO createReqVO);
    /**
     * æ›´æ–°å½’档台账
     */
    void updateArchive(@Valid MesPdArchiveSaveReqVO updateReqVO);
    /**
     * åˆ é™¤å½’档台账
     */
    void deleteArchive(Long id);
    /**
     * èŽ·å¾—å½’æ¡£å°è´¦
     */
    MesPdArchiveDO getArchive(Long id);
    /**
     * èŽ·å¾—å½’æ¡£å°è´¦åˆ†é¡µ
     */
    PageResult<MesPdArchiveDO> getArchivePage(MesPdArchivePageReqVO pageReqVO);
    /**
     * èŽ·å¾—å½’æ¡£å°è´¦åˆ†é¡µï¼ˆæŒ‰é¡¹ç›®/产品分组,每组取最新一条)
     * è¿”回 MesPdArchiveGroupRespVO(包含归档字段 + å…³è”项目的产品编码/名称)
     */
    IPage<MesPdArchiveGroupRespVO> getArchivePageGroupByProject(MesPdArchivePageReqVO pageReqVO,
                                                                 Page<MesPdArchiveGroupRespVO> page);
    /**
     * èŽ·å¾—å½’æ¡£å°è´¦è¯¦æƒ…ï¼ˆæŒ‰äº§å“ï¼‰ï¼ŒåŒ…å«é¡¹ç›®ä¿¡æ¯ã€å½’æ¡£è®°å½•åˆ—è¡¨ã€è®¾è®¡èµ„æ–™åˆ—è¡¨
     */
    List<MesPdArchiveDetailRespVO> getArchiveDetailByProductCode(String productCode);
    /**
     * èŽ·å¾—æŸé¡¹ç›®ä¸‹å½’æ¡£è®°å½•æ•°é‡
     */
    Long getArchiveCountByProjectId(Long projectId);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/archive/MesPdArchiveServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,187 @@
package cn.iocoder.yudao.module.mes.service.pd.archive;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchiveDetailRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchiveGroupRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchivePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchiveSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.archive.MesPdArchiveDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.document.MesPdDocumentDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.project.MesPdProjectDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.archive.MesPdArchiveMapper;
import cn.iocoder.yudao.module.mes.service.pd.document.MesPdDocumentService;
import cn.iocoder.yudao.module.mes.service.pd.project.MesPdProjectService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
/**
 * MES å½’档台账 Service å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Service
@Validated
public class MesPdArchiveServiceImpl implements MesPdArchiveService {
    @Resource
    private MesPdArchiveMapper archiveMapper;
    @Resource
    @Lazy
    private MesPdProjectService projectService;
    @Resource
    @Lazy
    private MesPdDocumentService documentService;
    @Resource
    @Lazy
    private AdminUserApi adminUserApi;
    @Override
    public Long createArchive(MesPdArchiveSaveReqVO createReqVO) {
        // æ ¡éªŒå½’档编号唯一
        validateArchiveCodeUnique(null, createReqVO.getArchiveCode());
        // æ ¡éªŒè®¾è®¡é¡¹ç›®å­˜åœ¨
        projectService.validateProjectExists(createReqVO.getProjectId());
        // æ’å…¥
        MesPdArchiveDO archive = BeanUtils.toBean(createReqVO, MesPdArchiveDO.class);
        archiveMapper.insert(archive);
        return archive.getId();
    }
    @Override
    public void updateArchive(MesPdArchiveSaveReqVO updateReqVO) {
        // æ ¡éªŒå­˜åœ¨
        validateArchiveExists(updateReqVO.getId());
        // æ ¡éªŒå½’档编号唯一
        validateArchiveCodeUnique(updateReqVO.getId(), updateReqVO.getArchiveCode());
        // æ›´æ–°
        MesPdArchiveDO updateObj = BeanUtils.toBean(updateReqVO, MesPdArchiveDO.class);
        archiveMapper.updateById(updateObj);
    }
    @Override
    public void deleteArchive(Long id) {
        // æ ¡éªŒå­˜åœ¨
        validateArchiveExists(id);
        // åˆ é™¤
        archiveMapper.deleteById(id);
    }
    @Override
    public MesPdArchiveDO getArchive(Long id) {
        return archiveMapper.selectById(id);
    }
    @Override
    public PageResult<MesPdArchiveDO> getArchivePage(MesPdArchivePageReqVO pageReqVO) {
        return archiveMapper.selectPage(pageReqVO);
    }
    @Override
    public IPage<MesPdArchiveGroupRespVO> getArchivePageGroupByProject(MesPdArchivePageReqVO pageReqVO, Page<MesPdArchiveGroupRespVO> page) {
        return archiveMapper.selectPageGroupByProjectId(page,pageReqVO);
    }
    @Override
    public List<MesPdArchiveDetailRespVO> getArchiveDetailByProductCode(String productCode) {
        // 1. æŸ¥é¡¹ç›®ä¿¡æ¯
        List<MesPdProjectDO> projects = projectService.getProjectByProductCode(productCode);
        List<MesPdArchiveDetailRespVO> mesPdArchiveDetailRespVOS = new ArrayList<>();
        projects.forEach(project -> {
            if (project == null) {
                throw exception(PD_PROJECT_NOT_EXISTS);
            }
            // 2. æŸ¥å½’档列表
            List<MesPdArchiveDO> archives = archiveMapper.selectListByProjectId(project.getId());
            // 3. æŸ¥èµ„料列表
            List<MesPdDocumentDO> documents = documentService.getDocumentListByProjectId(project.getId());
            // 4. ç»„装 VO
            MesPdArchiveDetailRespVO respVO = new MesPdArchiveDetailRespVO();
            // 4.1 é¡¹ç›®ä¿¡æ¯
            MesPdArchiveDetailRespVO.ProjectInfo projectInfo = BeanUtils.toBean(project, MesPdArchiveDetailRespVO.ProjectInfo.class);
            // æ‰¹é‡èŽ·å–ç”¨æˆ·ä¿¡æ¯
            Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                    cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet(
                            java.util.stream.Stream.of(project.getChiefDesigner(), project.getReviewer())
                                    .filter(java.util.Objects::nonNull)
                                    .collect(java.util.stream.Collectors.toSet()),
                            id -> id));
            MapUtils.findAndThen(userMap, project.getChiefDesigner(),
                    user -> projectInfo.setChiefDesignerNickname(user.getNickname()));
            MapUtils.findAndThen(userMap, project.getReviewer(),
                    user -> projectInfo.setReviewerNickname(user.getNickname()));
            respVO.setProject(projectInfo);
            // 4.2 å½’档记录列表
            if (!archives.isEmpty()) {
                Map<Long, AdminUserRespDTO> archiverUserMap = adminUserApi.getUserMap(
                        cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet(archives, MesPdArchiveDO::getArchiver));
                List<MesPdArchiveDetailRespVO.ArchiveInfo> archiveInfos = BeanUtils.toBean(archives, MesPdArchiveDetailRespVO.ArchiveInfo.class,
                        info -> MapUtils.findAndThen(archiverUserMap, info.getArchiver(),
                                user -> info.setArchiverNickname(user.getNickname())));
                respVO.setArchives(archiveInfos);
            }
            // 4.3 è®¾è®¡èµ„料列表
            if (!documents.isEmpty()) {
                Map<Long, AdminUserRespDTO> uploadUserMap = adminUserApi.getUserMap(
                        cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet(documents, MesPdDocumentDO::getUploadUserId));
                List<MesPdArchiveDetailRespVO.DocumentInfo> documentInfos = BeanUtils.toBean(documents, MesPdArchiveDetailRespVO.DocumentInfo.class,
                        info -> MapUtils.findAndThen(uploadUserMap, info.getUploadUserId(),
                                user -> info.setUploadUserNickname(user.getNickname())));
                respVO.setDocuments(documentInfos);
            }
            mesPdArchiveDetailRespVOS.add(respVO);
        });
        return mesPdArchiveDetailRespVOS;
    }
    @Override
    public Long getArchiveCountByProjectId(Long projectId) {
        return archiveMapper.selectCountByProjectId(projectId);
    }
    private MesPdArchiveDO validateArchiveExists(Long id) {
        MesPdArchiveDO archive = archiveMapper.selectById(id);
        if (archive == null) {
            throw exception(PD_ARCHIVE_NOT_EXISTS);
        }
        return archive;
    }
    private void validateArchiveCodeUnique(Long id, String archiveCode) {
        MesPdArchiveDO existing = archiveMapper.selectByArchiveCode(archiveCode);
        if (existing == null) {
            return;
        }
        // å¦‚æžœ id ä¸ºç©ºï¼Œè¯´æ˜Žæ˜¯æ–°å¢žï¼Œç¼–码重复
        if (id == null) {
            throw exception(PD_ARCHIVE_CODE_DUPLICATE);
        }
        // å¦‚æžœ id ä¸ä¸ºç©ºï¼Œè¯´æ˜Žæ˜¯ä¿®æ”¹ï¼Œç¼–码重复且不是同一条记录
        if (!existing.getId().equals(id)) {
            throw exception(PD_ARCHIVE_CODE_DUPLICATE);
        }
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/document/MesPdDocumentService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.mes.service.pd.document;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo.MesPdDocumentPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo.MesPdDocumentSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.document.MesPdDocumentDO;
import jakarta.validation.Valid;
import java.util.List;
/**
 * MES è®¾è®¡èµ„æ–™ Service æŽ¥å£
 *
 * @author èŠ‹é“æºç 
 */
public interface MesPdDocumentService {
    /**
     * åˆ›å»ºè®¾è®¡èµ„æ–™
     *
     * @param createReqVO åˆ›å»ºä¿¡æ¯
     * @return ç¼–号
     */
    Long createDocument(@Valid MesPdDocumentSaveReqVO createReqVO);
    /**
     * æ›´æ–°è®¾è®¡èµ„æ–™
     *
     * @param updateReqVO æ›´æ–°ä¿¡æ¯
     */
    void updateDocument(@Valid MesPdDocumentSaveReqVO updateReqVO);
    /**
     * åˆ é™¤è®¾è®¡èµ„æ–™
     *
     * @param id ç¼–号
     */
    void deleteDocument(Long id);
    /**
     * èŽ·å¾—è®¾è®¡èµ„æ–™
     *
     * @param id ç¼–号
     * @return è®¾è®¡èµ„æ–™
     */
    MesPdDocumentDO getDocument(Long id);
    /**
     * èŽ·å¾—è®¾è®¡èµ„æ–™åˆ†é¡µ
     *
     * @param pageReqVO åˆ†é¡µæŸ¥è¯¢
     * @return è®¾è®¡èµ„料分页
     */
    PageResult<MesPdDocumentDO> getDocumentPage(MesPdDocumentPageReqVO pageReqVO);
    /**
     * èŽ·å¾—æŒ‡å®šè®¾è®¡é¡¹ç›®ä¸‹çš„èµ„æ–™åˆ—è¡¨
     *
     * @param projectId è®¾è®¡é¡¹ç›®ID
     * @return èµ„料列表
     */
    List<MesPdDocumentDO> getDocumentListByProjectId(Long projectId);
    /**
     * åˆ é™¤æŒ‡å®šè®¾è®¡é¡¹ç›®ä¸‹çš„æ‰€æœ‰èµ„æ–™
     *
     * @param projectId è®¾è®¡é¡¹ç›®ID
     */
    void deleteByProjectId(Long projectId);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/document/MesPdDocumentServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
package cn.iocoder.yudao.module.mes.service.pd.document;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo.MesPdDocumentPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo.MesPdDocumentSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.document.MesPdDocumentDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.document.MesPdDocumentMapper;
import cn.iocoder.yudao.module.mes.service.pd.project.MesPdProjectService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_DOCUMENT_NOT_EXISTS;
/**
 * MES è®¾è®¡èµ„æ–™ Service å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Service
@Validated
public class MesPdDocumentServiceImpl implements MesPdDocumentService {
    @Resource
    private MesPdDocumentMapper documentMapper;
    @Resource
    @Lazy
    private MesPdProjectService projectService;
    @Override
    public Long createDocument(MesPdDocumentSaveReqVO createReqVO) {
        // æ ¡éªŒè®¾è®¡é¡¹ç›®å­˜åœ¨
        projectService.validateProjectExists(createReqVO.getProjectId());
        // æ’å…¥
        MesPdDocumentDO document = BeanUtils.toBean(createReqVO, MesPdDocumentDO.class);
        documentMapper.insert(document);
        return document.getId();
    }
    @Override
    public void updateDocument(MesPdDocumentSaveReqVO updateReqVO) {
        // æ ¡éªŒå­˜åœ¨
        validateDocumentExists(updateReqVO.getId());
        // æ›´æ–°
        MesPdDocumentDO updateObj = BeanUtils.toBean(updateReqVO, MesPdDocumentDO.class);
        documentMapper.updateById(updateObj);
    }
    @Override
    public void deleteDocument(Long id) {
        // æ ¡éªŒå­˜åœ¨
        validateDocumentExists(id);
        // åˆ é™¤
        documentMapper.deleteById(id);
    }
    @Override
    public MesPdDocumentDO getDocument(Long id) {
        return documentMapper.selectById(id);
    }
    @Override
    public PageResult<MesPdDocumentDO> getDocumentPage(MesPdDocumentPageReqVO pageReqVO) {
        return documentMapper.selectPage(pageReqVO);
    }
    @Override
    public List<MesPdDocumentDO> getDocumentListByProjectId(Long projectId) {
        return documentMapper.selectListByProjectId(projectId);
    }
    @Override
    public void deleteByProjectId(Long projectId) {
        documentMapper.deleteByProjectId(projectId);
    }
    private MesPdDocumentDO validateDocumentExists(Long id) {
        MesPdDocumentDO document = documentMapper.selectById(id);
        if (document == null) {
            throw exception(PD_DOCUMENT_NOT_EXISTS);
        }
        return document;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/documentaudit/MesPdDocumentAuditService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.mes.service.pd.documentaudit;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo.MesPdDocumentAuditPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo.MesPdDocumentAuditSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.documentaudit.MesPdDocumentAuditDO;
import jakarta.validation.Valid;
import java.util.List;
/**
 * MES èµ„料审核 Service æŽ¥å£
 *
 * @author èŠ‹é“æºç 
 */
public interface MesPdDocumentAuditService {
    /**
     * åˆ›å»ºèµ„料审核
     *
     * @param createReqVO åˆ›å»ºä¿¡æ¯
     * @return ç¼–号
     */
    Long createDocumentAudit(@Valid MesPdDocumentAuditSaveReqVO createReqVO);
    /**
     * æ›´æ–°èµ„料审核
     *
     * @param updateReqVO æ›´æ–°ä¿¡æ¯
     */
    void updateDocumentAudit(@Valid MesPdDocumentAuditSaveReqVO updateReqVO);
    /**
     * åˆ é™¤èµ„料审核
     *
     * @param id ç¼–号
     */
    void deleteDocumentAudit(Long id);
    /**
     * èŽ·å¾—èµ„æ–™å®¡æ ¸
     *
     * @param id ç¼–号
     * @return èµ„料审核
     */
    MesPdDocumentAuditDO getDocumentAudit(Long id);
    /**
     * èŽ·å¾—èµ„æ–™å®¡æ ¸åˆ†é¡µ
     *
     * @param pageReqVO åˆ†é¡µæŸ¥è¯¢
     * @return èµ„料审核分页
     */
    PageResult<MesPdDocumentAuditDO> getDocumentAuditPage(MesPdDocumentAuditPageReqVO pageReqVO);
    /**
     * èŽ·å¾—æŒ‡å®šèµ„æ–™ä¸‹çš„å®¡æ ¸åˆ—è¡¨
     *
     * @param documentId èµ„æ–™ID
     * @return å®¡æ ¸åˆ—表
     */
    List<MesPdDocumentAuditDO> getDocumentAuditListByDocumentId(Long documentId);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/documentaudit/MesPdDocumentAuditServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
package cn.iocoder.yudao.module.mes.service.pd.documentaudit;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo.MesPdDocumentAuditPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.documentaudit.vo.MesPdDocumentAuditSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.documentaudit.MesPdDocumentAuditDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.documentaudit.MesPdDocumentAuditMapper;
import cn.iocoder.yudao.module.mes.service.pd.document.MesPdDocumentService;
import com.mysql.cj.protocol.Security;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PD_DOCUMENT_AUDIT_NOT_EXISTS;
/**
 * MES èµ„料审核 Service å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Service
@Validated
public class MesPdDocumentAuditServiceImpl implements MesPdDocumentAuditService {
    @Resource
    private MesPdDocumentAuditMapper documentAuditMapper;
    @Resource
    private MesPdDocumentService documentService;
    @Override
    public Long createDocumentAudit(MesPdDocumentAuditSaveReqVO createReqVO) {
        // æ ¡éªŒèµ„料存在
        documentService.getDocument(createReqVO.getDocumentId());
        // æ’å…¥
        MesPdDocumentAuditDO documentAudit = BeanUtils.toBean(createReqVO, MesPdDocumentAuditDO.class);
        documentAuditMapper.insert(documentAudit);
        return documentAudit.getId();
    }
    @Override
    public void updateDocumentAudit(MesPdDocumentAuditSaveReqVO updateReqVO) {
        // æ ¡éªŒå­˜åœ¨
        validateDocumentAuditExists(updateReqVO.getId());
        if (updateReqVO.getAuditStatus() ==1 ) {
            updateReqVO.setReviewer(SecurityFrameworkUtils.getLoginUserId());
            updateReqVO.setAuditTime(LocalDateTime.now());
        }
        // æ›´æ–°
        MesPdDocumentAuditDO updateObj = BeanUtils.toBean(updateReqVO, MesPdDocumentAuditDO.class);
        documentAuditMapper.updateById(updateObj);
    }
    @Override
    public void deleteDocumentAudit(Long id) {
        // æ ¡éªŒå­˜åœ¨
        validateDocumentAuditExists(id);
        // åˆ é™¤
        documentAuditMapper.deleteById(id);
    }
    @Override
    public MesPdDocumentAuditDO getDocumentAudit(Long id) {
        return documentAuditMapper.selectById(id);
    }
    @Override
    public PageResult<MesPdDocumentAuditDO> getDocumentAuditPage(MesPdDocumentAuditPageReqVO pageReqVO) {
        return documentAuditMapper.selectPage(pageReqVO);
    }
    @Override
    public List<MesPdDocumentAuditDO> getDocumentAuditListByDocumentId(Long documentId) {
        return documentAuditMapper.selectListByDocumentId(documentId);
    }
    private MesPdDocumentAuditDO validateDocumentAuditExists(Long id) {
        MesPdDocumentAuditDO documentAudit = documentAuditMapper.selectById(id);
        if (documentAudit == null) {
            throw exception(PD_DOCUMENT_AUDIT_NOT_EXISTS);
        }
        return documentAudit;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/project/MesPdProjectService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
package cn.iocoder.yudao.module.mes.service.pd.project;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo.MesPdProjectPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo.MesPdProjectSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.project.MesPdProjectDO;
import jakarta.validation.Valid;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * MES è®¾è®¡é¡¹ç›® Service æŽ¥å£
 *
 * @author èŠ‹é“æºç 
 */
public interface MesPdProjectService {
    /**
     * åˆ›å»ºè®¾è®¡é¡¹ç›®
     *
     * @param createReqVO åˆ›å»ºä¿¡æ¯
     * @return ç¼–号
     */
    Long createProject(@Valid MesPdProjectSaveReqVO createReqVO);
    /**
     * æ›´æ–°è®¾è®¡é¡¹ç›®
     *
     * @param updateReqVO æ›´æ–°ä¿¡æ¯
     */
    void updateProject(@Valid MesPdProjectSaveReqVO updateReqVO);
    /**
     * åˆ é™¤è®¾è®¡é¡¹ç›®
     *
     * @param id ç¼–号
     */
    void deleteProject(Long id);
    /**
     * æ ¡éªŒè®¾è®¡é¡¹ç›®æ˜¯å¦å­˜åœ¨
     *
     * @param id ç¼–号
     * @return è®¾è®¡é¡¹ç›®
     */
    MesPdProjectDO validateProjectExists(Long id);
    /**
     * èŽ·å¾—è®¾è®¡é¡¹ç›®
     *
     * @param id ç¼–号
     * @return è®¾è®¡é¡¹ç›®
     */
    MesPdProjectDO getProject(Long id);
    /**
     * èŽ·å¾—è®¾è®¡é¡¹ç›®åˆ†é¡µ
     *
     * @param pageReqVO åˆ†é¡µæŸ¥è¯¢
     * @return è®¾è®¡é¡¹ç›®åˆ†é¡µ
     */
    PageResult<MesPdProjectDO> getProjectPage(MesPdProjectPageReqVO pageReqVO);
    /**
     * å‘布设计项目(待发布→待上传)
     *
     * @param id ç¼–号
     */
    void publishProject(Long id);
    /**
     * ä¸Šä¼ èµ„料(待上传→待审核)
     * <p>
     * åŒæ—¶è‡ªåŠ¨ä¸ºè¯¥é¡¹ç›®ä¸‹æ‰€æœ‰èµ„æ–™åˆ›å»ºå®¡æ ¸è®°å½•
     *
     * @param id     ç¼–号
     * @param userId å½“前登录用户ID
     */
    void uploadProject(Long id, Long userId);
    /**
     * å®¡æ ¸è®¾è®¡é¡¹ç›®ï¼ˆå¾…审核→待归档)
     *
     * @param id ç¼–号
     */
    void auditProject(Long id);
    /**
     * å½’档设计项目(待归档→已归档)
     * <p>
     * åŒæ—¶è‡ªåŠ¨åˆ›å»ºå½’æ¡£å°è´¦è®°å½•
     *
     * @param id     ç¼–号
     * @param userId å½“前登录用户ID
     */
    void archiveProject(Long id, Long userId);
    /**
     * æ‰¹é‡èŽ·å–è®¾è®¡é¡¹ç›® Map
     *
     * @param ids ç¼–号集合
     * @return è®¾è®¡é¡¹ç›® Map
     */
    Map<Long, MesPdProjectDO> getProjectMap(Collection<Long> ids);
    List<MesPdProjectDO> getProjectByProductCode(String productCode);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/project/MesPdProjectServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,274 @@
package cn.iocoder.yudao.module.mes.service.pd.project;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo.MesPdProjectPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pd.project.vo.MesPdProjectSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.document.MesPdDocumentDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.documentaudit.MesPdDocumentAuditDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.archive.MesPdArchiveDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.project.MesPdProjectDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.project.MesPdProjectMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.documentaudit.MesPdDocumentAuditMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.archive.MesPdArchiveMapper;
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.enums.pd.MesPdProjectStatusEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.service.pd.document.MesPdDocumentService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
/**
 * MES è®¾è®¡é¡¹ç›® Service å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Service
@Validated
public class MesPdProjectServiceImpl implements MesPdProjectService {
    @Resource
    private MesPdProjectMapper projectMapper;
    @Resource
    @Lazy
    private MesPdDocumentService documentService;
    @Resource
    private MesPdDocumentAuditMapper documentAuditMapper;
    @Resource
    private MesPdArchiveMapper archiveMapper;
    @Resource
    @Lazy
    private MesMdAutoCodeRecordService autoCodeRecordService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createProject(MesPdProjectSaveReqVO createReqVO) {
        // æ ¡éªŒä»»åŠ¡ç¼–ç å”¯ä¸€
        validateTaskCodeUnique(null, createReqVO.getTaskCode());
        // æ’å…¥
        MesPdProjectDO project = MesPdProjectDO.builder()
                .taskCode(createReqVO.getTaskCode())
                .taskName(createReqVO.getTaskName())
                .productCode(createReqVO.getProductCode())
                .productName(createReqVO.getProductName())
                .version(createReqVO.getVersion())
                .status(MesPdProjectStatusEnum.INITIATE.getStatus())
                .chiefDesigner(createReqVO.getChiefDesigner())
                .reviewer(createReqVO.getReviewer())
                .planFinishTime(createReqVO.getPlanFinishTime())
                .remark(createReqVO.getRemark())
                .build();
        projectMapper.insert(project);
        return project.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateProject(MesPdProjectSaveReqVO updateReqVO) {
        // æ ¡éªŒå­˜åœ¨
        MesPdProjectDO project = validateProjectExists(updateReqVO.getId());
        // æ ¡éªŒçŠ¶æ€ä¸ºå¾…å‘å¸ƒ
        validateProjectInitiate(project);
        // æ ¡éªŒä»»åŠ¡ç¼–ç å”¯ä¸€
        validateTaskCodeUnique(updateReqVO.getId(), updateReqVO.getTaskCode());
        // æ›´æ–°
        MesPdProjectDO updateObj = MesPdProjectDO.builder()
                .id(updateReqVO.getId())
                .taskCode(updateReqVO.getTaskCode())
                .taskName(updateReqVO.getTaskName())
                .productCode(updateReqVO.getProductCode())
                .productName(updateReqVO.getProductName())
                .version(updateReqVO.getVersion())
                .chiefDesigner(updateReqVO.getChiefDesigner())
                .reviewer(updateReqVO.getReviewer())
                .planFinishTime(updateReqVO.getPlanFinishTime())
                .remark(updateReqVO.getRemark())
                .build();
        projectMapper.updateById(updateObj);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProject(Long id) {
        // æ ¡éªŒå­˜åœ¨
        MesPdProjectDO project = validateProjectExists(id);
        // æ ¡éªŒçŠ¶æ€ä¸ºå¾…å‘å¸ƒ
        validateProjectInitiate(project);
        // åˆ é™¤èµ„料子表
        documentService.deleteByProjectId(id);
        // åˆ é™¤
        projectMapper.deleteById(id);
    }
    @Override
    public MesPdProjectDO validateProjectExists(Long id) {
        MesPdProjectDO project = projectMapper.selectById(id);
        if (project == null) {
            throw exception(PD_PROJECT_NOT_EXISTS);
        }
        return project;
    }
    @Override
    public MesPdProjectDO getProject(Long id) {
        return projectMapper.selectById(id);
    }
    @Override
    public PageResult<MesPdProjectDO> getProjectPage(MesPdProjectPageReqVO pageReqVO) {
        return projectMapper.selectPage(pageReqVO);
    }
    @Override
    public void publishProject(Long id) {
        // æ ¡éªŒå­˜åœ¨
        MesPdProjectDO project = validateProjectExists(id);
        // æ ¡éªŒçŠ¶æ€ä¸ºå¾…å‘å¸ƒ
        if (!MesPdProjectStatusEnum.INITIATE.getStatus().equals(project.getStatus())) {
            throw exception(PD_PROJECT_NOT_INITIATE_FOR_PUBLISH);
        }
        // æ›´æ–°çŠ¶æ€
        projectMapper.updateById(MesPdProjectDO.builder()
                .id(id)
                .status(MesPdProjectStatusEnum.PUBLISH.getStatus())
                .build());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void uploadProject(Long id, Long userId) {
        // æ ¡éªŒå­˜åœ¨
        MesPdProjectDO project = validateProjectExists(id);
        // æ ¡éªŒçŠ¶æ€ä¸ºå¾…ä¸Šä¼ 
        if (!MesPdProjectStatusEnum.PUBLISH.getStatus().equals(project.getStatus())) {
            throw exception(PD_PROJECT_NOT_PUBLISH_FOR_UPLOAD);
        }
        // æ›´æ–°é¡¹ç›®çŠ¶æ€ä¸ºå¾…å®¡æ ¸
        projectMapper.updateById(MesPdProjectDO.builder()
                .id(id)
                .status(MesPdProjectStatusEnum.UPLOAD.getStatus())
                .build());
        // è‡ªåŠ¨ä¸ºè¯¥é¡¹ç›®ä¸‹æ‰€æœ‰èµ„æ–™åˆ›å»ºå®¡æ ¸è®°å½•
        List<MesPdDocumentDO> documents = documentService.getDocumentListByProjectId(id);
        for (MesPdDocumentDO document : documents) {
            MesPdDocumentAuditDO audit = MesPdDocumentAuditDO.builder()
                    .auditNo(generateAuditNo())
                    .documentId(document.getId())
                    .documentName(document.getDocumentName())
                    .documentVersion(document.getDocumentVersion())
                    .reviewer(project.getReviewer())
                    .auditStatus(0) // å¾…审核
                    .build();
            documentAuditMapper.insert(audit);
        }
    }
    @Override
    public void auditProject(Long id) {
        // æ ¡éªŒå­˜åœ¨
        MesPdProjectDO project = validateProjectExists(id);
        // æ ¡éªŒçŠ¶æ€ä¸ºå¾…å®¡æ ¸
        if (!MesPdProjectStatusEnum.UPLOAD.getStatus().equals(project.getStatus())) {
            throw exception(PD_PROJECT_NOT_UPLOAD_FOR_AUDIT);
        }
        // æ ¡éªŒè¯¥é¡¹ç›®ä¸‹æ‰€æœ‰èµ„料审核记录均已审核(不存在待审核的记录)
        List<MesPdDocumentDO> documents = documentService.getDocumentListByProjectId(id);
        List<Long> documentIds = documents.stream().map(MesPdDocumentDO::getId).toList();
        if (documentAuditMapper.existsPendingAuditByDocumentIds(documentIds)) {
            throw exception(PD_PROJECT_AUDIT_NOT_FINISHED);
        }
        // æ›´æ–°çŠ¶æ€
        projectMapper.updateById(MesPdProjectDO.builder()
                .id(id)
                .status(MesPdProjectStatusEnum.AUDIT.getStatus())
                .build());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void archiveProject(Long id, Long userId) {
        // æ ¡éªŒå­˜åœ¨
        MesPdProjectDO project = validateProjectExists(id);
        // æ ¡éªŒçŠ¶æ€ä¸ºå¾…å½’æ¡£
        if (!MesPdProjectStatusEnum.AUDIT.getStatus().equals(project.getStatus())) {
            throw exception(PD_PROJECT_NOT_AUDIT_FOR_ARCHIVE);
        }
        // æ›´æ–°é¡¹ç›®çŠ¶æ€ä¸ºå·²å½’æ¡£
        projectMapper.updateById(MesPdProjectDO.builder()
                .id(id)
                .status(MesPdProjectStatusEnum.ARCHIVE.getStatus())
                .build());
        // è‡ªåŠ¨åˆ›å»ºå½’æ¡£å°è´¦è®°å½•
        MesPdArchiveDO archive = MesPdArchiveDO.builder()
                .archiveCode(generateArchiveCode())
                .projectId(id)
                .archiver(userId)
                .archiveStatus(1) // å·²å½’æ¡£
                .archiveTime(LocalDateTime.now())
                .build();
        archiveMapper.insert(archive);
    }
    @Override
    public Map<Long, MesPdProjectDO> getProjectMap(Collection<Long> ids) {
        List<MesPdProjectDO> list = projectMapper.selectBatchIds(ids);
        return convertMap(list, MesPdProjectDO::getId);
    }
    @Override
    public List<MesPdProjectDO> getProjectByProductCode(String productCode) {
        return projectMapper.selectList(new QueryWrapper<MesPdProjectDO>().lambda().eq(MesPdProjectDO::getProductCode,productCode).eq(MesPdProjectDO::getStatus, MesPdProjectStatusEnum.ARCHIVE.getStatus()));
    }
    private void validateProjectInitiate(MesPdProjectDO project) {
        if (!MesPdProjectStatusEnum.INITIATE.getStatus().equals(project.getStatus())) {
            throw exception(PD_PROJECT_NOT_INITIATE);
        }
    }
    private void validateTaskCodeUnique(Long id, String taskCode) {
        MesPdProjectDO existing = projectMapper.selectByTaskCode(taskCode);
        if (existing == null) {
            return;
        }
        // å¦‚æžœ id ä¸ºç©ºï¼Œè¯´æ˜Žæ˜¯æ–°å¢žï¼Œç¼–码重复
        if (id == null) {
            throw exception(PD_PROJECT_CODE_DUPLICATE);
        }
        // å¦‚æžœ id ä¸ä¸ºç©ºï¼Œè¯´æ˜Žæ˜¯ä¿®æ”¹ï¼Œç¼–码重复且不是同一条记录
        if (!existing.getId().equals(id)) {
            throw exception(PD_PROJECT_CODE_DUPLICATE);
        }
    }
    /**
     * ç”Ÿæˆå®¡æ ¸å•号(使用编码规则)
     */
    private String generateAuditNo() {
        return autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PD_DOCUMENT_AUDIT_CODE.getCode());
    }
    /**
     * ç”Ÿæˆå½’档编号(使用编码规则)
     */
    private String generateArchiveCode() {
        return autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PD_ARCHIVE_CODE.getCode());
    }
}
yudao-module-mes/src/main/resources/mapper/pd/archive/MesPdArchiveMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.mes.dal.mysql.pd.archive.MesPdArchiveMapper">
    <!--
        æŒ‰ project_id åˆ†ç»„分页查询:每组取最新一条归档记录
        åŒæ—¶å…³è”项目表获取产品编码、产品名称
        å­æŸ¥è¯¢ç»Ÿè®¡æ¯ä¸ªé¡¹ç›®ä¸‹çš„归档数量
        è¿”回最新归档的 updater、update_time
        MyBatis-Plus åˆ†é¡µæ’件会自动拦截 IPage å‚数并添加 LIMIT
    -->
    <select id="selectPageGroupByProjectId" resultType="cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo.MesPdArchiveGroupRespVO">
        SELECT a.*, p.product_code, p.product_name,p.task_code,p.task_name,su.nickname as archiver_nickname,
               (SELECT COUNT(*) FROM mes_pd_archive cnt WHERE cnt.project_id = a.project_id AND cnt.deleted = 0) AS archive_count
        FROM mes_pd_archive a
        INNER JOIN (
            SELECT MAX(sub.id) AS id
            FROM mes_pd_archive sub
            WHERE sub.deleted = 0
            <if test="req.archiveStatus != null">
                AND sub.archive_status = #{req.archiveStatus}
            </if>
            <if test="req.productCode != null and req.productCode != ''">
                AND EXISTS (
                    SELECT 1 FROM mes_pd_project pp
                    WHERE pp.id = sub.project_id AND pp.deleted = 0
                    AND pp.product_code LIKE CONCAT('%', #{req.productCode}, '%')
                )
            </if>
            <if test="req.productName != null and req.productName != ''">
                AND EXISTS (
                    SELECT 1 FROM mes_pd_project pp
                    WHERE pp.id = sub.project_id AND pp.deleted = 0
                    AND pp.product_name LIKE CONCAT('%', #{req.productName}, '%')
                )
            </if>
            GROUP BY sub.project_id
        ) g ON a.id = g.id
        LEFT JOIN mes_pd_project p ON a.project_id = p.id AND p.deleted = 0
        left join system_users su on su.id =  a.archiver
        <where>
            <if test="req.archiveCode != null and req.archiveCode != ''">
                AND a.archive_code LIKE CONCAT('%', #{req.archiveCode}, '%')
            </if>
        </where>
        ORDER BY a.update_time DESC
    </select>
</mapper>