oak-pay-business/lib/utils/ship.js

525 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.shipProjection = void 0;
exports.maskPhone = maskPhone;
exports.uploadShippingInfo = uploadShippingInfo;
exports.getOrderShipState = getOrderShipState;
exports.refreshMpOrderShipState = refreshMpOrderShipState;
exports.refreshShipState = refreshShipState;
exports.notifyConfirmReceive = notifyConfirmReceive;
const oak_external_sdk_1 = require("oak-external-sdk");
const assert_1 = require("oak-domain/lib/utils/assert");
const uuid_1 = require("oak-domain/lib/utils/uuid");
const Exception_1 = require("../types/Exception");
const validator_1 = require("oak-domain/lib/utils/validator");
const shipClazz_1 = require("./shipClazz");
exports.shipProjection = {
id: 1,
type: 1,
iState: 1,
entity: 1,
entityId: 1,
extraShipId: 1,
shipService: {
id: 1,
name: 1,
shipCompany: {
id: 1,
name: 1,
abbr: 1,
wechatMpName: 1,
}
},
deposit$ship: {
$entity: 'deposit',
data: {
id: 1,
iState: 1,
accountId: 1,
pay$deposit: {
$entity: 'pay',
data: {
id: 1,
applicationId: 1,
application: {
id: 1,
type: 1,
config: 1,
},
price: 1,
meta: 1,
iState: 1,
entity: 1,
entityId: 1,
},
filter: {
iState: 'paid'
}
}
}
},
shipOrder$ship: {
$entity: 'shipOrder',
data: {
id: 1,
orderId: 1,
order: {
id: 1,
iState: 1,
pay$order: {
$entity: 'pay',
data: {
id: 1,
applicationId: 1,
application: {
id: 1,
type: 1,
config: 1,
},
price: 1,
meta: 1,
iState: 1,
entity: 1,
entityId: 1,
},
filter: {
iState: 'paid'
}
}
}
}
},
from: {
id: 1,
detail: 1,
name: 1,
phone: 1,
area: {
id: 1,
name: 1,
parent: {
id: 1,
name: 1,
parent: {
id: 1,
name: 1,
},
},
},
},
to: {
id: 1,
detail: 1,
name: 1,
phone: 1,
area: {
id: 1,
name: 1,
parent: {
id: 1,
name: 1,
parent: {
id: 1,
name: 1,
},
},
},
},
};
/**
* 手机号掩码(130****0000)
* @param phone
*/
function maskPhone(phone) {
(0, assert_1.assert)((0, validator_1.isMobile)(phone));
const start = phone.slice(3);
const end = phone.slice(-4);
return start + '****' + end;
}
/**
* 小程序发货信息录入
* @param shipInfo
* @param context
* @returns
*/
async function uploadShippingInfo(shipId, context) {
const [ship] = await context.select('ship', {
data: exports.shipProjection,
filter: {
id: shipId,
}
}, { forUpdate: true });
const { deposit$ship: deposits, shipOrder$ship, type: shipType, extraShipId, shipService, from, to } = ship;
// assert(iState === 'unshipped'); //可更改一次发货信息
//发货信息录入前检查小程序订单发货状态
const shipState = await getOrderShipState(context, shipId);
const date = new Date();
const isoString = date.toISOString().replace('Z', ''); // 移除UTC的'Z'
// 手动添加时区偏移(例如 +08:00
const finalFormat = `${isoString}+08:00`;
// const application = context.getApplication();
// const { type, config } = application!;
// assert(type === 'wechatMp');
// const { appId, appSecret } = config as WechatMpConfig;
// const wechatInstance = WechatSDK.getInstance(
// appId,
// 'wechatMp',
// appSecret,
// ) as WechatMpInstance;
if (deposits && deposits.length > 0 && shipState === 'unshipped') {
//充值
(0, assert_1.assert)(deposits.length === 1);
const [deposit] = deposits;
const { pay$deposit: pays, } = deposit;
const pay = pays?.[0];
const meta = pay?.meta;
const shipInfo = {
order_key: {
order_number_type: 2,
transaction_id: meta?.transaction_id,
},
logistics_type: 3,
delivery_mode: 1,
shipping_list: [
{
item_desc: '账户充值',
}
],
upload_time: finalFormat,
payer: {
openid: meta?.payer?.openid,
},
};
const { type, config } = pay?.application;
if (type === 'wechatMp') {
const { appId, appSecret } = config;
const wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatMp', appSecret);
const result = await wechatInstance.uploadShippingInfo(shipInfo);
if (result?.errcode !== 0) {
console.error(JSON.stringify(result));
throw new Exception_1.UploadShipException(result?.message);
}
}
}
else if (shipOrder$ship && shipOrder$ship.length > 0 && shipState && ['unshipped', 'shipping'].includes(shipState)) {
//订单 每笔微信支付调用一次接口
//当已发货的订单再次调用小程序发货信息录入接口视为重新发货(仅可重新发货一次)
const orders = shipOrder$ship.map((ele) => ele.order);
const fromPhoneStr = maskPhone(from.phone);
const toPhoneStr = maskPhone(to.phone);
for (const order of orders) {
const { pay$order, desc } = order;
const wechatPay = pay$order.find((ele) => ele.entity === 'wpProduct');
const meta = wechatPay?.meta;
let shippingList = [];
if (shipType === 'express') {
shippingList = [
{
tracking_no: extraShipId,
express_company: shipService?.shipCompany?.wechatMpName,
item_desc: desc,
contact: {
consignor_contact: fromPhoneStr,
receiver_contact: toPhoneStr,
}
}
];
}
else if (shipType === 'pickup') {
shippingList = [{
item_desc: desc,
}];
}
const shipInfo = {
order_key: {
order_number_type: 2,
transaction_id: meta?.transaction_id,
},
logistics_type: shipType === 'express' ? 1 : 4,
delivery_mode: 1,
shipping_list: shippingList,
upload_time: finalFormat,
payer: {
openid: meta?.payer?.openid,
},
};
const { type, config } = wechatPay?.application;
if (type === 'wechatMp') {
const { appId, appSecret } = config;
const wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatMp', appSecret);
const result = await wechatInstance.uploadShippingInfo(shipInfo);
if (result?.errcode !== 0) {
console.error(JSON.stringify(result));
throw new Exception_1.UploadShipException(result?.message);
}
}
}
}
}
/**
* 获取小程序订单状态
* @param context
* @param shipId
* @returns
*/
async function getOrderShipState(context, shipId) {
const [ship] = await context.select('ship', {
data: exports.shipProjection,
filter: {
id: shipId,
}
}, {
blockTrigger: true,
forUpdate: true
});
(0, assert_1.assert)(ship);
const { deposit$ship: deposits, shipOrder$ship, type } = ship;
let info = undefined, wechatInstance = undefined;
if (deposits && deposits.length > 0) {
//充值
const [deposit] = deposits;
const { pay$deposit: pays, } = deposit;
const pay = pays?.[0];
if (shipId && pay) {
const { config } = pay.application;
const { appId, appSecret } = config;
wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatMp', appSecret);
info = {
transaction_id: pay?.meta?.transaction_id
};
}
}
else if (shipOrder$ship && shipOrder$ship.length > 0) {
const order = shipOrder$ship[0].order;
const { pay$order: pays, } = order;
const pay = pays?.find((ele) => ele.entity === 'wpProduct');
if (shipId && pay) {
const { config } = pay.application;
const { appId, appSecret } = config;
wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatMp', appSecret);
info = {
transaction_id: pay?.meta?.transaction_id
};
}
}
if (info && wechatInstance) {
const result = await wechatInstance.getOrderState(info);
const { orderState, inComplaint } = result;
if (!inComplaint) {
//未处于纠纷中
let state = undefined;
// let action = undefined;
switch (orderState) {
case 1: //待发货
state = 'unshipped';
break;
case 2: //已发货
if (['pickup', 'virtual'].includes(type)) {
state = 'receiving';
}
else {
state = 'shipping';
}
// action = 'ship';
break;
case 3: //确认收货
if (type === 'express') {
state = 'receiving';
}
break;
case 4: //交易完成
state = 'received';
// action = 'receive';
break;
}
return state;
}
}
}
/**
* 刷新小程序订单状态(自动确认收货)
* @param context
* @param shipId
* @returns
*/
async function refreshMpOrderShipState(shipId, context) {
const [ship] = await context.select('ship', {
data: {
id: 1,
iState: 1,
},
filter: {
id: shipId,
}
}, {
blockTrigger: true,
forUpdate: true
});
const mpState = await getOrderShipState(context, shipId);
// let action = undefined;
// switch (mpState) {
// case 'shipping': //已发货
// action = 'ship';
// break;
// case 'receiving': //已收货
// action = 'startReceiving';
// break;
// case 'received': //已确认
// action = 'succeedReceiving';
// break;
// case 'unknow': //不明
// action = 'unknow';
// break;
// default:
// action = undefined;
// }
if (ship.iState !== mpState) {
return await context.operate('ship', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'update',
data: {
iState: mpState,
},
filter: {
id: ship.id,
}
}, {});
}
}
/**
* 刷新ship的物流状态
* @param ship
* @param context
* @returns
*/
async function refreshShipState(shipId, context) {
const ships = await context.select('ship', {
data: exports.shipProjection,
filter: {
id: shipId,
}
}, {
blockTrigger: true,
forUpdate: true,
});
const ship = ships[0];
const { entity, entityId } = ship;
if (entity && entityId) {
const clazz = await (0, shipClazz_1.getShipClazz)(entity, entityId, context);
const { state, time } = await clazz.syncState(ship.id, context);
const wpPayIds = ship.shipOrder$ship?.map((ele) => ele.order).map((order) => order.pay$order).flat().filter((pay) => pay?.entity === 'wpProduct').map((ele) => ele?.id);
let needReceiving = false;
if (wpPayIds && wpPayIds.length > 0) {
const cnt = await context.count('pay', {
filter: {
id: {
$in: wpPayIds,
},
iState: {
$ne: 'closed',
},
wpProduct: {
needReceiving: true,
}
}
}, {});
if (cnt > 0) {
needReceiving = true;
}
}
let action = undefined, updateData = {};
switch (state) {
case 'shipping': //已发货
action = 'ship';
break;
case 'received': //已收货
action = needReceiving ? 'startReceiving' : 'receive';
updateData = {
receiveAt: time
};
break;
case 'unknow': //不明
action = 'unknow';
break;
default:
action = undefined;
}
if (action && ship.iState !== state) {
return await context.operate('ship', {
id: await (0, uuid_1.generateNewIdAsync)(),
action,
data: updateData,
filter: {
id: ship.id,
}
}, {});
}
}
}
/**
* 小程序确认收货提醒
*/
async function notifyConfirmReceive(shipId, context) {
const [ship] = await context.select('ship', {
data: {
id: 1,
receiveAt: 1,
shipOrder$ship: {
$entity: 'shipOrder',
data: {
id: 1,
orderId: 1,
order: {
id: 1,
iState: 1,
pay$order: {
$entity: 'pay',
data: {
id: 1,
applicationId: 1,
application: {
id: 1,
type: 1,
config: 1,
},
price: 1,
meta: 1,
iState: 1,
entity: 1,
entityId: 1,
},
filter: {
iState: 'paid'
}
}
}
}
},
},
filter: {
id: shipId,
}
}, {
blockTrigger: true,
forUpdate: true
});
(0, assert_1.assert)(ship);
const { shipOrder$ship, receiveAt } = ship;
(0, assert_1.assert)(!!receiveAt && shipOrder$ship && shipOrder$ship.length > 0);
const orders = shipOrder$ship.map((ele) => ele.order);
for (const order of orders) {
const wechatPay = order.pay$order.find((ele) => ele.entity === 'wpProduct');
const meta = wechatPay?.meta;
const notifyInfo = {
transaction_id: meta.transaction_id,
received_time: receiveAt.valueOf(),
};
const { type, config } = wechatPay?.application;
if (type === 'wechatMp') {
const { appId, appSecret } = config;
const wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatMp', appSecret);
await wechatInstance.notifyConfirmReceive(notifyInfo);
}
}
}