完善配送点合同附件管理
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
// 功能描述:实现配送点范围内的合同、合同气瓶和配送订单接口。
|
||||
// 版本:v1.1.0。
|
||||
package delivery
|
||||
|
||||
import (
|
||||
@@ -7,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
@@ -48,10 +51,53 @@ func scopedContract(ctx *gin.Context, identity string, pointID uint64) (models.G
|
||||
|
||||
func ListContract(ctx *gin.Context) {
|
||||
point, _, ok := currentScope(ctx)
|
||||
if ok {
|
||||
listScoped(ctx, &models.GasorderContract{}, common.ActiveRecords(db().Model(&models.GasorderContract{})).
|
||||
Where("gas_basic_id = ? AND delivery_basic_id = ?", point.GasBasicID, point.ID), "gasorder_contract.created_at desc")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
page, size := common.PageSize(ctx)
|
||||
query := common.ApplyKeywordFilter(ctx,
|
||||
common.ActiveRecords(db().Model(&models.GasorderContract{})).
|
||||
Where("gas_basic_id = ? AND delivery_basic_id = ?", point.GasBasicID, point.ID),
|
||||
&models.GasorderContract{})
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
var list []models.GasorderContract
|
||||
if err := query.Order("gasorder_contract.created_at desc").Offset((page - 1) * size).Limit(size).Find(&list).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
response, err := common.PublicResourceResponse(list)
|
||||
if err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
protected, err := protectDeliveryContractListResponse(ctx, response, list)
|
||||
if err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
}
|
||||
infra.Response.Success(ctx, gin.H{"total": total, "list": protected})
|
||||
}
|
||||
|
||||
// protectDeliveryContractListResponse 删除内部附件路径,仅补充无敏感信息的存在性标识。
|
||||
func protectDeliveryContractListResponse(ctx *gin.Context, response any, list []models.GasorderContract) ([]any, error) {
|
||||
protected := common.ProtectPreciseLocation(ctx, &models.GasorderContract{}, response)
|
||||
items, valid := protected.([]any)
|
||||
if !valid || len(items) != len(list) {
|
||||
return nil, errors.New("配送合同列表响应结构无效")
|
||||
}
|
||||
for index, item := range items {
|
||||
row, rowValid := item.(map[string]any)
|
||||
if !rowValid {
|
||||
return nil, errors.New("配送合同列表记录结构无效")
|
||||
}
|
||||
// 只返回是否存在附件,内部存储 URI 已由统一保护层删除。
|
||||
row["has_attachment"] = strings.TrimSpace(list[index].FileURI) != ""
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func GetContract(ctx *gin.Context) {
|
||||
@@ -63,6 +109,33 @@ func GetContract(ctx *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// UploadContractAttachment 在当前配送点登录范围内上传受控 PDF 临时附件。
|
||||
func UploadContractAttachment(ctx *gin.Context) {
|
||||
if _, _, ok := currentScope(ctx); !ok {
|
||||
return
|
||||
}
|
||||
platformgasorder.UploadGasorderContractAttachment(ctx)
|
||||
}
|
||||
|
||||
// CleanupContractAttachment 清理当前配送点操作人尚未绑定的临时附件。
|
||||
func CleanupContractAttachment(ctx *gin.Context) {
|
||||
if _, _, ok := currentScope(ctx); !ok {
|
||||
return
|
||||
}
|
||||
platformgasorder.CleanupGasorderContractAttachment(ctx)
|
||||
}
|
||||
|
||||
// ServeContractAttachment 仅预览当前配送点拥有的正式合同附件。
|
||||
func ServeContractAttachment(ctx *gin.Context) {
|
||||
point, _, ok := currentScope(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if _, valid := scopedContract(ctx, ctx.Param("identity"), point.ID); valid {
|
||||
platformgasorder.ServeGasorderContractAttachment(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func CreateContract(ctx *gin.Context) {
|
||||
point, station, ok := currentScope(ctx)
|
||||
if !ok {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// 功能描述:验证配送合同附件列表脱敏与公开状态投影。
|
||||
// 版本:v1.0.0。
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common"
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// TestProtectDeliveryContractListResponse 验证内部路径不会进入配送端列表响应。
|
||||
func TestProtectDeliveryContractListResponse(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
ctx.Request = httptest.NewRequest("GET", "/gasorder_contract", nil)
|
||||
contracts := []models.GasorderContract{
|
||||
{FileURI: "/uploads/contracts/2026/08/22/protected.pdf"},
|
||||
{},
|
||||
}
|
||||
response, err := common.PublicResourceResponse(contracts)
|
||||
if err != nil {
|
||||
t.Fatalf("构造公开响应失败: %v", err)
|
||||
}
|
||||
protected, err := protectDeliveryContractListResponse(ctx, response, contracts)
|
||||
if err != nil {
|
||||
t.Fatalf("合同列表脱敏失败: %v", err)
|
||||
}
|
||||
for index, item := range protected {
|
||||
row, ok := item.(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("第 %d 条响应不是对象", index)
|
||||
}
|
||||
if _, exposed := row["file_uri"]; exposed {
|
||||
t.Fatalf("第 %d 条响应暴露了内部附件路径", index)
|
||||
}
|
||||
}
|
||||
if protected[0].(map[string]any)["has_attachment"] != true {
|
||||
t.Fatal("存在附件的合同必须返回 has_attachment=true")
|
||||
}
|
||||
if protected[1].(map[string]any)["has_attachment"] != false {
|
||||
t.Fatal("无附件的合同必须返回 has_attachment=false")
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,10 @@ func RegisterDelivery(serviceKey string, engine *gin.Engine) {
|
||||
contract := protected.Group("/gasorder_contract")
|
||||
contract.GET("", deliverylogic.ListContract)
|
||||
contract.POST("", deliverylogic.CreateContract)
|
||||
contract.POST("/attachment/upload", deliverylogic.UploadContractAttachment)
|
||||
contract.POST("/attachment/cleanup", deliverylogic.CleanupContractAttachment)
|
||||
contract.GET("/:identity", deliverylogic.GetContract)
|
||||
contract.GET("/:identity/attachment", deliverylogic.ServeContractAttachment)
|
||||
contract.PUT("/:identity", deliverylogic.UpdateContract)
|
||||
contract.POST("/:identity/activate", deliverylogic.ActivateContract)
|
||||
contract.POST("/:identity/renew", deliverylogic.RenewContract)
|
||||
|
||||
@@ -47,6 +47,9 @@ func TestDeliveryOrderActionBoundary(t *testing.T) {
|
||||
"GET /heqi/delivery/v1/wallet_recharge/:identity",
|
||||
"GET /heqi/delivery/v1/staff_account/:identity/avatar",
|
||||
"GET /heqi/delivery/v1/user_account/:identity/avatar",
|
||||
"POST /heqi/delivery/v1/gasorder_contract/attachment/upload",
|
||||
"POST /heqi/delivery/v1/gasorder_contract/attachment/cleanup",
|
||||
"GET /heqi/delivery/v1/gasorder_contract/:identity/attachment",
|
||||
} {
|
||||
if !routes[required] {
|
||||
t.Fatalf("missing confirmed delivery action %s", required)
|
||||
|
||||
Reference in New Issue
Block a user