333 lines
11 KiB
Go
333 lines
11 KiB
Go
package model
|
|
|
|
import (
|
|
"backend/sdk/ship/model/base"
|
|
util "backend/util"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
OrderReissueStatusPending = 1
|
|
OrderReissueStatusApproved = 2
|
|
OrderReissueStatusRejected = 3
|
|
)
|
|
|
|
type OrderReissueAudit struct {
|
|
AuditId int `json:"audit_id" db:"id"`
|
|
Uid int `json:"Uid" db:"uid"`
|
|
AppId int `json:"AppId" db:"app_id"`
|
|
ServerId int `json:"ServerId" db:"server_id"`
|
|
OrderId string `json:"OrderId" db:"order_id"`
|
|
ThirdPartyOrderId string `json:"ThirdPartyOrderId" db:"third_party_order_id"`
|
|
Reason string `json:"Reason" db:"reason"`
|
|
Price float64 `json:"Price" db:"price"`
|
|
ProductId int `json:"ProductId" db:"product_id"`
|
|
PayStatus int `json:"PayStatus" db:"pay_status"`
|
|
Applicant string `json:"applicant" db:"applicant"`
|
|
Reviewer string `json:"reviewer" db:"reviewer"`
|
|
ReviewRemark string `json:"review_remark" db:"review_remark"`
|
|
Status int `json:"status" db:"status"`
|
|
CreateTime int64 `json:"create_time" db:"create_time"`
|
|
ReviewTime int64 `json:"review_time" db:"review_time"`
|
|
OriginalPayTime int64 `json:"original_pay_time" db:"original_pay_time"`
|
|
OriginalChannelRef string `json:"original_channel_order_id" db:"original_channel_order_id"`
|
|
}
|
|
|
|
func (o *OrderReissueAudit) Apply() error {
|
|
o.OrderId = strings.TrimSpace(o.OrderId)
|
|
o.ThirdPartyOrderId = strings.TrimSpace(o.ThirdPartyOrderId)
|
|
o.Reason = strings.TrimSpace(o.Reason)
|
|
if o.Uid <= 0 {
|
|
return fmt.Errorf("Uid 不能为空")
|
|
}
|
|
if o.OrderId == "" {
|
|
return fmt.Errorf("订单号不能为空")
|
|
}
|
|
if o.ThirdPartyOrderId == "" {
|
|
return fmt.Errorf("第三方订单号不能为空")
|
|
}
|
|
if o.Reason == "" {
|
|
return fmt.Errorf("补单理由不能为空")
|
|
}
|
|
if o.AppId <= 0 {
|
|
o.AppId = o.Uid / 100000000
|
|
}
|
|
if o.ServerId <= 0 {
|
|
o.ServerId = 1
|
|
}
|
|
|
|
order, err := o.loadSourceOrder()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch order.PayStatus {
|
|
case 0, 3:
|
|
// 允许以下两类补单:
|
|
// 1. 未支付状态但玩家实际已支付,需要先手动置为已支付再发货。
|
|
// 2. 已发货状态但玩家未实际收到,需要回退到已支付后重新发货。
|
|
case 1:
|
|
return fmt.Errorf("订单已支付,无需补单")
|
|
case 2:
|
|
return fmt.Errorf("订单支付失败,不能补单")
|
|
default:
|
|
return fmt.Errorf("当前订单状态不支持补单")
|
|
}
|
|
|
|
auditDb := util.MPool.GetGameDB()
|
|
if auditDb == nil {
|
|
return fmt.Errorf("failed to get audit db")
|
|
}
|
|
defer auditDb.Close()
|
|
if err := ensureOrderReissueAuditTable(auditDb); err != nil {
|
|
return err
|
|
}
|
|
|
|
var exists int
|
|
if err := auditDb.QueryRow("SELECT COUNT(*) FROM order_reissue_audit WHERE order_id = ?", o.OrderId).Scan(&exists); err != nil {
|
|
return fmt.Errorf("failed to query order reissue audit: %v", err)
|
|
}
|
|
if exists > 0 {
|
|
return fmt.Errorf("该订单已提交过补单申请,不能重复补单")
|
|
}
|
|
|
|
o.Price = order.Price
|
|
o.ProductId = order.ProductId
|
|
o.PayStatus = order.PayStatus
|
|
o.OriginalPayTime = int64(order.PayTime)
|
|
o.OriginalChannelRef = order.PayChannelOrderId
|
|
o.Status = OrderReissueStatusPending
|
|
o.CreateTime = util.Now()
|
|
|
|
_, err = auditDb.Exec(
|
|
"INSERT INTO order_reissue_audit (`uid`, `app_id`, `server_id`, `order_id`, `third_party_order_id`, `reason`, `price`, `product_id`, `pay_status`, `applicant`, `reviewer`, `review_remark`, `status`, `create_time`, `review_time`, `original_pay_time`, `original_channel_order_id`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '', '', ?, ?, 0, ?, ?)",
|
|
o.Uid,
|
|
o.AppId,
|
|
o.ServerId,
|
|
o.OrderId,
|
|
o.ThirdPartyOrderId,
|
|
o.Reason,
|
|
o.Price,
|
|
o.ProductId,
|
|
o.PayStatus,
|
|
o.Applicant,
|
|
o.Status,
|
|
o.CreateTime,
|
|
o.OriginalPayTime,
|
|
o.OriginalChannelRef,
|
|
)
|
|
if err != nil {
|
|
if strings.Contains(strings.ToLower(err.Error()), "duplicate") {
|
|
return fmt.Errorf("该订单已提交过补单申请,不能重复补单")
|
|
}
|
|
return fmt.Errorf("failed to create order reissue audit: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (o *OrderReissueAudit) Approve() error {
|
|
auditOrder, auditDb, err := o.getAuditForUpdate()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer auditDb.Close()
|
|
|
|
order, err := auditOrder.loadSourceOrder()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := auditOrder.prepareSourceOrderForShipping(order); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := base.ShipOrder(base.Param{
|
|
Uid: auditOrder.Uid,
|
|
AppId: auditOrder.AppId,
|
|
ServerId: auditOrder.ServerId,
|
|
OrderId: auditOrder.OrderId,
|
|
ChannelOrderId: auditOrder.ThirdPartyOrderId,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = auditDb.Exec(
|
|
"UPDATE order_reissue_audit SET `status` = ?, `reviewer` = ?, `review_remark` = ?, `review_time` = ? WHERE `id` = ?",
|
|
OrderReissueStatusApproved,
|
|
o.Reviewer,
|
|
o.ReviewRemark,
|
|
util.Now(),
|
|
o.AuditId,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update order reissue audit: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (o *OrderReissueAudit) prepareSourceOrderForShipping(order *Order) error {
|
|
switch order.PayStatus {
|
|
case 0:
|
|
return o.updateSourceOrderPaidState(true)
|
|
case 1:
|
|
return o.updateSourceOrderPaidState(false)
|
|
case 3:
|
|
return o.updateSourceOrderPaidState(false)
|
|
case 2:
|
|
return fmt.Errorf("订单支付失败,不能补单")
|
|
default:
|
|
return fmt.Errorf("当前订单状态不支持补单")
|
|
}
|
|
}
|
|
|
|
func (o *OrderReissueAudit) Reject() error {
|
|
_, auditDb, err := o.getAuditForUpdate()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer auditDb.Close()
|
|
|
|
_, err = auditDb.Exec(
|
|
"UPDATE order_reissue_audit SET `status` = ?, `reviewer` = ?, `review_remark` = ?, `review_time` = ? WHERE `id` = ?",
|
|
OrderReissueStatusRejected,
|
|
o.Reviewer,
|
|
o.ReviewRemark,
|
|
util.Now(),
|
|
o.AuditId,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to reject order reissue audit: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func loadOrderReissueAuditMap(uid int) (map[string]*OrderReissueAudit, error) {
|
|
auditDb := util.MPool.GetGameDB()
|
|
if auditDb == nil {
|
|
return nil, fmt.Errorf("failed to get audit db")
|
|
}
|
|
defer auditDb.Close()
|
|
if err := ensureOrderReissueAuditTable(auditDb); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var auditOrders []*OrderReissueAudit
|
|
if err := auditDb.Select(&auditOrders, "SELECT `id`, `uid`, `app_id`, `server_id`, `order_id`, `third_party_order_id`, `reason`, `price`, `product_id`, `pay_status`, `applicant`, `reviewer`, `review_remark`, `status`, `create_time`, `review_time`, `original_pay_time`, `original_channel_order_id` FROM order_reissue_audit WHERE uid = ? ORDER BY create_time DESC, id DESC", uid); err != nil {
|
|
return nil, fmt.Errorf("failed to query order reissue audits: %v", err)
|
|
}
|
|
|
|
result := make(map[string]*OrderReissueAudit, len(auditOrders))
|
|
for _, auditOrder := range auditOrders {
|
|
result[auditOrder.OrderId] = auditOrder
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (o *OrderReissueAudit) loadSourceOrder() (*Order, error) {
|
|
if o.AppId <= 0 {
|
|
o.AppId = o.Uid / 100000000
|
|
}
|
|
if o.ServerId <= 0 {
|
|
o.ServerId = 1
|
|
}
|
|
appConfig, err := util.GetAppConfig(o.AppId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
db := util.MPool.GetMysqlDB(appConfig, o.ServerId)
|
|
if db == nil {
|
|
return nil, fmt.Errorf("failed to get mysql database")
|
|
}
|
|
defer db.Close()
|
|
|
|
var order Order
|
|
err = db.Get(&order, "SELECT `id`, `Uid`, `ProductId`, `Price`, `CreateTime`, `PayTime`, `PayStatus`, `PayChannelOrderId`, `OrderId`, `PayChannelExtra` FROM t_player_charge WHERE Uid = ? AND OrderId = ? LIMIT 1", o.Uid, o.OrderId)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("未找到订单信息: %v", err)
|
|
}
|
|
order.AppId = o.AppId
|
|
order.ServerId = o.ServerId
|
|
return &order, nil
|
|
}
|
|
|
|
func (o *OrderReissueAudit) updateSourceOrderPaidState(refreshPayTime bool) error {
|
|
appConfig, err := util.GetAppConfig(o.AppId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
db := util.MPool.GetMysqlDB(appConfig, o.ServerId)
|
|
if db == nil {
|
|
return fmt.Errorf("failed to get mysql database")
|
|
}
|
|
defer db.Close()
|
|
|
|
if refreshPayTime {
|
|
_, err = db.Exec("UPDATE t_player_charge SET `PayStatus` = 1, `PayTime` = ?, `PayChannelOrderId` = ? WHERE `Uid` = ? AND `OrderId` = ?", util.Now(), o.ThirdPartyOrderId, o.Uid, o.OrderId)
|
|
} else {
|
|
_, err = db.Exec("UPDATE t_player_charge SET `PayStatus` = 1, `PayChannelOrderId` = ? WHERE `Uid` = ? AND `OrderId` = ?", o.ThirdPartyOrderId, o.Uid, o.OrderId)
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update order paid state: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (o *OrderReissueAudit) getAuditForUpdate() (*OrderReissueAudit, *util.Db, error) {
|
|
if o.AuditId <= 0 {
|
|
return nil, nil, fmt.Errorf("audit_id is required")
|
|
}
|
|
auditDb := util.MPool.GetGameDB()
|
|
if auditDb == nil {
|
|
return nil, nil, fmt.Errorf("failed to get audit db")
|
|
}
|
|
if err := ensureOrderReissueAuditTable(auditDb); err != nil {
|
|
auditDb.Close()
|
|
return nil, nil, err
|
|
}
|
|
|
|
var auditOrder OrderReissueAudit
|
|
err := auditDb.Get(&auditOrder, "SELECT `id`, `uid`, `app_id`, `server_id`, `order_id`, `third_party_order_id`, `reason`, `price`, `product_id`, `pay_status`, `applicant`, `reviewer`, `review_remark`, `status`, `create_time`, `review_time`, `original_pay_time`, `original_channel_order_id` FROM order_reissue_audit WHERE id = ?", o.AuditId)
|
|
if err != nil {
|
|
auditDb.Close()
|
|
return nil, nil, fmt.Errorf("failed to get order reissue audit: %v", err)
|
|
}
|
|
if auditOrder.Status != OrderReissueStatusPending {
|
|
auditDb.Close()
|
|
if auditOrder.Status == OrderReissueStatusApproved {
|
|
return nil, nil, fmt.Errorf("补单申请已审核通过")
|
|
}
|
|
return nil, nil, fmt.Errorf("补单申请已被驳回")
|
|
}
|
|
return &auditOrder, auditDb, nil
|
|
}
|
|
|
|
func ensureOrderReissueAuditTable(db *util.Db) error {
|
|
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS order_reissue_audit (
|
|
id INT NOT NULL AUTO_INCREMENT,
|
|
uid BIGINT NOT NULL DEFAULT 0,
|
|
app_id INT NOT NULL DEFAULT 0,
|
|
server_id INT NOT NULL DEFAULT 0,
|
|
order_id VARCHAR(128) NOT NULL DEFAULT '',
|
|
third_party_order_id VARCHAR(128) NOT NULL DEFAULT '',
|
|
reason VARCHAR(500) NOT NULL DEFAULT '',
|
|
price DECIMAL(10, 2) NOT NULL DEFAULT 0,
|
|
product_id INT NOT NULL DEFAULT 0,
|
|
pay_status INT NOT NULL DEFAULT 0,
|
|
applicant VARCHAR(100) NOT NULL DEFAULT '',
|
|
reviewer VARCHAR(100) NOT NULL DEFAULT '',
|
|
review_remark VARCHAR(500) NOT NULL DEFAULT '',
|
|
status INT NOT NULL DEFAULT 1,
|
|
create_time BIGINT NOT NULL DEFAULT 0,
|
|
review_time BIGINT NOT NULL DEFAULT 0,
|
|
original_pay_time BIGINT NOT NULL DEFAULT 0,
|
|
original_channel_order_id VARCHAR(128) NOT NULL DEFAULT '',
|
|
PRIMARY KEY (id),
|
|
UNIQUE KEY uk_order_reissue_order_id (order_id),
|
|
KEY idx_order_reissue_uid (uid),
|
|
KEY idx_order_reissue_status (status)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to ensure order_reissue_audit table: %v", err)
|
|
}
|
|
return nil
|
|
}
|