package delivery import ( "bytes" "encoding/json" "errors" "fmt" "io" "math" "time" "git.apinb.com/bsm-sdk/core/errcode" "git.apinb.com/bsm-sdk/core/infra" "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common" platformgasorder "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform/gasorder" "git.apinb.com/heqiapp/platforms/backend/api/internal/models" "github.com/gin-gonic/gin" "gorm.io/gorm" "gorm.io/gorm/clause" ) func rewriteJSON(ctx *gin.Context, mutate func(map[string]any) bool) bool { body, err := io.ReadAll(ctx.Request.Body) if err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return false } var values map[string]any if json.Unmarshal(body, &values) != nil || !mutate(values) { if !ctx.IsAborted() { infra.Response.Error(ctx, errcode.ErrInvalidArgument) } return false } body, _ = json.Marshal(values) ctx.Request.Body = io.NopCloser(bytes.NewReader(body)) return true } func scopedContract(ctx *gin.Context, identity string, pointID uint64) (models.GasorderContract, bool) { var contract models.GasorderContract if err := common.ActiveRecords(db()).Where("identity = ? AND delivery_basic_id = ?", identity, pointID).First(&contract).Error; err != nil { common.RespondRecordError(ctx, err) return contract, false } return contract, true } 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") } } func GetContract(ctx *gin.Context) { point, _, ok := currentScope(ctx) if ok { if _, valid := scopedContract(ctx, ctx.Param("identity"), point.ID); valid { platformgasorder.GetGasorderContract(ctx) } } } func CreateContract(ctx *gin.Context) { point, station, ok := currentScope(ctx) if !ok { return } if !rewriteJSON(ctx, func(values map[string]any) bool { userIdentity, _ := values["user_account_identity"].(string) if _, _, valid := scopedUser(ctx, userIdentity, point.ID); !valid { return false } values["gas_basic_identity"] = station.Identity values["delivery_basic_identity"] = point.Identity return true }) { return } platformgasorder.CreateGasorderContract(ctx) } func withContract(ctx *gin.Context, handler gin.HandlerFunc) { point, _, ok := currentScope(ctx) if ok { if _, valid := scopedContract(ctx, ctx.Param("identity"), point.ID); valid { handler(ctx) } } } func UpdateContract(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } if _, valid := scopedContract(ctx, ctx.Param("identity"), point.ID); !valid { return } if !rewriteJSON(ctx, func(values map[string]any) bool { values["delivery_basic_identity"] = point.Identity return true }) { return } platformgasorder.UpdateGasorderContract(ctx) } func ActivateContract(ctx *gin.Context) { withContract(ctx, platformgasorder.ActivateGasorderContract) } func RenewContract(ctx *gin.Context) { withContract(ctx, platformgasorder.RenewGasorderContract) } func TerminateContract(ctx *gin.Context) { withContract(ctx, platformgasorder.TerminateGasorderContract) } func ListContractProduct(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } query := common.ActiveRecords(db().Model(&models.GasorderContractProduct{})). Joins("JOIN gasorder_contract ON gasorder_contract.id = gasorder_contract_product.gasorder_contract_id"). Where("gasorder_contract.delivery_basic_id = ?", point.ID) listScoped(ctx, &models.GasorderContractProduct{}, query, "gasorder_contract_product.created_at desc") } func GetContractProduct(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } var item models.GasorderContractProduct query := common.ActiveRecords(db().Model(&models.GasorderContractProduct{})). Select("gasorder_contract_product.*"). Joins("JOIN gasorder_contract ON gasorder_contract.id = gasorder_contract_product.gasorder_contract_id"). Where("gasorder_contract_product.identity = ? AND gasorder_contract.delivery_basic_id = ?", ctx.Param("identity"), point.ID) respondRecord(ctx, query, &item) } func BindContractProduct(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } if !rewriteJSON(ctx, func(values map[string]any) bool { identity, _ := values["gasorder_contract_identity"].(string) _, valid := scopedContract(ctx, identity, point.ID) return valid }) { return } platformgasorder.BindGasorderContractProduct(ctx) } func UnbindContractProduct(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } var count int64 err := db().Model(&models.GasorderContractProduct{}). Joins("JOIN gasorder_contract ON gasorder_contract.id = gasorder_contract_product.gasorder_contract_id"). Where("gasorder_contract_product.identity = ? AND gasorder_contract.delivery_basic_id = ?", ctx.Param("identity"), point.ID). Count(&count).Error if err != nil || count != 1 { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } platformgasorder.UnbindGasorderContractProduct(ctx) } func ListContractRevision(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } query := common.ActiveRecords(db().Model(&models.GasorderContractRevision{})). Joins("JOIN gasorder_contract ON gasorder_contract.id = gasorder_contract_revision.gasorder_contract_id"). Where("gasorder_contract.delivery_basic_id = ?", point.ID) listScoped(ctx, &models.GasorderContractRevision{}, query, "gasorder_contract_revision.created_at desc") } func GetContractRevision(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } var item models.GasorderContractRevision query := common.ActiveRecords(db().Model(&models.GasorderContractRevision{})). Select("gasorder_contract_revision.*"). Joins("JOIN gasorder_contract ON gasorder_contract.id = gasorder_contract_revision.gasorder_contract_id"). Where("gasorder_contract_revision.identity = ? AND gasorder_contract.delivery_basic_id = ?", ctx.Param("identity"), point.ID) respondRecord(ctx, query, &item) } func ListProductCandidate(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } query := common.ActiveRecords(db().Model(&models.ProductInfo{})). Joins("JOIN user_service_relation ON user_service_relation.user_account_id = product_info.user_account_id AND user_service_relation.status <> ?", common.StatusArchived).Where("user_service_relation.delivery_basic_id = ?", point.ID) listScoped(ctx, &models.ProductInfo{}, query, "product_info.created_at desc") } func GetProductCandidate(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } var item models.ProductInfo query := common.ActiveRecords(db().Model(&models.ProductInfo{})). Select("product_info.*"). Joins("JOIN user_service_relation ON user_service_relation.user_account_id = product_info.user_account_id AND user_service_relation.status <> ?", common.StatusArchived). Where("product_info.identity = ? AND user_service_relation.delivery_basic_id = ?", ctx.Param("identity"), point.ID) respondRecord(ctx, query, &item) } func scopedOrder(ctx *gin.Context, identity string, pointID uint64) (models.GasorderBasic, bool) { var order models.GasorderBasic if err := common.ActiveRecords(db()).Where("identity = ? AND delivery_basic_id = ?", identity, pointID).First(&order).Error; err != nil { common.RespondRecordError(ctx, err) return order, false } return order, true } func ListOrder(ctx *gin.Context) { point, _, ok := currentScope(ctx) if ok { listScoped(ctx, &models.GasorderBasic{}, common.ActiveRecords(db().Model(&models.GasorderBasic{})). Where("delivery_basic_id = ?", point.ID), "gasorder_basic.created_at desc") } } func GetOrder(ctx *gin.Context) { point, _, ok := currentScope(ctx) if ok { if _, valid := scopedOrder(ctx, ctx.Param("identity"), point.ID); valid { platformgasorder.GetGasorderBasic(ctx) } } } func CreateOrder(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } if !rewriteJSON(ctx, func(values map[string]any) bool { identity, _ := values["gasorder_contract_identity"].(string) if _, valid := scopedContract(ctx, identity, point.ID); !valid { return false } values["creator_type"] = "delivery" values["creator_identity"] = point.Identity return true }) { return } platformgasorder.CreateGasorderBasic(ctx) } func AssignOrder(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } if _, valid := scopedOrder(ctx, ctx.Param("identity"), point.ID); !valid { return } if !rewriteJSON(ctx, func(values map[string]any) bool { staffIdentity, _ := values["staff_account_identity"].(string) staff, valid := scopedStaff(ctx, staffIdentity, point) if !valid || staff.WorkStatus != "on_duty" || staff.Status != common.StatusEnable { return false } var credentialCount int64 err := common.ActiveRecords(db().Model(&models.StaffCredential{})). Where("staff_account_id = ? AND (expired_at IS NULL OR expired_at > ?)", staff.ID, time.Now()). Count(&credentialCount).Error if err != nil || credentialCount == 0 { return false } values["delivery_basic_identity"] = point.Identity return true }) { return } platformgasorder.AssignGasorderBasic(ctx) } func withOrder(ctx *gin.Context, handler gin.HandlerFunc) { point, _, ok := currentScope(ctx) if ok { if _, valid := scopedOrder(ctx, ctx.Param("identity"), point.ID); valid { handler(ctx) } } } func OrderException(ctx *gin.Context) { withOrder(ctx, platformgasorder.GasorderException) } func OrderRecover(ctx *gin.Context) { withOrder(ctx, platformgasorder.GasorderRecover) } func OrderCancel(ctx *gin.Context) { withOrder(ctx, platformgasorder.GasorderCancel) } func ReclaimOrder(ctx *gin.Context) { account, point, _, ok := CurrentDeliveryAccount(ctx) if !ok { return } var request struct { Reason string `json:"reason" binding:"required,max=1000"` } if err := ctx.ShouldBindJSON(&request); err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } err := db().Transaction(func(tx *gorm.DB) error { var order models.GasorderBasic if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). Where("identity = ? AND delivery_basic_id = ?", ctx.Param("identity"), point.ID).First(&order).Error; err != nil { return err } if order.OrderStatus != common.StatusAssigned { return errors.New("only assigned order can be reclaimed") } if err := tx.Model(&order).Updates(map[string]any{ "staff_account_id": 0, "order_status": common.StatusCreated, }).Error; err != nil { return err } return tx.Create(&models.GasorderStatus{ Entity: common.NewEntity(common.StatusEnable), GasorderBasicID: order.ID, FromStatus: common.StatusAssigned, ToStatus: common.StatusCreated, OperatorIdentity: account.Identity, OperatorName: account.DisplayName, OccurredAt: time.Now(), Reason: request.Reason, }).Error }) if err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } infra.Response.Success(ctx, gin.H{"updated": true, "order_status": common.StatusCreated}) } func AdjustOrderAmount(ctx *gin.Context) { point, _, ok := currentScope(ctx) if !ok { return } var request struct { DeliveryFee int64 `json:"delivery_fee"` DiscountAmount int64 `json:"discount_amount"` Reason string `json:"reason" binding:"required,max=1000"` } if err := ctx.ShouldBindJSON(&request); err != nil || request.DeliveryFee < 0 || request.DiscountAmount < 0 { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } account, _, _, _ := CurrentDeliveryAccount(ctx) err := db().Transaction(func(tx *gorm.DB) error { var order models.GasorderBasic if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). Where("identity = ? AND delivery_basic_id = ?", ctx.Param("identity"), point.ID).First(&order).Error; err != nil { return err } if order.OrderStatus != common.StatusCreated && order.OrderStatus != common.StatusAssigned { return errors.New("order amount cannot be adjusted") } var paymentCount int64 if err := tx.Model(&models.GasorderPayment{}). Joins("JOIN payment_order ON payment_order.id = gasorder_payment.payment_order_id"). Where("gasorder_payment.gasorder_basic_id = ? AND payment_order.payment_status = ?", order.ID, 23). Count(&paymentCount).Error; err != nil || paymentCount > 0 { return errors.New("paid order cannot be adjusted") } if order.ProductAmount > math.MaxInt64-request.DeliveryFee { return errors.New("payable amount overflow") } payable := order.ProductAmount + request.DeliveryFee - request.DiscountAmount if payable <= 0 { return errors.New("payable amount must be positive") } if err := tx.Model(&order).Updates(map[string]any{ "delivery_fee": request.DeliveryFee, "discount_amount": request.DiscountAmount, "payable_amount": payable, }).Error; err != nil { return err } record := models.GasorderStatus{ Entity: common.NewEntity(common.StatusEnable), GasorderBasicID: order.ID, FromStatus: order.OrderStatus, ToStatus: order.OrderStatus, OperatorIdentity: account.Identity, OperatorName: account.DisplayName, OccurredAt: time.Now(), Reason: fmt.Sprintf("%s;配送费 %d→%d,优惠金额 %d→%d,应付金额 %d→%d", request.Reason, order.DeliveryFee, request.DeliveryFee, order.DiscountAmount, request.DiscountAmount, order.PayableAmount, payable), } return tx.Create(&record).Error }) if err != nil { infra.Response.Error(ctx, errcode.ErrInvalidArgument) return } infra.Response.Success(ctx, gin.H{"updated": true}) }