From 2ed3d48717a5ef2c91d282b3698f6a82dac85883 Mon Sep 17 00:00:00 2001 From: Dev Date: Wed, 30 Sep 2026 16:57:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E9=99=84=E4=BB=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/application/api/controller/Demand.php | 10 +-- .../application/common/command/DemandDeal.php | 2 +- .../application/common/model/order/Ships.php | 69 ++++++++++++++++++- .../common/service/OrderShipService.php | 69 +++++++++++++++++-- .../miniapp/pages/demand/deliver/index.js | 12 +--- 5 files changed, 138 insertions(+), 24 deletions(-) diff --git a/backend/application/api/controller/Demand.php b/backend/application/api/controller/Demand.php index 0e1274b..ef1ef1a 100644 --- a/backend/application/api/controller/Demand.php +++ b/backend/application/api/controller/Demand.php @@ -187,7 +187,7 @@ class Demand extends Api }; $list = DemandService::getList($where, $page_size, 'id desc', '', ['user', 'demandContracts', 'demandBiddings', 'winBidding']); //已发货的需求集合(发货表),用于开发者视角的发货按钮展示 - $deliveredDemandIds = OrderShipService::where(['demand_id' => ['in', $list->column('id')]])->column('demand_id'); + $deliveredDemandIds = OrderShipService::getDeliveredDemandIds($list->column('id')); $list = $list->each(function ($item, $key) use ($user_id, $opType, $btnNameFun, $deliveredDemandIds) { $demand_bidding = $item->demand_biddings; $bidUserIds = array_column($demand_bidding, 'user_id'); @@ -253,13 +253,7 @@ class Demand extends Api 'name' => urldecode(basename($fl)) ], $info->files); //交付内容(订单发货表,开发者发货后展示) - $ship = OrderShipService::where(['demand_id' => $id])->order('id', 'desc')->find(); - $info->delivery = $ship ? [ - 'images' => json_decode($ship->images, true) ?: [], - 'files' => json_decode($ship->files, true) ?: [], - 'remark' => $ship->remark, - 'delivery_time' => $ship->createtime, - ] : null; + $info->delivery = OrderShipService::getDemandDelivery($id); $this->success('', $info); } catch (\Exception $e) { diff --git a/backend/application/common/command/DemandDeal.php b/backend/application/common/command/DemandDeal.php index c28a84a..d0a4bad 100644 --- a/backend/application/common/command/DemandDeal.php +++ b/backend/application/common/command/DemandDeal.php @@ -47,7 +47,7 @@ class DemandDeal extends Command try { Db::transaction(function () use ($item, $confirmReceiptTimeoutDay) { //未发货的需求不自动确认收货,等待开发者发货交付 - $ship = OrderShipService::where(['demand_id' => $item->id])->order('id', 'desc')->find(); + $ship = OrderShipService::getDemandLastShip($item->id); if (!$ship) { return; } diff --git a/backend/application/common/model/order/Ships.php b/backend/application/common/model/order/Ships.php index 0529f85..cf9dd73 100644 --- a/backend/application/common/model/order/Ships.php +++ b/backend/application/common/model/order/Ships.php @@ -25,8 +25,75 @@ class Ships extends Model // 追加属性 protected $append = [ - + 'images_list', + 'files_list' ]; + + /** + * 多图列表([{url, name}]),供接口与后台直接使用 + */ + public function getImagesListAttr($value, $data) + { + return self::parseUrlList($data['images'] ?? ''); + } + + /** + * 多文件列表([{url, name}]),供接口与后台直接使用 + */ + public function getFilesListAttr($value, $data) + { + return self::parseUrlList($data['files'] ?? ''); + } + + /** + * 解析多图/多文件字段:英文逗号分隔的 URL 字符串 -> [{url, name}] + * 例:http://a.com/1.jpg,http://a.com/2.pdf => [['url'=>'http://a.com/1.jpg','name'=>'1.jpg'], ...] + * 文件名取 URL 末段并 urldecode(OSS 路径为中文转义) + * @param string $value + * @return array + */ + public static function parseUrlList($value) + { + $value = trim((string)$value); + if ($value === '') { + return []; + } + $list = []; + foreach (array_filter(explode(',', $value)) as $url) { + $url = trim($url); + if (!$url) { + continue; + } + $list[] = [ + 'url' => $url, + 'name' => urldecode(basename($url)) + ]; + } + + return $list; + } + + /** + * 将 URL 列表规范化为英文逗号分隔字符串(写入库时使用) + * 支持传入 URL 数组或 [{url=>''}] 数组 + * @param array|string $value + * @return string + */ + public static function toUrlString($value) + { + if (is_string($value)) { + $value = array_filter(explode(',', $value)); + } + $urls = []; + foreach ((array)$value as $item) { + $url = is_array($item) ? trim($item['url'] ?? '') : trim((string)$item); + if ($url) { + $urls[] = $url; + } + } + + return implode(',', array_unique($urls)); + } diff --git a/backend/application/common/service/OrderShipService.php b/backend/application/common/service/OrderShipService.php index b66af90..ce9c8e4 100644 --- a/backend/application/common/service/OrderShipService.php +++ b/backend/application/common/service/OrderShipService.php @@ -38,21 +38,24 @@ class OrderShipService /** * 需求交付发货单(开发者发货:凭证截图+交付附件+备注) + * 多图/多文件统一存英文逗号分隔的 URL 字符串: + * http://xxx/a.jpg,http://xxx/b.pdf * @param $order_no * @param $order_goods_spu_id * @param $demand_id - * @param array $images - * @param array $files + * @param array $images URL 数组 + * @param array $files URL 数组 * @param string $remark * @return mixed * @throws \Exception */ public static function createDemandDelivery($order_no, $order_goods_spu_id, $demand_id, array $images, array $files, $remark = '') { - $imagesStr = json_encode($images, JSON_UNESCAPED_UNICODE); - $filesStr = json_encode($files, JSON_UNESCAPED_UNICODE); + // 复用模型的规范化方法,统一为英文逗号分隔字符串 + $imagesStr = OrderShips::toUrlString($images); + $filesStr = OrderShips::toUrlString($files); if (mb_strlen($imagesStr) > 1000 || mb_strlen($filesStr) > 1000) { - exception('附件内容过多,请精简后重新发货'); + exception('附件数量过多或链接过长,请精简后重新发货'); } $data = [ 'order_no' => $order_no, @@ -69,4 +72,60 @@ class OrderShipService return $res; } + + /** + * 获取需求最新的发货单(未发货返回 null) + * @param int $demandId + * @return OrderShips|null + */ + public static function getDemandLastShip($demandId) + { + return self::where(['demand_id' => $demandId])->order('id', 'desc')->find(); + } + + /** + * 获取需求的交付内容(未发货返回 null) + * 结构:['images' => [{url,name}], 'files' => [{url,name}], 'remark' => '', 'delivery_time' => 时间戳] + * @param int $demandId + * @return array|null + */ + public static function getDemandDelivery($demandId) + { + $ship = self::getDemandLastShip($demandId); + if (!$ship) { + return null; + } + + return [ + 'images' => $ship->images_list, + 'files' => $ship->files_list, + 'remark' => $ship->remark, + 'delivery_time' => $ship->createtime, + ]; + } + + /** + * 判断需求是否已发货 + * @param int $demandId + * @return bool + */ + public static function hasDemandDelivery($demandId) + { + return !empty(self::getDemandLastShip($demandId)); + } + + /** + * 批量获取已发货的需求ID(用于列表按钮展示,避免 N+1 查询) + * @param array $demandIds + * @return array + */ + public static function getDeliveredDemandIds(array $demandIds) + { + $demandIds = array_filter(array_unique(array_map('intval', $demandIds))); + if (!$demandIds) { + return []; + } + + return self::where(['demand_id' => ['in', $demandIds]])->column('demand_id'); + } } \ No newline at end of file diff --git a/frontend/miniapp/pages/demand/deliver/index.js b/frontend/miniapp/pages/demand/deliver/index.js index 91089da..691c3d1 100644 --- a/frontend/miniapp/pages/demand/deliver/index.js +++ b/frontend/miniapp/pages/demand/deliver/index.js @@ -245,18 +245,12 @@ Page({ title: '提交中...' }); const { demandId, imageFiles, attachFiles, remark } = this.data; - // 只提交服务端地址与文件名,避免提交本地临时路径 + // 只提交服务端地址,避免提交本地临时路径(后端按英文逗号分隔存储) const params = { demandId, opType: 'deliverGoods', - images: imageFiles.map(file => ({ - url: file.fullUrl || file.url, - name: file.name || '' - })), - files: attachFiles.map(file => ({ - url: file.fullUrl || file.url, - name: file.name - })), + images: imageFiles.map(file => file.fullUrl || file.url), + files: attachFiles.map(file => file.fullUrl || file.url), remark }; demandOperate(params).then(