diff --git a/es/aspects/AspectDict.d.ts b/es/aspects/AspectDict.d.ts index 01c6e005..b9b3c574 100644 --- a/es/aspects/AspectDict.d.ts +++ b/es/aspects/AspectDict.d.ts @@ -7,7 +7,6 @@ export type AspectDict = { withdrawAccountId?: string; }, context: BackendRuntimeContext) => Promise; getMpShipState: (params: { - depositId?: string; - orderId?: string; + shipId: string; }, context: BackendRuntimeContext) => Promise; }; diff --git a/es/aspects/ship.d.ts b/es/aspects/ship.d.ts index 4be4fe0a..6c5128ce 100644 --- a/es/aspects/ship.d.ts +++ b/es/aspects/ship.d.ts @@ -5,6 +5,5 @@ import { BRC } from '../types/RuntimeCxt'; * @param context */ export declare function getMpShipState(params: { - depositId?: string; - orderId?: string; + shipId: string; }, context: BRC): Promise; diff --git a/es/aspects/ship.js b/es/aspects/ship.js index 917ddf67..3f29addb 100644 --- a/es/aspects/ship.js +++ b/es/aspects/ship.js @@ -8,30 +8,8 @@ export async function getMpShipState(params, context) { const application = context.getApplication(); const { type, } = application; if (type === 'wechatMp') { - const { depositId, orderId } = params; - if (depositId) { - //充值 - const deposits = await context.select('deposit', { - data: { - id: 1, - iState: 1, - shipId: 1, - ship: { - id: 1, - iState: 1, - }, - }, - filter: { - id: depositId, - } - }, {}); - const deposit = deposits[0]; - if (deposit) { - const shipState = await getShipState(context, deposit); - return shipState; - } - } - if (orderId) { - } + const { shipId } = params; + const shipState = await getShipState(context, shipId); + return shipState; } } diff --git a/es/components/order/pay/index.js b/es/components/order/pay/index.js index 26bd7a1c..721793a8 100644 --- a/es/components/order/pay/index.js +++ b/es/components/order/pay/index.js @@ -24,10 +24,10 @@ export default OakComponent({ }, isList: false, properties: { - accountId: '', // 是否可以使用帐户中的余额抵扣 - accountAvailMax: 0, // 本次交易可以使用的帐户中的Avail max值,调用者自己保证此数值的一致性,不要扣成负数 + accountId: '', + accountAvailMax: 0, onSetPays: (pays) => undefined, - accountTips: '', // 使用余额支付的提示说明 + accountTips: '', autoStartPay: false, }, formData({ data }) { diff --git a/es/components/pay/detail/index.js b/es/components/pay/detail/index.js index 6d58efb9..d96c2077 100644 --- a/es/components/pay/detail/index.js +++ b/es/components/pay/detail/index.js @@ -369,7 +369,7 @@ export default OakComponent({ async success() { console.log('success'); const mpShipState = await this.features.cache.exec('getMpShipState', { - depositId, + shipId: next.shipId, }); if (mpShipState === 'received') { this.setMessage({ diff --git a/es/triggers/ship.js b/es/triggers/ship.js index ffb55237..62bb1a3c 100644 --- a/es/triggers/ship.js +++ b/es/triggers/ship.js @@ -59,7 +59,7 @@ const triggers = [ } }, { - // todo,应该是所有的ship + // todo,deposit的ship和 shipClazz中有配置wechatShip且可获取openId的order的ship entity: 'ship', name: '当虚拟的ship变为shipping状态时,调用小程序发货信息录入接口', action: 'ship', @@ -77,21 +77,45 @@ const triggers = [ data: { id: 1, }, - indexFrom: 0, - count: 1, + }, + entity: 1, + entityId: 1, + shipServiceId: 1, + shipOrder$ship: { + $entity: 'shipOrder', + data: { + id: 1, + orderId: 1, + } + }, + wechatMpShip: { + id: 1, + applicationId: 1, } }, filter, }, {}); - const { type, deposit$ship: deposits } = ship || {}; - if (type === 'virtual') { - const deposit = deposits?.[0]; - //当已发货的订单再次调用小程序发货信息录入接口视为重新发货(仅可重新发货一次) + const { id: shipId, type, deposit$ship: deposits, shipOrder$ship, shipServiceId, entity, entityId, wechatMpShip } = ship || {}; + if (deposits && deposits.length > 0) { + //充值 (此时该充值必定为受发货限制的小程序上的充值) //发货前先查询,检查是否为未同步微信端发货状态 - const shipState = await getShipState(context, deposit); + const shipState = await getShipState(context, shipId); if (shipState === 'unshipped') { - const result = await uploadShippingInfo({ depositId: deposit?.id, }, context); - if (result) { + await uploadShippingInfo(shipId, context); + return 1; + } + } + if (shipOrder$ship && shipOrder$ship.length > 0) { + //订单 + const clazz = await getShipClazz(entity, entityId, context); + const { openId } = await clazz.getReceiverInfo(shipOrder$ship.map((ele) => ele.orderId), wechatMpShip?.applicationId, context); + if (openId) { + //当存在openId时调用小程序发货信息录入 + const shipState = await getShipState(context, shipId); + //当已发货的订单再次调用小程序发货信息录入接口视为重新发货(仅可重新发货一次) + if (shipState && ['unshipped', 'shipping'].includes(shipState)) { + // 发货/更换发货 + await uploadShippingInfo(shipId, context); return 1; } } @@ -203,7 +227,7 @@ const triggers = [ if (packaged.length > 0) { await context.operate('order', { id: await generateNewIdAsync(), - action: 'ship', + action: 'send', data: {}, filter: { id: { @@ -249,33 +273,16 @@ const triggers = [ } } }, { dontCollect: true }); - const packaged = orders.filter(ele => ele.receivingMethod === 'express'); - if (packaged.length > 0) { - await context.operate('order', { - id: await generateNewIdAsync(), - action: 'turnBack', - data: {}, - filter: { - id: { - $in: packaged.map(ele => ele.id), - }, + await context.operate('order', { + id: await generateNewIdAsync(), + action: 'unship', + data: {}, + filter: { + id: { + $in: orders.map(ele => ele.id), }, - }, option); - } - const staged = orders.filter(ele => ele.receivingMethod === 'pickup'); - if (staged.length > 0) { - await context.operate('order', { - id: await generateNewIdAsync(), - action: 'cancelTaking', - data: {}, - filter: { - id: { - $in: staged.map(ele => ele.id), - }, - }, - }, option); - } - assert(staged.length + packaged.length === orders.length); + }, + }, option); return orders.length; } }, diff --git a/es/types/ShipClazz.d.ts b/es/types/ShipClazz.d.ts index 58dbfa86..ec8d2163 100644 --- a/es/types/ShipClazz.d.ts +++ b/es/types/ShipClazz.d.ts @@ -2,6 +2,10 @@ import { EntityDict } from '../oak-app-domain'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity'; import BackendRuntimeContext from '../context/BackendRuntimeContext'; export default interface ShipClazz> { + getReceiverInfo(orderIds: string[], applicationId: string, context: Context): Promise<{ + openId?: string; + appWxId?: string; + }>; available(shipServiceId: string, orderIds: string[], context: Context): Promise; eOrder(shipId: string, context: Context): Promise; cancelOrder(shipId: string, context: Context): Promise; diff --git a/es/utils/ship.d.ts b/es/utils/ship.d.ts index b9ff2eae..538b7100 100644 --- a/es/utils/ship.d.ts +++ b/es/utils/ship.d.ts @@ -1,16 +1,18 @@ import { EntityDict } from '../oak-app-domain'; import { BRC } from '../types/RuntimeCxt'; -export declare const fullShipProjection: EntityDict['ship']['Projection']; +export declare const shipProjection: EntityDict['ship']['Projection']; +/** + * 手机号掩码(130****0000) + * @param phone + */ +export declare function maskPhone(phone: string): string; /** * 小程序发货信息录入 * @param shipInfo * @param context * @returns */ -export declare function uploadShippingInfo(param: { - depositId?: string; - orderId?: string; -}, context: BRC): Promise; +export declare function uploadShippingInfo(shipId: string, context: BRC): Promise; /** * 获取小程序物流状态 * @param context @@ -18,7 +20,7 @@ export declare function uploadShippingInfo(param: { * @param order * @returns */ -export declare function getShipState(context: BRC, deposit?: EntityDict['deposit']['Schema'], order?: EntityDict['order']['Schema']): Promise; +export declare function getShipState(context: BRC, shipId: string): Promise; /**刷新shipping的ship的小程序物流状态 * @param ship * @param context diff --git a/es/utils/ship.js b/es/utils/ship.js index 33a1e3fc..fe6c2f13 100644 --- a/es/utils/ship.js +++ b/es/utils/ship.js @@ -3,10 +3,14 @@ import { assert } from 'oak-domain/lib/utils/assert'; import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid'; import { UploadShipException } from '../types/Exception'; import dayjs from 'dayjs'; -export const fullShipProjection = { +import { isMobile } from 'oak-domain/lib/utils/validator'; +export const shipProjection = { id: 1, type: 1, iState: 1, + entity: 1, + entityId: 1, + extraShipId: 1, shipService: { id: 1, name: 1, @@ -17,7 +21,6 @@ export const fullShipProjection = { wechatMpName: 1, } }, - serial: 1, deposit$ship: { $entity: 'deposit', data: { @@ -39,6 +42,9 @@ export const fullShipProjection = { iState: 1, entity: 1, entityId: 1, + }, + filter: { + iState: 'paid' } } } @@ -66,75 +72,90 @@ export const fullShipProjection = { 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 + */ +export function maskPhone(phone) { + assert(isMobile(phone)); + const start = phone.slice(3); + const end = phone.slice(-4); + return start + '****' + end; +} /** * 小程序发货信息录入 * @param shipInfo * @param context * @returns */ -export async function uploadShippingInfo(param, context) { - const { depositId, orderId } = param; - if (depositId) { - const [deposit] = await context.select('deposit', { - data: { - id: 1, - iState: 1, - shipId: 1, - ship: { - id: 1, - iState: 1, - type: 1, - }, - pay$deposit: { - $entity: 'pay', - data: { - id: 1, - entity: 1, - entityId: 1, - meta: 1, - applicationId: 1, - }, - filter: { - iState: 'paid', - }, - indexFrom: 0, - count: 1, - }, - }, - filter: { - id: depositId, - } - }, { - blockTrigger: true, - forUpdate: true, - }); - const { pay$deposit: pays, } = deposit || {}; +export async function uploadShippingInfo(shipId, context) { + const [ship] = await context.select('ship', { + data: 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 getShipState(context, shipId); + const now = dayjs().format(); + const application = context.getApplication(); + const { type, config } = application; + assert(type === 'wechatMp'); + const { appId, appSecret } = config; + const wechatInstance = WechatSDK.getInstance(appId, 'wechatMp', appSecret); + if (deposits && deposits.length > 0 && shipState === 'unshipped') { + //充值 + assert(deposits.length === 1); + const [deposit] = deposits; + const { pay$deposit: pays, } = deposit; const pay = pays?.[0]; - const applicationId = pay?.applicationId; - assert(applicationId); - const [application] = await context.select('application', { - data: { - id: 1, - type: 1, - config: 1. - }, - filter: { - id: applicationId, - } - }, { - blockTrigger: true, - forUpdate: true, - }); - const { type, config } = application; - assert(type === 'wechatMp'); - const { appId, appSecret } = config; - const wechatInstance = WechatSDK.getInstance(appId, 'wechatMp', appSecret); const meta = pay?.meta; const shipInfo = { order_key: { @@ -148,22 +169,64 @@ export async function uploadShippingInfo(param, context) { item_desc: '账户充值', } ], - upload_time: dayjs().format(), + upload_time: now, payer: { openid: meta?.payer?.openid, }, }; const result = await wechatInstance.uploadShippingInfo(shipInfo); - if (result?.errcode === 0) { - return true; - } - else { + if (result?.errcode !== 0) { console.error(JSON.stringify(result)); throw new UploadShipException(result?.message); } } - if (orderId) { - //订单 + 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, + }, + logistic_type: shipType === 'express' ? 1 : 4, + delivery_mode: 1, + shipping_list: shippingList, + upload_time: now, + payer: { + openid: meta?.payer?.openid, + }, + }; + const result = await wechatInstance.uploadShippingInfo(shipInfo); + if (result?.errcode !== 0) { + console.error(JSON.stringify(result)); + throw new UploadShipException(result?.message); + } + } } } /** @@ -173,89 +236,67 @@ export async function uploadShippingInfo(param, context) { * @param order * @returns */ -export async function getShipState(context, deposit, order) { +export async function getShipState(context, shipId) { + const [ship] = await context.select('ship', { + data: shipProjection, + filter: { + id: shipId, + } + }, { + blockTrigger: true, + forUpdate: true + }); + assert(ship); + const { deposit$ship: deposits, shipOrder$ship } = ship; const application = context.getApplication(); const { type, config } = application; assert(type === 'wechatMp'); const { appId, appSecret } = config; const wechatInstance = WechatSDK.getInstance(appId, 'wechatMp', appSecret); - if (deposit) { + let info = undefined; + if (deposits && deposits.length > 0) { //充值 - let deposit2 = deposit; - const deposits = await context.select('deposit', { - data: { - id: 1, - iState: 1, - pay$deposit: { - $entity: 'pay', - data: { - id: 1, - iState: 1, - meta: 1, - entity: 1, - entityId: 1, - }, - filter: { - iState: 'paid', - }, - indexFrom: 0, - count: 1, - }, - shipId: 1, - ship: { - id: 1, - type: 1, - iState: 1, - } - }, - filter: { - id: deposit?.id, - } - }, { - blockTrigger: true, - forUpdate: true, - }); - deposit2 = deposits[0]; - const { pay$deposit: pays, ship, shipId } = deposit2; + const [deposit] = deposits; + const { pay$deposit: pays, } = deposit; const pay = pays?.[0]; if (shipId && pay) { - const info = { + info = { transaction_id: pay?.meta?.transaction_id }; - 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: //已发货 - state = 'shipping'; - // action = 'ship'; - break; - case 3: //确认收货 - state = 'received'; - // action = 'receive'; - break; - } - // if (action && ship?.iState !== state) { - // await context.operate('ship', { - // id: await generateNewIdAsync(), - // action, - // data: {}, - // filter: { - // id: shipId, - // } - // }, {}); - // } - return state; - } } } - if (order) { + 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) { + info = { + transaction_id: pay?.meta?.transaction_id + }; + } + } + if (info) { + 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: //已发货 + state = 'shipping'; + // action = 'ship'; + break; + case 3: //确认收货 + state = 'received'; + // action = 'receive'; + break; + } + return state; + } } } /**刷新shipping的ship的小程序物流状态 @@ -265,9 +306,9 @@ export async function getShipState(context, deposit, order) { */ export async function refreshtShipState(ship, context) { let ship2 = ship; - if (!ship2.iState || (!ship2.deposit$ship && !ship2.shipOrder$ship)) { + if (!ship2.iState) { const ships = await context.select('ship', { - data: fullShipProjection, + data: shipProjection, filter: { id: ship.id, } @@ -277,57 +318,26 @@ export async function refreshtShipState(ship, context) { }); ship2 = ships[0]; } - let application = undefined, info = undefined; - const { deposit$ship: deposits, shipOrder$ship: shipOrders, } = ship2; - if (deposits && deposits.length > 0) { - //充值 - const deposit = deposits[0]; - const { pay$deposit } = deposit; - application = pay$deposit?.[0].application; - const meta = pay$deposit?.[0].meta; - info = { - transaction_id: meta?.transaction_id - }; + const state = await getShipState(context, ship2.id); + let action = undefined; + switch (state) { + case 'shipping': //已发货 + action = 'ship'; + break; + case 'received': //确认收货 + action = 'receive'; + break; + default: + action = undefined; } - else if (shipOrders && shipOrders.length > 0) { - //订单 - } - if (application) { - const { type, config } = application; - assert(type === 'wechatMp'); - const { appId, appSecret } = config; - const wechatInstance = WechatSDK.getInstance(appId, 'wechatMp', appSecret); - if (info) { - 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: //已发货 - state = 'shipping'; - action = 'ship'; - break; - case 3: //确认收货 - state = 'received'; - action = 'receive'; - break; - } - if (action && ship2.iState !== state) { - return await context.operate('ship', { - id: await generateNewIdAsync(), - action, - data: {}, - filter: { - id: ship2.id, - } - }, {}); - } + if (action && ship2.iState !== state) { + return await context.operate('ship', { + id: await generateNewIdAsync(), + action, + data: {}, + filter: { + id: ship2.id, } - } + }, {}); } } diff --git a/es/utils/shipClazz/WechatMpShip/WechatMpShip.debug.d.ts b/es/utils/shipClazz/WechatMpShip/WechatMpShip.debug.d.ts index e79a416b..05bab020 100644 --- a/es/utils/shipClazz/WechatMpShip/WechatMpShip.debug.d.ts +++ b/es/utils/shipClazz/WechatMpShip/WechatMpShip.debug.d.ts @@ -8,7 +8,10 @@ type ExtraAddExpressOrderData = Omit> implements ShipClazz { private wechatMpShipId; private wechatMpShip?; - private getReceiverInfo; + getReceiverInfo: (orderIds: string[], applicationId: string, context: Context) => Promise<{ + openId?: string; + appWxId?: string; + }>; private getExtraData; constructor(wechatMpShipId: string, getReceiverInfo: (orderIds: string[], applicationId: string, context: Context) => Promise<{ openId?: string; diff --git a/es/utils/shipClazz/WechatMpShip/WechatMpShip.debug.js b/es/utils/shipClazz/WechatMpShip/WechatMpShip.debug.js index ffa44e51..1099c634 100644 --- a/es/utils/shipClazz/WechatMpShip/WechatMpShip.debug.js +++ b/es/utils/shipClazz/WechatMpShip/WechatMpShip.debug.js @@ -18,6 +18,7 @@ const ShipServiceCodeDict = { export default class WechatMpShipDebug { wechatMpShipId; wechatMpShip; + // 这个外部可能也需要调用 getReceiverInfo; getExtraData; constructor(wechatMpShipId, getReceiverInfo, getExtraData) { diff --git a/es/watchers/ship.js b/es/watchers/ship.js index 24944f3e..a08f6cdb 100644 --- a/es/watchers/ship.js +++ b/es/watchers/ship.js @@ -1,5 +1,5 @@ import { mergeOperationResult } from 'oak-domain/lib/utils/operationResult'; -import { fullShipProjection, refreshtShipState } from '../utils/ship'; +import { shipProjection, refreshtShipState } from '../utils/ship'; const QUERY_PAYING_STATE_GAP = process.env.NODE_ENV === 'production' ? 600 * 1000 : 60 * 1000; const watchers = [ { @@ -14,7 +14,7 @@ const watchers = [ }, }; }, - projection: fullShipProjection, + projection: shipProjection, fn: async (context, data) => { const results = []; for (const ship of data) { diff --git a/lib/aspects/AspectDict.d.ts b/lib/aspects/AspectDict.d.ts index 01c6e005..b9b3c574 100644 --- a/lib/aspects/AspectDict.d.ts +++ b/lib/aspects/AspectDict.d.ts @@ -7,7 +7,6 @@ export type AspectDict = { withdrawAccountId?: string; }, context: BackendRuntimeContext) => Promise; getMpShipState: (params: { - depositId?: string; - orderId?: string; + shipId: string; }, context: BackendRuntimeContext) => Promise; }; diff --git a/lib/aspects/ship.d.ts b/lib/aspects/ship.d.ts index 4be4fe0a..6c5128ce 100644 --- a/lib/aspects/ship.d.ts +++ b/lib/aspects/ship.d.ts @@ -5,6 +5,5 @@ import { BRC } from '../types/RuntimeCxt'; * @param context */ export declare function getMpShipState(params: { - depositId?: string; - orderId?: string; + shipId: string; }, context: BRC): Promise; diff --git a/lib/aspects/ship.js b/lib/aspects/ship.js index 1d9290e7..b83cdb90 100644 --- a/lib/aspects/ship.js +++ b/lib/aspects/ship.js @@ -11,31 +11,9 @@ async function getMpShipState(params, context) { const application = context.getApplication(); const { type, } = application; if (type === 'wechatMp') { - const { depositId, orderId } = params; - if (depositId) { - //充值 - const deposits = await context.select('deposit', { - data: { - id: 1, - iState: 1, - shipId: 1, - ship: { - id: 1, - iState: 1, - }, - }, - filter: { - id: depositId, - } - }, {}); - const deposit = deposits[0]; - if (deposit) { - const shipState = await (0, ship_1.getShipState)(context, deposit); - return shipState; - } - } - if (orderId) { - } + const { shipId } = params; + const shipState = await (0, ship_1.getShipState)(context, shipId); + return shipState; } } exports.getMpShipState = getMpShipState; diff --git a/lib/triggers/ship.js b/lib/triggers/ship.js index ef81e17b..c15d9c88 100644 --- a/lib/triggers/ship.js +++ b/lib/triggers/ship.js @@ -62,7 +62,7 @@ const triggers = [ } }, { - // todo,应该是所有的ship + // todo,deposit的ship和 shipClazz中有配置wechatShip且可获取openId的order的ship entity: 'ship', name: '当虚拟的ship变为shipping状态时,调用小程序发货信息录入接口', action: 'ship', @@ -80,21 +80,45 @@ const triggers = [ data: { id: 1, }, - indexFrom: 0, - count: 1, + }, + entity: 1, + entityId: 1, + shipServiceId: 1, + shipOrder$ship: { + $entity: 'shipOrder', + data: { + id: 1, + orderId: 1, + } + }, + wechatMpShip: { + id: 1, + applicationId: 1, } }, filter, }, {}); - const { type, deposit$ship: deposits } = ship || {}; - if (type === 'virtual') { - const deposit = deposits?.[0]; - //当已发货的订单再次调用小程序发货信息录入接口视为重新发货(仅可重新发货一次) + const { id: shipId, type, deposit$ship: deposits, shipOrder$ship, shipServiceId, entity, entityId, wechatMpShip } = ship || {}; + if (deposits && deposits.length > 0) { + //充值 (此时该充值必定为受发货限制的小程序上的充值) //发货前先查询,检查是否为未同步微信端发货状态 - const shipState = await (0, ship_1.getShipState)(context, deposit); + const shipState = await (0, ship_1.getShipState)(context, shipId); if (shipState === 'unshipped') { - const result = await (0, ship_1.uploadShippingInfo)({ depositId: deposit?.id, }, context); - if (result) { + await (0, ship_1.uploadShippingInfo)(shipId, context); + return 1; + } + } + if (shipOrder$ship && shipOrder$ship.length > 0) { + //订单 + const clazz = await (0, shipClazz_1.getShipClazz)(entity, entityId, context); + const { openId } = await clazz.getReceiverInfo(shipOrder$ship.map((ele) => ele.orderId), wechatMpShip?.applicationId, context); + if (openId) { + //当存在openId时调用小程序发货信息录入 + const shipState = await (0, ship_1.getShipState)(context, shipId); + //当已发货的订单再次调用小程序发货信息录入接口视为重新发货(仅可重新发货一次) + if (shipState && ['unshipped', 'shipping'].includes(shipState)) { + // 发货/更换发货 + await (0, ship_1.uploadShippingInfo)(shipId, context); return 1; } } @@ -206,7 +230,7 @@ const triggers = [ if (packaged.length > 0) { await context.operate('order', { id: await (0, uuid_1.generateNewIdAsync)(), - action: 'ship', + action: 'send', data: {}, filter: { id: { @@ -252,33 +276,16 @@ const triggers = [ } } }, { dontCollect: true }); - const packaged = orders.filter(ele => ele.receivingMethod === 'express'); - if (packaged.length > 0) { - await context.operate('order', { - id: await (0, uuid_1.generateNewIdAsync)(), - action: 'turnBack', - data: {}, - filter: { - id: { - $in: packaged.map(ele => ele.id), - }, + await context.operate('order', { + id: await (0, uuid_1.generateNewIdAsync)(), + action: 'unship', + data: {}, + filter: { + id: { + $in: orders.map(ele => ele.id), }, - }, option); - } - const staged = orders.filter(ele => ele.receivingMethod === 'pickup'); - if (staged.length > 0) { - await context.operate('order', { - id: await (0, uuid_1.generateNewIdAsync)(), - action: 'cancelTaking', - data: {}, - filter: { - id: { - $in: staged.map(ele => ele.id), - }, - }, - }, option); - } - (0, assert_1.default)(staged.length + packaged.length === orders.length); + }, + }, option); return orders.length; } }, diff --git a/lib/types/ShipClazz.d.ts b/lib/types/ShipClazz.d.ts index 58dbfa86..ec8d2163 100644 --- a/lib/types/ShipClazz.d.ts +++ b/lib/types/ShipClazz.d.ts @@ -2,6 +2,10 @@ import { EntityDict } from '../oak-app-domain'; import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity'; import BackendRuntimeContext from '../context/BackendRuntimeContext'; export default interface ShipClazz> { + getReceiverInfo(orderIds: string[], applicationId: string, context: Context): Promise<{ + openId?: string; + appWxId?: string; + }>; available(shipServiceId: string, orderIds: string[], context: Context): Promise; eOrder(shipId: string, context: Context): Promise; cancelOrder(shipId: string, context: Context): Promise; diff --git a/lib/utils/ship.d.ts b/lib/utils/ship.d.ts index b9ff2eae..538b7100 100644 --- a/lib/utils/ship.d.ts +++ b/lib/utils/ship.d.ts @@ -1,16 +1,18 @@ import { EntityDict } from '../oak-app-domain'; import { BRC } from '../types/RuntimeCxt'; -export declare const fullShipProjection: EntityDict['ship']['Projection']; +export declare const shipProjection: EntityDict['ship']['Projection']; +/** + * 手机号掩码(130****0000) + * @param phone + */ +export declare function maskPhone(phone: string): string; /** * 小程序发货信息录入 * @param shipInfo * @param context * @returns */ -export declare function uploadShippingInfo(param: { - depositId?: string; - orderId?: string; -}, context: BRC): Promise; +export declare function uploadShippingInfo(shipId: string, context: BRC): Promise; /** * 获取小程序物流状态 * @param context @@ -18,7 +20,7 @@ export declare function uploadShippingInfo(param: { * @param order * @returns */ -export declare function getShipState(context: BRC, deposit?: EntityDict['deposit']['Schema'], order?: EntityDict['order']['Schema']): Promise; +export declare function getShipState(context: BRC, shipId: string): Promise; /**刷新shipping的ship的小程序物流状态 * @param ship * @param context diff --git a/lib/utils/ship.js b/lib/utils/ship.js index 9ded6ad1..49120127 100644 --- a/lib/utils/ship.js +++ b/lib/utils/ship.js @@ -1,16 +1,20 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.refreshtShipState = exports.getShipState = exports.uploadShippingInfo = exports.fullShipProjection = void 0; +exports.refreshtShipState = exports.getShipState = exports.uploadShippingInfo = exports.maskPhone = exports.shipProjection = void 0; const tslib_1 = require("tslib"); 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 dayjs_1 = tslib_1.__importDefault(require("dayjs")); -exports.fullShipProjection = { +const validator_1 = require("oak-domain/lib/utils/validator"); +exports.shipProjection = { id: 1, type: 1, iState: 1, + entity: 1, + entityId: 1, + extraShipId: 1, shipService: { id: 1, name: 1, @@ -21,7 +25,6 @@ exports.fullShipProjection = { wechatMpName: 1, } }, - serial: 1, deposit$ship: { $entity: 'deposit', data: { @@ -43,6 +46,9 @@ exports.fullShipProjection = { iState: 1, entity: 1, entityId: 1, + }, + filter: { + iState: 'paid' } } } @@ -70,75 +76,91 @@ exports.fullShipProjection = { 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; +} +exports.maskPhone = maskPhone; /** * 小程序发货信息录入 * @param shipInfo * @param context * @returns */ -async function uploadShippingInfo(param, context) { - const { depositId, orderId } = param; - if (depositId) { - const [deposit] = await context.select('deposit', { - data: { - id: 1, - iState: 1, - shipId: 1, - ship: { - id: 1, - iState: 1, - type: 1, - }, - pay$deposit: { - $entity: 'pay', - data: { - id: 1, - entity: 1, - entityId: 1, - meta: 1, - applicationId: 1, - }, - filter: { - iState: 'paid', - }, - indexFrom: 0, - count: 1, - }, - }, - filter: { - id: depositId, - } - }, { - blockTrigger: true, - forUpdate: true, - }); - const { pay$deposit: pays, } = deposit || {}; +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 getShipState(context, shipId); + const now = (0, dayjs_1.default)().format(); + const application = context.getApplication(); + const { type, config } = application; + (0, assert_1.assert)(type === 'wechatMp'); + const { appId, appSecret } = config; + const wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatMp', appSecret); + 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 applicationId = pay?.applicationId; - (0, assert_1.assert)(applicationId); - const [application] = await context.select('application', { - data: { - id: 1, - type: 1, - config: 1. - }, - filter: { - id: applicationId, - } - }, { - blockTrigger: true, - forUpdate: true, - }); - const { type, config } = application; - (0, assert_1.assert)(type === 'wechatMp'); - const { appId, appSecret } = config; - const wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatMp', appSecret); const meta = pay?.meta; const shipInfo = { order_key: { @@ -152,22 +174,64 @@ async function uploadShippingInfo(param, context) { item_desc: '账户充值', } ], - upload_time: (0, dayjs_1.default)().format(), + upload_time: now, payer: { openid: meta?.payer?.openid, }, }; const result = await wechatInstance.uploadShippingInfo(shipInfo); - if (result?.errcode === 0) { - return true; - } - else { + if (result?.errcode !== 0) { console.error(JSON.stringify(result)); throw new Exception_1.UploadShipException(result?.message); } } - if (orderId) { - //订单 + 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, + }, + logistic_type: shipType === 'express' ? 1 : 4, + delivery_mode: 1, + shipping_list: shippingList, + upload_time: now, + payer: { + openid: meta?.payer?.openid, + }, + }; + const result = await wechatInstance.uploadShippingInfo(shipInfo); + if (result?.errcode !== 0) { + console.error(JSON.stringify(result)); + throw new Exception_1.UploadShipException(result?.message); + } + } } } exports.uploadShippingInfo = uploadShippingInfo; @@ -178,89 +242,67 @@ exports.uploadShippingInfo = uploadShippingInfo; * @param order * @returns */ -async function getShipState(context, deposit, order) { +async function getShipState(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 } = ship; const application = context.getApplication(); const { type, config } = application; (0, assert_1.assert)(type === 'wechatMp'); const { appId, appSecret } = config; const wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatMp', appSecret); - if (deposit) { + let info = undefined; + if (deposits && deposits.length > 0) { //充值 - let deposit2 = deposit; - const deposits = await context.select('deposit', { - data: { - id: 1, - iState: 1, - pay$deposit: { - $entity: 'pay', - data: { - id: 1, - iState: 1, - meta: 1, - entity: 1, - entityId: 1, - }, - filter: { - iState: 'paid', - }, - indexFrom: 0, - count: 1, - }, - shipId: 1, - ship: { - id: 1, - type: 1, - iState: 1, - } - }, - filter: { - id: deposit?.id, - } - }, { - blockTrigger: true, - forUpdate: true, - }); - deposit2 = deposits[0]; - const { pay$deposit: pays, ship, shipId } = deposit2; + const [deposit] = deposits; + const { pay$deposit: pays, } = deposit; const pay = pays?.[0]; if (shipId && pay) { - const info = { + info = { transaction_id: pay?.meta?.transaction_id }; - 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: //已发货 - state = 'shipping'; - // action = 'ship'; - break; - case 3: //确认收货 - state = 'received'; - // action = 'receive'; - break; - } - // if (action && ship?.iState !== state) { - // await context.operate('ship', { - // id: await generateNewIdAsync(), - // action, - // data: {}, - // filter: { - // id: shipId, - // } - // }, {}); - // } - return state; - } } } - if (order) { + 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) { + info = { + transaction_id: pay?.meta?.transaction_id + }; + } + } + if (info) { + 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: //已发货 + state = 'shipping'; + // action = 'ship'; + break; + case 3: //确认收货 + state = 'received'; + // action = 'receive'; + break; + } + return state; + } } } exports.getShipState = getShipState; @@ -271,9 +313,9 @@ exports.getShipState = getShipState; */ async function refreshtShipState(ship, context) { let ship2 = ship; - if (!ship2.iState || (!ship2.deposit$ship && !ship2.shipOrder$ship)) { + if (!ship2.iState) { const ships = await context.select('ship', { - data: exports.fullShipProjection, + data: exports.shipProjection, filter: { id: ship.id, } @@ -283,58 +325,27 @@ async function refreshtShipState(ship, context) { }); ship2 = ships[0]; } - let application = undefined, info = undefined; - const { deposit$ship: deposits, shipOrder$ship: shipOrders, } = ship2; - if (deposits && deposits.length > 0) { - //充值 - const deposit = deposits[0]; - const { pay$deposit } = deposit; - application = pay$deposit?.[0].application; - const meta = pay$deposit?.[0].meta; - info = { - transaction_id: meta?.transaction_id - }; + const state = await getShipState(context, ship2.id); + let action = undefined; + switch (state) { + case 'shipping': //已发货 + action = 'ship'; + break; + case 'received': //确认收货 + action = 'receive'; + break; + default: + action = undefined; } - else if (shipOrders && shipOrders.length > 0) { - //订单 - } - if (application) { - const { type, config } = application; - (0, assert_1.assert)(type === 'wechatMp'); - const { appId, appSecret } = config; - const wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, 'wechatMp', appSecret); - if (info) { - 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: //已发货 - state = 'shipping'; - action = 'ship'; - break; - case 3: //确认收货 - state = 'received'; - action = 'receive'; - break; - } - if (action && ship2.iState !== state) { - return await context.operate('ship', { - id: await (0, uuid_1.generateNewIdAsync)(), - action, - data: {}, - filter: { - id: ship2.id, - } - }, {}); - } + if (action && ship2.iState !== state) { + return await context.operate('ship', { + id: await (0, uuid_1.generateNewIdAsync)(), + action, + data: {}, + filter: { + id: ship2.id, } - } + }, {}); } } exports.refreshtShipState = refreshtShipState; diff --git a/lib/utils/shipClazz/WechatMpShip/WechatMpShip.debug.d.ts b/lib/utils/shipClazz/WechatMpShip/WechatMpShip.debug.d.ts index e79a416b..05bab020 100644 --- a/lib/utils/shipClazz/WechatMpShip/WechatMpShip.debug.d.ts +++ b/lib/utils/shipClazz/WechatMpShip/WechatMpShip.debug.d.ts @@ -8,7 +8,10 @@ type ExtraAddExpressOrderData = Omit> implements ShipClazz { private wechatMpShipId; private wechatMpShip?; - private getReceiverInfo; + getReceiverInfo: (orderIds: string[], applicationId: string, context: Context) => Promise<{ + openId?: string; + appWxId?: string; + }>; private getExtraData; constructor(wechatMpShipId: string, getReceiverInfo: (orderIds: string[], applicationId: string, context: Context) => Promise<{ openId?: string; diff --git a/lib/utils/shipClazz/WechatMpShip/WechatMpShip.debug.js b/lib/utils/shipClazz/WechatMpShip/WechatMpShip.debug.js index 73d219a5..c35bdce0 100644 --- a/lib/utils/shipClazz/WechatMpShip/WechatMpShip.debug.js +++ b/lib/utils/shipClazz/WechatMpShip/WechatMpShip.debug.js @@ -21,6 +21,7 @@ const ShipServiceCodeDict = { class WechatMpShipDebug { wechatMpShipId; wechatMpShip; + // 这个外部可能也需要调用 getReceiverInfo; getExtraData; constructor(wechatMpShipId, getReceiverInfo, getExtraData) { diff --git a/lib/watchers/ship.js b/lib/watchers/ship.js index ded032b0..f82c5bb7 100644 --- a/lib/watchers/ship.js +++ b/lib/watchers/ship.js @@ -16,7 +16,7 @@ const watchers = [ }, }; }, - projection: ship_1.fullShipProjection, + projection: ship_1.shipProjection, fn: async (context, data) => { const results = []; for (const ship of data) { diff --git a/src/aspects/AspectDict.ts b/src/aspects/AspectDict.ts index f5291618..077a0179 100644 --- a/src/aspects/AspectDict.ts +++ b/src/aspects/AspectDict.ts @@ -8,7 +8,6 @@ export type AspectDict = { withdrawAccountId?: string; }, context: BackendRuntimeContext) => Promise; getMpShipState: (params: { - depositId?: string; - orderId?: string; + shipId: string; }, context: BackendRuntimeContext) => Promise; }; diff --git a/src/aspects/ship.ts b/src/aspects/ship.ts index a6ebf221..c4469e06 100644 --- a/src/aspects/ship.ts +++ b/src/aspects/ship.ts @@ -11,41 +11,18 @@ import { getShipState } from '../utils/ship'; */ export async function getMpShipState( params: { - depositId?: string, - orderId?: string, + shipId: string, }, context: BRC ) { const application = context.getApplication(); const { type, } = application!; if (type === 'wechatMp') { - const { depositId, orderId } = params; - if (depositId) { - //充值 - const deposits = await context.select('deposit', { - data: { - id: 1, - iState: 1, - shipId: 1, - ship: { - id: 1, - iState: 1, - }, - }, - filter: { - id: depositId, - } - }, {}); - const deposit = deposits[0] as EntityDict['deposit']['Schema']; - if (deposit) { - const shipState = await getShipState( - context, - deposit, - ) as EntityDict['ship']['Schema']['iState']; - return shipState; - } - } - if (orderId) { - } + const { shipId } = params; + const shipState = await getShipState( + context, + shipId, + ) as EntityDict['ship']['Schema']['iState']; + return shipState; } } \ No newline at end of file diff --git a/src/components/pay/detail/index.ts b/src/components/pay/detail/index.ts index 6e65b4f5..2c0732a8 100644 --- a/src/components/pay/detail/index.ts +++ b/src/components/pay/detail/index.ts @@ -162,7 +162,7 @@ export default OakComponent({ ); const { mode } = this.props; - const succeedable =data["#oakLegalActions"]?.includes('succeedPaying'); + const succeedable = data["#oakLegalActions"]?.includes('succeedPaying'); const payChannels = this.features.pay.getPayChannels(); const offlines = this.features.cache.get('offlineAccount', { @@ -400,7 +400,7 @@ export default OakComponent({ async success() { console.log('success'); const mpShipState = await this.features.cache.exec('getMpShipState', { - depositId, + shipId: next.shipId, }) if (mpShipState === 'received') { this.setMessage({ diff --git a/src/triggers/ship.ts b/src/triggers/ship.ts index 61e0dd55..9d806cce 100644 --- a/src/triggers/ship.ts +++ b/src/triggers/ship.ts @@ -67,7 +67,7 @@ const triggers: Trigger[] = [ } } as CreateTriggerCrossTxn, { - // todo,应该是所有的ship + // todo,deposit的ship和 shipClazz中有配置wechatShip且可获取openId的order的ship entity: 'ship', name: '当虚拟的ship变为shipping状态时,调用小程序发货信息录入接口', action: 'ship', @@ -85,21 +85,47 @@ const triggers: Trigger[] = [ data: { id: 1, }, - indexFrom: 0, - count: 1, + }, + entity: 1, + entityId: 1, + shipServiceId: 1, + shipOrder$ship: { + $entity: 'shipOrder', + data: { + id: 1, + orderId: 1, + + } + }, + wechatMpShip: { + id: 1, + applicationId: 1, } }, filter, }, {}); - const { type, deposit$ship: deposits } = ship || {}; - if (type === 'virtual') { - const deposit = deposits?.[0]; - //当已发货的订单再次调用小程序发货信息录入接口视为重新发货(仅可重新发货一次) + const { id: shipId, type, deposit$ship: deposits, shipOrder$ship, shipServiceId, entity, entityId, wechatMpShip } = ship || {}; + if (deposits && deposits.length > 0) { + //充值 (此时该充值必定为受发货限制的小程序上的充值) + //发货前先查询,检查是否为未同步微信端发货状态 - const shipState = await getShipState(context, deposit); + const shipState = await getShipState(context, shipId!); if (shipState === 'unshipped') { - const result = await uploadShippingInfo({ depositId: deposit?.id, }, context); - if (result) { + await uploadShippingInfo(shipId!, context); + return 1; + } + } + if (shipOrder$ship && shipOrder$ship.length > 0) { + //订单 + const clazz = await getShipClazz(entity!, entityId!, context); + const { openId } = await clazz.getReceiverInfo(shipOrder$ship.map((ele) => ele.orderId!), wechatMpShip?.applicationId!, context); + if (openId) { + //当存在openId时调用小程序发货信息录入 + const shipState = await getShipState(context, shipId!); + //当已发货的订单再次调用小程序发货信息录入接口视为重新发货(仅可重新发货一次) + if (shipState && ['unshipped', 'shipping'].includes(shipState)) { + // 发货/更换发货 + await uploadShippingInfo(shipId!, context); return 1; } } diff --git a/src/types/ShipClazz.ts b/src/types/ShipClazz.ts index 213d2e72..54d03e3f 100644 --- a/src/types/ShipClazz.ts +++ b/src/types/ShipClazz.ts @@ -3,6 +3,10 @@ import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity'; import BackendRuntimeContext from '../context/BackendRuntimeContext'; export default interface ShipClazz> { + getReceiverInfo(orderIds: string[], applicationId: string, context: Context): Promise<{ + openId?: string; + appWxId?: string; + }>; // 是否可以使用这个接口下单 available(shipServiceId: string, orderIds: string[], context: Context): Promise; diff --git a/src/utils/ship.ts b/src/utils/ship.ts index 7caadfb1..1115a239 100644 --- a/src/utils/ship.ts +++ b/src/utils/ship.ts @@ -6,12 +6,18 @@ import { WechatMpConfig } from '@project/oak-app-domain/Application/Schema'; import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid'; import { UploadShipException } from '../types/Exception'; import dayjs from 'dayjs'; +import { isMobile } from 'oak-domain/lib/utils/validator'; +import { OakInputIllegalException } from 'oak-domain/lib/types'; +import { unionBy } from 'oak-domain/lib/utils/lodash'; -export const fullShipProjection: EntityDict['ship']['Projection'] = { +export const shipProjection: EntityDict['ship']['Projection'] = { id: 1, type: 1, iState: 1, + entity: 1, + entityId: 1, + extraShipId: 1, shipService: { id: 1, name: 1, @@ -43,6 +49,9 @@ export const fullShipProjection: EntityDict['ship']['Projection'] = { iState: 1, entity: 1, entityId: 1, + }, + filter: { + iState: 'paid' } } } @@ -70,11 +79,61 @@ export const fullShipProjection: EntityDict['ship']['Projection'] = { 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 + */ +export function maskPhone(phone: string) { + assert(isMobile(phone)); + const start = phone.slice(3); + const end = phone.slice(-4); + return start + '****' + end; } /** @@ -84,74 +143,38 @@ export const fullShipProjection: EntityDict['ship']['Projection'] = { * @returns */ export async function uploadShippingInfo( - param: { - depositId?: string, - orderId?: string, - }, + shipId: string, context: BRC, ) { - const { depositId, orderId } = param; - if (depositId) { - const [deposit] = await context.select('deposit', { - data: { - id: 1, - iState: 1, - shipId: 1, - ship: { - id: 1, - iState: 1, - type: 1, - }, - pay$deposit: { - $entity: 'pay', - data: { - id: 1, - entity: 1, - entityId: 1, - meta: 1, - applicationId: 1, - }, - filter: { - iState: 'paid', - }, - indexFrom: 0, - count: 1, - }, - }, - filter: { - id: depositId, - } - }, { - blockTrigger: true, - forUpdate: true, - }); - const { pay$deposit: pays, } = deposit || {}; + const [ship] = await context.select('ship', { + data: 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 getShipState(context, shipId); + const now = dayjs().format(); + + 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') { + //充值 + assert(deposits.length === 1); + const [deposit] = deposits; + const { pay$deposit: pays, } = deposit; const pay = pays?.[0]; - const applicationId = pay?.applicationId; - assert(applicationId); - const [application] = await context.select('application', { - data: { - id: 1, - type: 1, - config: 1. - }, - filter: { - id: applicationId, - } - }, { - blockTrigger: true, - forUpdate: true, - }) - const { type, config } = application!; - assert(type === 'wechatMp'); - const { appId, appSecret } = config as WechatMpConfig; - - const wechatInstance = WechatSDK.getInstance( - appId, - 'wechatMp', - appSecret, - ) as WechatMpInstance; - const meta = pay?.meta as { transaction_id: string; payer: { @@ -168,29 +191,71 @@ export async function uploadShippingInfo( shipping_list: [ { item_desc: '账户充值', - } ], - upload_time: dayjs().format(), + upload_time: now, payer: { openid: meta?.payer?.openid, }, } const result = await wechatInstance.uploadShippingInfo(shipInfo); - if (result?.errcode === 0) { - return true; - } else { + if (result?.errcode !== 0) { console.error(JSON.stringify(result)); throw new 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 as { + transaction_id: string; + payer: { + openid: string; + }; + }; + let shippingList: Array = []; + 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: any = { + order_key: { + order_number_type: 2, + transaction_id: meta?.transaction_id, + }, + logistic_type: shipType === 'express' ? 1 : 4, + delivery_mode: 1, + shipping_list: shippingList, + upload_time: now, + payer: { + openid: meta?.payer?.openid, + }, + } + + const result = await wechatInstance.uploadShippingInfo(shipInfo); + if (result?.errcode !== 0) { + console.error(JSON.stringify(result)); + throw new UploadShipException(result?.message); + } + } } - - if (orderId) { - //订单 - } - - - } /** @@ -202,98 +267,74 @@ export async function uploadShippingInfo( */ export async function getShipState( context: BRC, - deposit?: EntityDict['deposit']['Schema'], - order?: EntityDict['order']['Schema'], + shipId: string, ) { + const [ship] = await context.select('ship', { + data: shipProjection, + filter: { + id: shipId, + } + }, { + blockTrigger: true, + forUpdate: true + }); + + assert(ship); + const { deposit$ship: deposits, shipOrder$ship } = ship; + 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 (deposit) { + let info = undefined; + if (deposits && deposits.length > 0) { //充值 - let deposit2 = deposit; - const deposits = await context.select('deposit', { - data: { - id: 1, - iState: 1, - pay$deposit: { - $entity: 'pay', - data: { - id: 1, - iState: 1, - meta: 1, - entity: 1, - entityId: 1, - }, - filter: { - iState: 'paid', - }, - indexFrom: 0, - count: 1, - }, - shipId: 1, - ship: { - id: 1, - type: 1, - iState: 1, - } - }, - filter: { - id: deposit?.id, - } - }, { - blockTrigger: true, - forUpdate: true, - }); - deposit2 = deposits[0] as typeof deposit; - const { pay$deposit: pays, ship, shipId } = deposit2; + const [deposit] = deposits; + const { pay$deposit: pays, } = deposit; const pay = pays?.[0]; if (shipId && pay) { - const info = { + info = { + transaction_id: (pay?.meta as { transaction_id: string })?.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) { + info = { transaction_id: (pay?.meta as { transaction_id: string })?.transaction_id }; - 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: //已发货 - state = 'shipping'; - // action = 'ship'; - break; - case 3: //确认收货 - state = 'received'; - // action = 'receive'; - break; - } - // if (action && ship?.iState !== state) { - // await context.operate('ship', { - // id: await generateNewIdAsync(), - // action, - // data: {}, - // filter: { - // id: shipId, - // } - // }, {}); - // } - return state; - } } } - if (order) { - + if (info) { + 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: //已发货 + state = 'shipping'; + // action = 'ship'; + break; + case 3: //确认收货 + state = 'received'; + // action = 'receive'; + break; + } + return state; + } } } @@ -309,9 +350,9 @@ export async function refreshtShipState( context: BRC, ) { let ship2 = ship; - if (!ship2.iState || (!ship2.deposit$ship && !ship2.shipOrder$ship)) { + if (!ship2.iState) { const ships = await context.select('ship', { - data: fullShipProjection, + data: shipProjection, filter: { id: ship.id, } @@ -321,62 +362,26 @@ export async function refreshtShipState( }); ship2 = ships[0] as typeof ship; } - let application = undefined, info = undefined; - const { deposit$ship: deposits, shipOrder$ship: shipOrders, } = ship2; - if (deposits && deposits.length > 0) { - //充值 - const deposit = deposits[0]; - const { pay$deposit } = deposit; - application = pay$deposit?.[0]!.application; - const meta = pay$deposit?.[0]!.meta; - info = { - transaction_id: (meta as { transaction_id: string })?.transaction_id - }; + const state = await getShipState(context, ship2.id!); + let action = undefined; + switch (state) { + case 'shipping': //已发货 + action = 'ship'; + break; + case 'received': //确认收货 + action = 'receive'; + break; + default: + action = undefined; } - else if (shipOrders && shipOrders.length > 0) { - //订单 - } - if (application) { - const { type, config } = application!; - assert(type === 'wechatMp'); - const { appId, appSecret } = config as WechatMpConfig; - const wechatInstance = WechatSDK.getInstance( - appId, - 'wechatMp', - appSecret, - ) as WechatMpInstance; - - if (info) { - 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: //已发货 - state = 'shipping'; - action = 'ship'; - break; - case 3: //确认收货 - state = 'received'; - action = 'receive' - break; - } - if (action && ship2.iState !== state) { - return await context.operate('ship', { - id: await generateNewIdAsync(), - action, - data: {}, - filter: { - id: ship2.id, - } - }, {}); - } + if (action && ship2.iState !== state) { + return await context.operate('ship', { + id: await generateNewIdAsync(), + action, + data: {}, + filter: { + id: ship2.id, } - } + }, {}); } } \ No newline at end of file diff --git a/src/watchers/ship.ts b/src/watchers/ship.ts index 770b6653..4a9dd288 100644 --- a/src/watchers/ship.ts +++ b/src/watchers/ship.ts @@ -3,7 +3,7 @@ import { EntityDict } from '../oak-app-domain'; import { BRC } from '../types/RuntimeCxt'; import { OperationResult } from 'oak-domain/lib/types'; import { mergeOperationResult } from 'oak-domain/lib/utils/operationResult'; -import { fullShipProjection, refreshtShipState } from '../utils/ship'; +import { shipProjection, refreshtShipState } from '../utils/ship'; const QUERY_PAYING_STATE_GAP = process.env.NODE_ENV === 'production' ? 600 * 1000 : 60 * 1000; @@ -20,7 +20,7 @@ const watchers: Watcher[] = [ }, }; }, - projection: fullShipProjection, + projection: shipProjection, fn: async (context, data) => { const results = [] as OperationResult[]; for (const ship of data) {