恢复修改的数据
This commit is contained in:
@@ -1,25 +1,183 @@
|
||||
import type { Demand } from '@/types/business/demand'
|
||||
import { storage } from '@/utils/storage'
|
||||
// 需求提报接口(真实后端绑定)
|
||||
// 草稿 com.ruoyi.vermins.controller.pc.web.WorkUserOrderDraftController → /web/workOrderDraft
|
||||
// 提交 com.ruoyi.vermins.controller.pc.web.ProjectRequirementUserController → /web/requirementPool/create(WorkOrderDraftBo)
|
||||
// 同控制器的 /submit(RdProjectBo)是直接提报入口(额外落 vm_rd_project),前端当前未使用
|
||||
// 业务类型 com.ruoyi.vermins.controller.pc.ProjectSubmissionWebController → /web/projectSubmission(@SaIgnore 免登录)
|
||||
// 预算范围 org.dromara.system.controller.system.SysDictDataController → /system/dict/data/type/{dictType}
|
||||
// 图片换链 org.dromara.resource.controller.SysOssController → /resource/oss/listByIds/{ids}
|
||||
// 鉴权:业务类型列表免登录;草稿 / 提交 / 字典 / OSS 需登录态(匿名实测 401,故 /demand 路由带 requiresAuth)
|
||||
//
|
||||
// 使用约定(与项目 SKILL.md 一致):
|
||||
// 页面/组件禁止直接 import axios,一律经 store 调用本模块;本文件只放「函数签名 + 请求路径」。
|
||||
import service, { request } from '@/utils/request'
|
||||
import type { ApiResponse } from '@/types/api'
|
||||
import type {
|
||||
Demand,
|
||||
DemandDraftVo,
|
||||
DictOption,
|
||||
OssUploadVo,
|
||||
OssUrlVo,
|
||||
SubmissionBizTypeVo,
|
||||
} from '@/types/business/demand'
|
||||
|
||||
// TODO(api): 待接口文档到位后,将以下 mock 替换为 request<T> 调用(页面 → store → api 数据流不变)
|
||||
/** 业务类型字典类型(sys_dict_type.dict_type)——仅作提报列表为空时的兜底值域 */
|
||||
export const BIZ_TYPE_DICT = 'vm_biz_type'
|
||||
/** 预算范围字典类型 */
|
||||
export const BUDGET_RANGE_DICT = 'vm_budget_range'
|
||||
|
||||
const DRAFT_KEY = 'demand-draft'
|
||||
/**
|
||||
* 业务类型列表(项目提报表,**免登录**)。
|
||||
*
|
||||
* <p>后端 `ProjectSubmissionWebController` 带 `@SaIgnore`,是需求页唯一匿名可用的接口;
|
||||
* 它与首页 `/web/home-page/service-type-list` 共用同一个 `queryEnabledList()`,只返回
|
||||
* `enabled = 1` 且未被逻辑删除的记录。</p>
|
||||
*
|
||||
* <p>返回项里 `subtitle` 是卡片描述来源、`logoUrl` 是卡片图标来源(实测是 OSS 文件标识,
|
||||
* 需经 {@link getOssUrls} 换链)。</p>
|
||||
*/
|
||||
export function getSubmissionBizTypes(): Promise<SubmissionBizTypeVo[]> {
|
||||
return request<SubmissionBizTypeVo[]>({
|
||||
url: '/web/projectSubmission/list',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
/** 提交需求(mock:返回提报单 id) */
|
||||
export function submitDemand(demand: Demand): Promise<{ id: string }> {
|
||||
if (!demand.businessType || !demand.projectName || !demand.coreDescription) {
|
||||
return Promise.reject(new Error('需求信息不完整,无法提交'))
|
||||
/**
|
||||
* OSS 文件标识 → 可直接访问的地址。
|
||||
*
|
||||
* <p>提报列表的 `coverUrl` / `logoUrl` 下发的是 OSS 主键(如 2099753406859292674)而非地址,
|
||||
* 直接当 `<img src>` 用会请求到本站的错误路径上,必须换链。</p>
|
||||
*
|
||||
* <p>⚠️ 本接口需要登录态;失败时调用方回退前端内置图标,且不阻塞页面。</p>
|
||||
*/
|
||||
export function getOssUrls(ids: (string | number)[]): Promise<OssUrlVo[]> {
|
||||
return request<OssUrlVo[]>({
|
||||
url: `/resource/oss/listByIds/${ids.join(',')}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务类型字典(`vm_biz_type`)。
|
||||
*
|
||||
* <p>⚠️ 仅作**兜底**用:业务类型值域已改为提报列表 {@link getSubmissionBizTypes};
|
||||
* 只有当提报表一条可用记录都没有时,store 才回退到本字典,避免页面无可选项。</p>
|
||||
*/
|
||||
export function getBizTypeDict(): Promise<DictOption[]> {
|
||||
return request<DictOption[]>({
|
||||
url: `/system/dict/data/type/${BIZ_TYPE_DICT}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
/** 预算范围选项(字典 vm_budget_range) */
|
||||
export function getBudgetRangeDict(): Promise<DictOption[]> {
|
||||
return request<DictOption[]>({
|
||||
url: `/system/dict/data/type/${BUDGET_RANGE_DICT}`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取当前登录用户的需求提报草稿(无草稿返回 null)。
|
||||
*
|
||||
* <p>⚠️ 后端 `WorkUserOrderDraftController#getDraft` 在"该用户还没有草稿"时会 NPE:
|
||||
* `VmWorkOrderDraftServiceImpl#queryById` 里 `draft` 为 null 仍继续执行 `draft.getId()`,
|
||||
* 实测返回 code=500「发生未知异常」。这里刻意绕过统一 `request`(它会对非 200 弹错误提示),
|
||||
* 直接用原始实例静默兜底:非 200 一律视作"无草稿"。后端补上判空后本段可简化。</p>
|
||||
*/
|
||||
export async function getDraftDemand(): Promise<DemandDraftVo | null> {
|
||||
const resp = await service.request<ApiResponse<DemandDraftVo>>({
|
||||
url: '/web/workOrderDraft/getDraft',
|
||||
method: 'get',
|
||||
})
|
||||
if (resp.data?.code !== 200) return null
|
||||
return resp.data.data ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* Demand → 后端 `WorkOrderDraftBo` 请求体。
|
||||
*
|
||||
* <p>显式列出 BO 的字段,**剔掉纯前端字段 `budgetManual`**(后端 BO/VO 与两张表都没有
|
||||
* 「是否手写」的概念,留着虽然会被 Jackson 忽略,但容易被误读成后端支持该标记)。</p>
|
||||
*
|
||||
* <p>预算范围只有一个字段可下发:预设档位是字典 dictValue,手写时是用户填的金额文本
|
||||
* ——后端没有可用的 `budgetCustom`(`WorkOrderDraftBo` 里被注释掉、表也无该列)。</p>
|
||||
*
|
||||
* <p>`developmentCycle` 是 Integer,表单未填时是空串,必须归一成 null:空串虽然能靠
|
||||
* Jackson 的宽松转换塞进去,但依赖默认行为、且语义上是"没填"。</p>
|
||||
*/
|
||||
function toBoPayload(demand: Demand): Record<string, unknown> {
|
||||
return {
|
||||
id: demand.id,
|
||||
userName: demand.userName,
|
||||
userPhone: demand.userPhone,
|
||||
bizType: demand.bizType,
|
||||
budgetRange: demand.budgetRange,
|
||||
developmentCycle: demand.developmentCycle === '' ? null : demand.developmentCycle,
|
||||
supplementaryRemarks: demand.supplementaryRemarks,
|
||||
projectName: demand.projectName,
|
||||
requirementDesc: demand.requirementDesc,
|
||||
attachmentInfo: demand.attachmentInfo,
|
||||
}
|
||||
return Promise.resolve({ id: `demand-${Date.now()}` })
|
||||
}
|
||||
|
||||
/** 读取本地草稿(无草稿返回 null) */
|
||||
export function getDraftDemand(): Promise<Demand | null> {
|
||||
return Promise.resolve(storage.sessionGet<Demand>(DRAFT_KEY))
|
||||
}
|
||||
|
||||
/** 保存草稿到本地 */
|
||||
/** 新增/覆盖草稿(后端 insertByBo 按登录用户 upsert) */
|
||||
export function saveDraft(demand: Demand): Promise<void> {
|
||||
storage.sessionSet(DRAFT_KEY, { ...demand, status: 'draft' })
|
||||
return Promise.resolve()
|
||||
return request<void>({
|
||||
url: '/web/workOrderDraft/add',
|
||||
method: 'post',
|
||||
data: toBoPayload(demand),
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空当前用户的草稿(后端 cleanHistoryDrafts) */
|
||||
export function resetDraft(): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/web/workOrderDraft/reset',
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
||||
/** 上传附件(multipart,后端字段名 file,返回 SysOssVo) */
|
||||
export function uploadDemandFile(file: File): Promise<OssUploadVo> {
|
||||
const data = new FormData()
|
||||
data.append('file', file)
|
||||
return request<OssUploadVo>({
|
||||
url: '/web/workOrderDraft/upload',
|
||||
method: 'post',
|
||||
data,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交需求(`POST /web/requirementPool/create`)。
|
||||
*
|
||||
* <p>入参与草稿共用同一个 BO(`WorkOrderDraftBo`),故请求体直接复用 {@link toBoPayload},
|
||||
* 不需要字段名转换。</p>
|
||||
*
|
||||
* <p>后端 `createRequirement` 一个事务里做三件事:</p>
|
||||
* <ul>
|
||||
* <li>落 `vm_project_requirement`(需求主记录,生成项目编号、优先级=未评估、状态=待接洽);</li>
|
||||
* <li>把**请求体里的 `attachmentInfo`** 逐条复制成需求附件(只复制 `ossId` 引用到
|
||||
* `vm_project_requirement_attachment`,OSS 文件不重复上传)——注意这里是直接吃请求体,
|
||||
* 不像 `/submit` 那样还得先落一次草稿,所以附件随本次请求一起走;</li>
|
||||
* <li>清掉该用户的草稿(`cleanHistoryDrafts`)。</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>后端必填校验:`bizType` / `projectName` / `requirementDesc` 任一为空返回
|
||||
* 「请完整填写业务类型、项目名称和需求描述」;联系人姓名与电话缺失时先回退登录用户资料,
|
||||
* 仍为空则报「请完善联系人姓名和联系电话」。</p>
|
||||
*
|
||||
* <p>⚠️ 本条链路**只落 `vm_project_requirement` 单表**。而「我的项目/需求」列表
|
||||
* (`/web/requirementPool/list`)查的是 `vm_rd_project`,因此 create 提交的记录不会出现在
|
||||
* 该列表里——要同时落两张表得走后端 `/web/requirementPool/submit`(`RdProjectBo`)。</p>
|
||||
*/
|
||||
export function submitDemand(demand: Demand): Promise<void> {
|
||||
return request<void>({
|
||||
url: '/web/requirementPool/create',
|
||||
method: 'post',
|
||||
data: toBoPayload(demand),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user