无尘阁日记

无尘阁日记

跟AI协作封装上传底稿
2025-04-22

问:

在上传底稿的这个方法前面加一个判断是否单项资产项目的逻辑。

public function actionUpload()

{

//   file_put_contents(Yii::$app->basePath . "/uploadProject.json", json_encode(array_merge(Yii::$app->request->requestParams(), Yii::$app->request->post()), JSON_UNESCAPED_UNICODE));

$params = array_merge(Yii::$app->request->requestParams(), Yii::$app->request->post());

$result = $this->checkParams($params);

if ($result !== true) return $result;

如果是,则走新分支,新分支调用一个写在SingleAssetHelper这个类里的静态独立方法,要求实现的逻辑是:

1. 上传来的文件存到两个地方,一个是更新当前项目底稿路径,一个是生成版本。

当前底稿路径为:TJProjectStatistics这个模型里的ManuscriptFile那里,覆盖就行。

版本则是新生成一条版本记录,目录路径为:

$curDay = date('Ymd', time());

$createAt = time();

$ext = pathinfo($data['OriginalMbFile'], PATHINFO_EXTENSION);

$versionPath = Util::addTenantIdToDir(Yii::$app->params['user.headOfficeUid'], DIRECTORY_SEPARATOR

. Yii::$app->params['globalVariables']['versionDir'], $curDay . DIRECTORY_SEPARATOR . 'SingleAsset' .

DIRECTORY_SEPARATOR);

Yii::error($versionPath, '项目版本文件');

if (!file_exists(Yii::$app->basePath . $versionPath)) {

mkdir(Yii::$app->basePath . $versionPath, 0777, true);

}

这段代码是生成版本文件路径的。

另外呢生成版本的调用代码如下:

TJProjectStatistics::updateAll(['ManuscriptFile' => $fileResolveData['Data']['CurrentPath'], 'UpdatedAt' => time()], ['Id' => $data['Id']]);

$filePath = Yii::$app->basePath . $fileResolveData['Data']['VersionPath'];

$sizeInBytes = filesize($filePath); // 返回文件大小(字节);

$fileName = pathinfo($fileResolveData['Data']['VersionPath'], PATHINFO_FILENAME);

$file = [

'FilePath' => $fileResolveData['Data']['VersionPath'],  // 文件在服务器上的存储路径或唯一标识

'FileName' => $fileName,                                // 原始文件名

'Size' => $sizeInBytes,                                 // 文件大小(字节),可选

];

$versionType = TJFileHistory::VERSION_TYPE_AUTOUPLOAD + TJFileHistory::VERSION_TYPE_DENY;

$historyParams = [

'UserId' => $data['UserId'],                     // 当前登录用户的ID

'ProjectId' => $data['Id'],        // 所属项目ID

'Remark' => '首次上传审计底稿',       // 系统备注(记录上传原因)

'VersionType' => $versionType,     // 版本类型:例如 2 = 模型同步前版本

'UserRemark' => '项目初稿',   // 用户备注(允许后续修改)

];


// 调用保存方法

$result = TJFileHistorySingleAsset::saveFileAndHistory($file, $historyParams);


if ($result['success']) {

Yii::info("版本生成成功,FileId: {$result['fileId']},HistoryId: {$result['historyId']}");

} else {

Yii::error("版本生成失败:{$result['message']}");

}


你帮我把这个独立方法封装一下,有不明白的问我。

答:

太好了,你已经提供了所有关键细节。我来帮你做以下三件事:


✅ 1. 修改 actionUpload 开头,判断是否是单项资产项目

public function actionUpload()
{
    $params = array_merge(Yii::$app->request->requestParams(), Yii::$app->request->post());
    $result = $this->checkParams($params);
    if ($result !== true) return $result;

    // 新增逻辑:判断是否单项资产项目
    $project = TJProjectStatistics::findOne($params['ProjectId']);
    if ($project && (int)$project->ProjectType === TJProjectStatistics::PROJECT_TYPE_SINGLE_ASSET) {
        return SingleAssetHelper::handleUpload($project, $params);
    }

    // 继续原本普通项目逻辑...
}

✅ 2. 新建 SingleAssetHelper::handleUpload() 静态方法

class SingleAssetHelper
{
    /**
     * 处理单项资产项目的底稿上传逻辑
     */
    public static function handleUpload($project, $params)
    {
        try {
            $curDay = date('Ymd');
            $createAt = time();
            $ext = pathinfo($params['OriginalMbFile'], PATHINFO_EXTENSION);

            // 构建版本路径
            $versionPath = Util::addTenantIdToDir(
                Yii::$app->params['user.headOfficeUid'],
                DIRECTORY_SEPARATOR . Yii::$app->params['globalVariables']['versionDir'],
                $curDay . DIRECTORY_SEPARATOR . 'SingleAsset' . DIRECTORY_SEPARATOR
            );
            Yii::error($versionPath, '项目版本文件');

            $fullVersionDir = Yii::$app->basePath . $versionPath;
            if (!file_exists($fullVersionDir)) {
                mkdir($fullVersionDir, 0777, true);
            }

            // 构建文件实际路径
            $fileName = uniqid('single_', true) . '.' . $ext;
            $targetPath = $fullVersionDir . $fileName;
            $versionRelPath = $versionPath . $fileName;

            // 保存上传文件到目标路径
            $uploadFilePath = Yii::$app->basePath . $params['OriginalMbFile'];
            if (!file_exists($uploadFilePath)) {
                return ['IsSuccess' => 0, 'ErrMsg' => '上传文件不存在'];
            }

            if (!copy($uploadFilePath, $targetPath)) {
                return ['IsSuccess' => 0, 'ErrMsg' => '文件保存失败'];
            }

            // 更新项目当前底稿路径
            TJProjectStatistics::updateAll([
                'ManuscriptFile' => $params['OriginalMbFile'],
                'UpdatedAt' => time()
            ], ['Id' => $project->Id]);

            // 构造版本文件数据结构
            $fileSize = filesize($targetPath);
            $fileInfo = [
                'FilePath' => $versionRelPath,
                'FileName' => pathinfo($fileName, PATHINFO_FILENAME),
                'Size' => $fileSize
            ];

            $versionType = TJFileHistory::VERSION_TYPE_AUTOUPLOAD + TJFileHistory::VERSION_TYPE_DENY;
            $historyParams = [
                'UserId' => $params['UserId'],
                'ProjectId' => $project->Id,
                'Remark' => '首次上传审计底稿',
                'VersionType' => $versionType,
                'UserRemark' => '项目初稿',
            ];

            // 调用生成版本方法
            $result = TJFileHistorySingleAsset::saveFileAndHistory($fileInfo, $historyParams);
            if ($result['success']) {
                Yii::info("版本生成成功,FileId: {$result['fileId']},HistoryId: {$result['historyId']}");
                return ['IsSuccess' => 1, 'ErrMsg' => '上传成功'];
            } else {
                Yii::error("版本生成失败:" . $result['message']);
                return ['IsSuccess' => 0, 'ErrMsg' => '版本生成失败:' . $result['message']];
            }

        } catch (\Exception $e) {
            Yii::error("handleUpload异常:" . $e->getMessage());
            return ['IsSuccess' => 0, 'ErrMsg' => '系统异常:' . $e->getMessage()];
        }
    }
}

✅ 3. 参数说明与依赖提醒

这个方法需要:

  • $params['OriginalMbFile']:上传文件的路径,相对路径,例如 /uploads/tmp/somefile.xlsx

  • Util::addTenantIdToDir()Yii::$app->params['user.headOfficeUid']:你已有函数和配置

  • TJFileHistorySingleAsset::saveFileAndHistory():昨天实现过,压缩加密并入库文件和历史表

问:

// 保存上传文件到目标路径

$uploadFilePath = Yii::$app->basePath . $params['OriginalMbFile'];

if (!file_exists($uploadFilePath)) {

return ['IsSuccess' => 0, 'ErrMsg' => '上传文件不存在'];

}


你这保存是不对的。我的意思是:

用户通过file上传来的文件流,需要保存到$project['ManuscriptFile']这个路径和$targetPath底下。

上传的文件要落盘到这两个位置。你需要实现这个落盘的过程,用yii2的文件保存方法、检测等。

答:

非常清楚了!你想要的是:

  1. 用户通过 file 表单上传的底稿文件;

  2. 保存到两个路径

    • TJProjectStatistics.ManuscriptFile 记录的路径(当前底稿);

    • $targetPath(新生成的版本文件路径);

  3. 使用 Yii2 的文件上传处理方法(不能用 copy(),必须用 UploadedFile 的方式落盘);

  4. 然后更新数据库、生成版本。

那我来重构这部分核心逻辑,确保它能正确落盘到两个位置,并符合你的实际使用方式。


✅ 重构后的落盘保存逻辑(支持双路径保存)

请替换你之前 handleUpload() 方法中处理文件保存的部分:

use yii\web\UploadedFile;

public static function handleUpload($project, $params)
{
    try {
        $curDay = date('Ymd');
        $createAt = time();
        $uploadFile = UploadedFile::getInstanceByName('file'); // 获取上传的文件对象

        if (!$uploadFile) {
            return ['IsSuccess' => 0, 'ErrMsg' => '未收到上传文件'];
        }

        // 判断原始扩展名
        $ext = $uploadFile->getExtension();
        $fileName = uniqid('single_', true) . '.' . $ext;

        // 生成版本文件路径
        $versionPath = Util::addTenantIdToDir(
            Yii::$app->params['user.headOfficeUid'],
            DIRECTORY_SEPARATOR . Yii::$app->params['globalVariables']['versionDir'],
            $curDay . DIRECTORY_SEPARATOR . 'SingleAsset' . DIRECTORY_SEPARATOR
        );
        $fullVersionDir = Yii::$app->basePath . $versionPath;
        if (!file_exists($fullVersionDir)) {
            mkdir($fullVersionDir, 0777, true);
        }
        $targetPath = $fullVersionDir . $fileName;
        $versionRelPath = $versionPath . $fileName;

        // 生成当前底稿路径(覆盖项目底稿)
        $currentPath = '/uploads/single_asset/' . $project->Id . '.' . $ext;
        $absCurrentPath = Yii::$app->basePath . $currentPath;
        $currentDir = dirname($absCurrentPath);
        if (!file_exists($currentDir)) {
            mkdir($currentDir, 0777, true);
        }

        // 保存到两个路径
        if (!$uploadFile->saveAs($absCurrentPath)) {
            return ['IsSuccess' => 0, 'ErrMsg' => '保存当前底稿失败'];
        }
        if (!$uploadFile->saveAs($targetPath)) {
            return ['IsSuccess' => 0, 'ErrMsg' => '保存版本底稿失败'];
        }

        // 更新当前底稿路径
        TJProjectStatistics::updateAll([
            'ManuscriptFile' => $currentPath,
            'UpdatedAt' => time()
        ], ['Id' => $project->Id]);

        // 构造版本入库文件结构
        $fileSize = filesize($targetPath);
        $fileInfo = [
            'FilePath' => $versionRelPath,
            'FileName' => pathinfo($fileName, PATHINFO_FILENAME),
            'Size' => $fileSize
        ];

        $versionType = TJFileHistory::VERSION_TYPE_AUTOUPLOAD + TJFileHistory::VERSION_TYPE_DENY;
        $historyParams = [
            'UserId' => $params['UserId'],
            'ProjectId' => $project->Id,
            'Remark' => '首次上传审计底稿',
            'VersionType' => $versionType,
            'UserRemark' => '项目初稿',
        ];

        $result = TJFileHistorySingleAsset::saveFileAndHistory($fileInfo, $historyParams);
        if ($result['success']) {
            Yii::info("版本生成成功,FileId: {$result['fileId']},HistoryId: {$result['historyId']}");
            return ['IsSuccess' => 1, 'ErrMsg' => '上传成功'];
        } else {
            Yii::error("版本生成失败:" . $result['message']);
            return ['IsSuccess' => 0, 'ErrMsg' => '版本生成失败:' . $result['message']];
        }

    } catch (\Exception $e) {
        Yii::error("handleUpload异常:" . $e->getMessage());
        return ['IsSuccess' => 0, 'ErrMsg' => '系统异常:' . $e->getMessage()];
    }
}

✅ 说明与注意事项

  • 文件字段名必须是 file,对应 <input type="file" name="file">

  • 底稿路径存一份在 /uploads/single_asset/{项目ID}.{ext},用作当前版本;

  • 另一份是版本目录文件,生成版本记录;

  • 入库过程依赖昨天写好的 TJFileHistorySingleAsset::saveFileAndHistory() 方法。