Compare commits
10 Commits
39afbae7cf
...
9137e95c02
| Author | SHA1 | Date |
|---|---|---|
|
|
9137e95c02 | |
|
|
2ad48143b7 | |
|
|
ca1a15c75d | |
|
|
8a1b929692 | |
|
|
d23acbee1c | |
|
|
96eb7768f3 | |
|
|
c55eefa3de | |
|
|
6c65f5be85 | |
|
|
b0e2c42ad0 | |
|
|
015113d3b3 |
|
|
@ -39,13 +39,22 @@ export default {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
wpApiUrl: 'http://t1-shop.local/',
|
wpApiUrl: 'http://t2-shop.local/',
|
||||||
consumerKey: 'ck_a369473a6451dbaec63d19cbfd74a074b2c5f742',
|
consumerKey: 'ck_a369473a6451dbaec63d19cbfd74a074b2c5f742',
|
||||||
consumerSecret: 'cs_0946bbbeea1bfefff08a69e817ac62a48412df8c',
|
consumerSecret: 'cs_0946bbbeea1bfefff08a69e817ac62a48412df8c',
|
||||||
siteName: 'Local',
|
siteName: 'Local',
|
||||||
email: '444693295@qq.com',
|
email: '444693295@qq.com',
|
||||||
emailPswd: 'lulin91.',
|
emailPswd: 'lulin91.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
wpApiUrl: 'http://t1-shop.local/',
|
||||||
|
consumerKey: 'ck_a369473a6451dbaec63d19cbfd74a074b2c5f742',
|
||||||
|
consumerSecret: 'cs_0946bbbeea1bfefff08a69e817ac62a48412df8c',
|
||||||
|
siteName: 'Local-test-2',
|
||||||
|
email: '444693295@qq.com',
|
||||||
|
emailPswd: 'lulin91.',
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// id: '2',
|
// id: '2',
|
||||||
// wpApiUrl: 'http://localhost:10004',
|
// wpApiUrl: 'http://localhost:10004',
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ export class LogisticsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Post('/updateState/:id')
|
@Post('/updateState/:shipmentId')
|
||||||
async updateShipmentState(
|
async updateShipmentState(
|
||||||
@Param('shipmentId') shipmentId: number
|
@Param('shipmentId') shipmentId: number
|
||||||
) {
|
) {
|
||||||
|
|
@ -247,11 +247,11 @@ export class LogisticsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Post('/getTrackingNumber')
|
@Post('/getOrderList')
|
||||||
async getTrackingNumber(@Query('number') number: string) {
|
async getOrderList(@Query('number') number: string) {
|
||||||
try {
|
try {
|
||||||
return successResponse(
|
return successResponse(
|
||||||
await this.logisticsService.getTrackingNumber(number)
|
await this.logisticsService.getOrderList(number)
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || '获取失败');
|
return errorResponse(error?.message || '获取失败');
|
||||||
|
|
@ -259,11 +259,11 @@ export class LogisticsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Post('/getListByTrackingId')
|
@Post('/getListByOrderId')
|
||||||
async getListByTrackingId(@Query('shipment_id') shipment_id: number) {
|
async getListByOrderId(@Query('id') orderId: number) {
|
||||||
try {
|
try {
|
||||||
return successResponse(
|
return successResponse(
|
||||||
await this.logisticsService.getListByTrackingId(shipment_id)
|
await this.logisticsService.getListByOrderId(orderId)
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || '获取失败');
|
return errorResponse(error?.message || '获取失败');
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,12 @@ export class OrderController {
|
||||||
@Get('/getOrders')
|
@Get('/getOrders')
|
||||||
async getOrders(
|
async getOrders(
|
||||||
@Query()
|
@Query()
|
||||||
param: QueryOrderDTO
|
param: QueryOrderDTO,
|
||||||
|
@User() user
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const count = await this.orderService.getOrderStatus(param);
|
const count = await this.orderService.getOrderStatus(param);
|
||||||
const data = await this.orderService.getOrders(param);
|
const data = await this.orderService.getOrders(param, user.id);
|
||||||
return successResponse({
|
return successResponse({
|
||||||
...data,
|
...data,
|
||||||
count,
|
count,
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,15 @@ export class UserController {
|
||||||
@Inject()
|
@Inject()
|
||||||
userService: UserService;
|
userService: UserService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
ctx;
|
||||||
|
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
type: LoginRes,
|
type: LoginRes,
|
||||||
})
|
})
|
||||||
@Post('/login')
|
@Post('/login')
|
||||||
async login(@Body() body) {
|
async login(@Body() body) {
|
||||||
|
this.ctx.logger.info('ip:', this.ctx.ip, '; path:', this.ctx.path, '; user:', body?.username);
|
||||||
try {
|
try {
|
||||||
const result = await this.userService.login(body);
|
const result = await this.userService.login(body);
|
||||||
return successResponse(result, '登录成功');
|
return successResponse(result, '登录成功');
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ export class WebhookController {
|
||||||
switch (topic) {
|
switch (topic) {
|
||||||
case 'product.created':
|
case 'product.created':
|
||||||
case 'product.updated':
|
case 'product.updated':
|
||||||
const site = await this.wpProductService.geSite(siteId);
|
const site = await this.wpProductService.getSite(siteId);
|
||||||
// 变体更新
|
// 变体更新
|
||||||
if (body.type === 'variation') {
|
if (body.type === 'variation') {
|
||||||
const variation = await this.wpApiService.getVariation(
|
const variation = await this.wpApiService.getVariation(
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
Query,
|
Query,
|
||||||
Put,
|
Put,
|
||||||
Body,
|
Body,
|
||||||
|
Config,
|
||||||
} from '@midwayjs/core';
|
} from '@midwayjs/core';
|
||||||
import { WpProductService } from '../service/wp_product.service';
|
import { WpProductService } from '../service/wp_product.service';
|
||||||
import { errorResponse, successResponse } from '../utils/response.util';
|
import { errorResponse, successResponse } from '../utils/response.util';
|
||||||
|
|
@ -19,9 +20,21 @@ import {
|
||||||
UpdateWpProductDTO,
|
UpdateWpProductDTO,
|
||||||
} from '../dto/wp_product.dto';
|
} from '../dto/wp_product.dto';
|
||||||
import { WPService } from '../service/wp.service';
|
import { WPService } from '../service/wp.service';
|
||||||
|
import { WpSite } from '../interface';
|
||||||
|
|
||||||
@Controller('/wp_product')
|
@Controller('/wp_product')
|
||||||
export class WpProductController {
|
export class WpProductController {
|
||||||
|
@Inject()
|
||||||
|
wpService: WPService;
|
||||||
|
|
||||||
|
@Config('wpSite')
|
||||||
|
sites: WpSite[];
|
||||||
|
|
||||||
|
getSite(id: string): WpSite {
|
||||||
|
let idx = this.sites.findIndex(item => item.id === id);
|
||||||
|
return this.sites[idx];
|
||||||
|
}
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private readonly wpProductService: WpProductService;
|
private readonly wpProductService: WpProductService;
|
||||||
|
|
||||||
|
|
@ -73,6 +86,22 @@ export class WpProductController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: BooleanRes
|
||||||
|
})
|
||||||
|
@Post('/updateState/:id')
|
||||||
|
async updateWPProductState(
|
||||||
|
@Param('id') id: number,
|
||||||
|
@Body() body: any, // todo
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const res = await this.wpProductService.updateProductStatus(id, body?.status, body?.stock_status);
|
||||||
|
return successResponse(res);
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新产品接口
|
* 更新产品接口
|
||||||
* @param productId 产品 ID
|
* @param productId 产品 ID
|
||||||
|
|
@ -96,7 +125,7 @@ export class WpProductController {
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
return errorResponse('SKU已存在');
|
return errorResponse('SKU已存在');
|
||||||
}
|
}
|
||||||
const site = await this.wpProductService.geSite(siteId);
|
const site = await this.wpProductService.getSite(siteId);
|
||||||
const result = await this.wpApiService.updateProduct(
|
const result = await this.wpApiService.updateProduct(
|
||||||
site,
|
site,
|
||||||
productId,
|
productId,
|
||||||
|
|
@ -136,7 +165,7 @@ export class WpProductController {
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
return errorResponse('SKU已存在');
|
return errorResponse('SKU已存在');
|
||||||
}
|
}
|
||||||
const site = await this.wpProductService.geSite(siteId);
|
const site = await this.wpProductService.getSite(siteId);
|
||||||
const result = await this.wpApiService.updateVariation(
|
const result = await this.wpApiService.updateVariation(
|
||||||
site,
|
site,
|
||||||
productId,
|
productId,
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,10 @@ export class QueryOrderSalesDTO {
|
||||||
@Rule(RuleType.bool().default(false))
|
@Rule(RuleType.bool().default(false))
|
||||||
isSource: boolean;
|
isSource: boolean;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.bool().default(false))
|
||||||
|
exceptPackage: boolean;
|
||||||
|
|
||||||
@ApiProperty({ example: '1', description: '页码' })
|
@ApiProperty({ example: '1', description: '页码' })
|
||||||
@Rule(RuleType.number())
|
@Rule(RuleType.number())
|
||||||
current: number;
|
current: number;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ export class OrderItem {
|
||||||
externalOrderId: string; // WooCommerce 订单 ID
|
externalOrderId: string; // WooCommerce 订单 ID
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column({ nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
externalOrderItemId: string; // WooCommerce 订单item ID
|
externalOrderItemId: string; // WooCommerce 订单item ID
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ export class OrderSaleOriginal {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@ManyToOne(() => Order)
|
@ManyToOne(() => Order)
|
||||||
@JoinColumn({ name: 'order_id' })
|
@JoinColumn({ name: 'order_id' })
|
||||||
|
@Column({ name: 'order_id' })
|
||||||
@Expose()
|
@Expose()
|
||||||
orderId: number; // 订单 ID
|
orderId: number; // 订单 ID
|
||||||
|
|
||||||
|
|
@ -31,7 +32,7 @@ export class OrderSaleOriginal {
|
||||||
siteId: string; // 来源站点唯一标识
|
siteId: string; // 来源站点唯一标识
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column({ nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
externalOrderItemId: string; // WooCommerce 订单item ID
|
externalOrderItemId: string; // WooCommerce 订单item ID
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import {
|
||||||
Entity,
|
Entity,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { ProductStatus, ProductType } from '../enums/base.enum';
|
import { ProductStatus, ProductStockStatus, ProductType } from '../enums/base.enum';
|
||||||
|
|
||||||
@Entity('wp_product')
|
@Entity('wp_product')
|
||||||
@Unique(['siteId', 'externalProductId']) // 确保产品的唯一性
|
@Unique(['siteId', 'externalProductId']) // 确保产品的唯一性
|
||||||
|
|
@ -56,6 +56,15 @@ export class WpProduct {
|
||||||
@Column({ type: 'enum', enum: ProductStatus })
|
@Column({ type: 'enum', enum: ProductStatus })
|
||||||
status: ProductStatus;
|
status: ProductStatus;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '上下架状态', enum: ProductStockStatus })
|
||||||
|
@Column({
|
||||||
|
name: 'stock_status',
|
||||||
|
type: 'enum',
|
||||||
|
enum: ProductStockStatus,
|
||||||
|
default: ProductStockStatus.INSTOCK
|
||||||
|
})
|
||||||
|
stockStatus: ProductStockStatus;
|
||||||
|
|
||||||
@ApiProperty({ description: '常规价格', type: Number })
|
@ApiProperty({ description: '常规价格', type: Number })
|
||||||
@Column('decimal', { precision: 10, scale: 2, nullable: true })
|
@Column('decimal', { precision: 10, scale: 2, nullable: true })
|
||||||
regular_price: number; // 常规价格
|
regular_price: number; // 常规价格
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,12 @@ export enum ProductStatus {
|
||||||
INHERIT = 'inherit', // 继承状态
|
INHERIT = 'inherit', // 继承状态
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ProductStockStatus {
|
||||||
|
INSTOCK = 'instock',
|
||||||
|
OUT_OF_STOCK = 'outofstock',
|
||||||
|
ON_BACK_ORDER = 'onbackorder',
|
||||||
|
}
|
||||||
|
|
||||||
export enum ProductType {
|
export enum ProductType {
|
||||||
SIMPLE = 'simple',
|
SIMPLE = 'simple',
|
||||||
VARIABLE = 'variable',
|
VARIABLE = 'variable',
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ export class LogisticsService {
|
||||||
@Config('wpSite')
|
@Config('wpSite')
|
||||||
sites: WpSite[];
|
sites: WpSite[];
|
||||||
|
|
||||||
geSite(id: string): WpSite {
|
getSite(id: string): WpSite {
|
||||||
let idx = this.sites.findIndex(item => item.id === id);
|
let idx = this.sites.findIndex(item => item.id === id);
|
||||||
return this.sites[idx];
|
return this.sites[idx];
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +129,7 @@ export class LogisticsService {
|
||||||
async updateShipmentState(shipment: Shipment) {
|
async updateShipmentState(shipment: Shipment) {
|
||||||
try {
|
try {
|
||||||
const data = await this.uniExpressService.getOrderStatus(shipment.return_tracking_number);
|
const data = await this.uniExpressService.getOrderStatus(shipment.return_tracking_number);
|
||||||
|
console.log('updateShipmentState data:', data);
|
||||||
shipment.state = data.data[0].state;
|
shipment.state = data.data[0].state;
|
||||||
if (shipment.state in [203, 215, 216, 230]) { // todo,写常数
|
if (shipment.state in [203, 215, 216, 230]) { // todo,写常数
|
||||||
shipment.finished = true;
|
shipment.finished = true;
|
||||||
|
|
@ -136,7 +137,8 @@ export class LogisticsService {
|
||||||
this.shipmentModel.save(shipment);
|
this.shipmentModel.save(shipment);
|
||||||
return shipment.state;
|
return shipment.state;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`更新运单状态失败 ${error.message}`);
|
throw error;
|
||||||
|
// throw new Error(`更新运单状态失败 ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,7 +263,7 @@ export class LogisticsService {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 同步订单状态到woocommerce
|
// 同步订单状态到woocommerce
|
||||||
const site = await this.geSite(order.siteId);
|
const site = await this.getSite(order.siteId);
|
||||||
if (order.status === OrderStatus.COMPLETED) {
|
if (order.status === OrderStatus.COMPLETED) {
|
||||||
await this.wpService.updateOrder(site, order.externalOrderId, {
|
await this.wpService.updateOrder(site, order.externalOrderId, {
|
||||||
status: OrderStatus.PROCESSING,
|
status: OrderStatus.PROCESSING,
|
||||||
|
|
@ -365,7 +367,7 @@ export class LogisticsService {
|
||||||
const tracking_provider = 'UniUni'; // todo: id未确定,后写进常数
|
const tracking_provider = 'UniUni'; // todo: id未确定,后写进常数
|
||||||
|
|
||||||
// 同步物流信息到woocommerce
|
// 同步物流信息到woocommerce
|
||||||
const site = await this.geSite(order.siteId);
|
const site = await this.getSite(order.siteId);
|
||||||
const res = await this.wpService.createShipment(site, order.externalOrderId, {
|
const res = await this.wpService.createShipment(site, order.externalOrderId, {
|
||||||
tracking_number: resShipmentOrder.data.tno,
|
tracking_number: resShipmentOrder.data.tno,
|
||||||
tracking_provider: tracking_provider,
|
tracking_provider: tracking_provider,
|
||||||
|
|
@ -408,9 +410,11 @@ export class LogisticsService {
|
||||||
// 更新产品发货信息
|
// 更新产品发货信息
|
||||||
this.orderService.updateOrderSales(order.id, sales);
|
this.orderService.updateOrderSales(order.id, sales);
|
||||||
|
|
||||||
return { data: {
|
return {
|
||||||
|
data: {
|
||||||
shipmentId
|
shipmentId
|
||||||
} };
|
}
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (resShipmentOrder.status === 'SUCCESS') {
|
if (resShipmentOrder.status === 'SUCCESS') {
|
||||||
await this.uniExpressService.deleteShipment(resShipmentOrder.data.tno);
|
await this.uniExpressService.deleteShipment(resShipmentOrder.data.tno);
|
||||||
|
|
@ -489,7 +493,7 @@ export class LogisticsService {
|
||||||
const order = await this.orderModel.findOneBy({
|
const order = await this.orderModel.findOneBy({
|
||||||
id: orderShipment.order_id,
|
id: orderShipment.order_id,
|
||||||
});
|
});
|
||||||
const site = this.geSite(order.siteId);
|
const site = this.getSite(order.siteId);
|
||||||
await this.wpService.updateOrder(site, order.externalOrderId, {
|
await this.wpService.updateOrder(site, order.externalOrderId, {
|
||||||
status: OrderStatus.COMPLETED,
|
status: OrderStatus.COMPLETED,
|
||||||
});
|
});
|
||||||
|
|
@ -552,7 +556,7 @@ export class LogisticsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTrackingNumber(number: string) {
|
async getOrderList(number: string) {
|
||||||
const orders = await this.orderModel.find({
|
const orders = await this.orderModel.find({
|
||||||
where: {
|
where: {
|
||||||
externalOrderId: Like(`%${number}%`),
|
externalOrderId: Like(`%${number}%`),
|
||||||
|
|
@ -567,56 +571,14 @@ export class LogisticsService {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async getListByTrackingId(id: number) {
|
async getListByOrderId(id: number) {
|
||||||
const qb = `
|
const item = await this.orderItem.find({ where: { orderId: id } });
|
||||||
SELECT
|
const saleItem = await this.orderSaleModel.find({ where: { orderId: id } });
|
||||||
oi.name,
|
|
||||||
oi.quantity,
|
|
||||||
CASE
|
|
||||||
WHEN oi.externalVariationId != 0 THEN v.constitution
|
|
||||||
ELSE p.constitution
|
|
||||||
END AS constitution
|
|
||||||
FROM order_item oi
|
|
||||||
LEFT JOIN wp_product p ON oi.siteId=p.siteId AND oi.externalProductId=p.externalProductId
|
|
||||||
LEFT JOIN variation v ON oi.siteId=v.siteId AND oi.externalVariationId=v.externalVariationId
|
|
||||||
WHERE oi.orderId=?
|
|
||||||
`;
|
|
||||||
const saleItem = await this.orderSaleModel.query(qb, [id]);
|
|
||||||
const allSkus = new Set<string>();
|
|
||||||
for (const item of saleItem) {
|
|
||||||
if (!item.constitution) continue;
|
|
||||||
try {
|
|
||||||
item.constitution.forEach(c => allSkus.add(c.sku));
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Invalid constitution JSON:', item.constitution);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(allSkus);
|
|
||||||
const skuList = Array.from(allSkus);
|
|
||||||
let skuNameMap = new Map<string, string>();
|
|
||||||
|
|
||||||
if (skuList.length > 0) {
|
return {
|
||||||
const placeholders = skuList.map(() => '?').join(', ');
|
item,
|
||||||
const productRows = await this.orderSaleModel.query(
|
saleItem
|
||||||
`SELECT sku, name FROM product WHERE sku IN (${placeholders})`,
|
};
|
||||||
skuList
|
|
||||||
);
|
|
||||||
skuNameMap = new Map(productRows.map(p => [p.sku, p.name]));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const item of saleItem) {
|
|
||||||
if (!item.constitution) continue;
|
|
||||||
try {
|
|
||||||
item.constitution = item.constitution.map(c => ({
|
|
||||||
...c,
|
|
||||||
name: skuNameMap.get(c.sku) || null,
|
|
||||||
}));
|
|
||||||
} catch (e) {
|
|
||||||
item.constitution = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return saleItem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getList(param: Record<string, any>) {
|
async getList(param: Record<string, any>) {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,9 @@ export class OrderService {
|
||||||
@InjectEntityModel(Order)
|
@InjectEntityModel(Order)
|
||||||
orderModel: Repository<Order>;
|
orderModel: Repository<Order>;
|
||||||
|
|
||||||
|
@InjectEntityModel(User)
|
||||||
|
userModel: Repository<User>;
|
||||||
|
|
||||||
@InjectEntityModel(OrderItem)
|
@InjectEntityModel(OrderItem)
|
||||||
orderItemModel: Repository<OrderItem>;
|
orderItemModel: Repository<OrderItem>;
|
||||||
|
|
||||||
|
|
@ -546,7 +549,7 @@ export class OrderService {
|
||||||
current,
|
current,
|
||||||
pageSize,
|
pageSize,
|
||||||
customer_email,
|
customer_email,
|
||||||
}) {
|
}, userId = undefined) {
|
||||||
const parameters: any[] = [];
|
const parameters: any[] = [];
|
||||||
|
|
||||||
// 基础查询
|
// 基础查询
|
||||||
|
|
@ -629,6 +632,14 @@ export class OrderService {
|
||||||
totalQuery += ` AND o.date_created <= ?`;
|
totalQuery += ` AND o.date_created <= ?`;
|
||||||
parameters.push(endDate);
|
parameters.push(endDate);
|
||||||
}
|
}
|
||||||
|
const user = await this.userModel.findOneBy({id: userId});
|
||||||
|
if (user?.permissions?.includes('order-10-days')) {
|
||||||
|
sqlQuery += ` AND o.date_created >= ?`;
|
||||||
|
totalQuery += ` AND o.date_created >= ?`;
|
||||||
|
const tenDaysAgo = new Date();
|
||||||
|
tenDaysAgo.setDate(tenDaysAgo.getDate() - 10);
|
||||||
|
parameters.push(tenDaysAgo.toISOString());
|
||||||
|
}
|
||||||
|
|
||||||
// 处理 status 参数
|
// 处理 status 参数
|
||||||
if (status) {
|
if (status) {
|
||||||
|
|
@ -738,7 +749,7 @@ export class OrderService {
|
||||||
return await query.getRawMany();
|
return await query.getRawMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrderSales({ siteId, startDate, endDate, current, pageSize, name }: QueryOrderSalesDTO) {
|
async getOrderSales({ siteId, startDate, endDate, current, pageSize, name, exceptPackage }: QueryOrderSalesDTO) {
|
||||||
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
const nameKeywords = name ? name.split(' ').filter(Boolean) : [];
|
||||||
const offset = (current - 1) * pageSize;
|
const offset = (current - 1) * pageSize;
|
||||||
|
|
||||||
|
|
@ -785,6 +796,16 @@ export class OrderService {
|
||||||
itemSql += ' AND os.siteId = ?';
|
itemSql += ' AND os.siteId = ?';
|
||||||
itemParams.push(siteId);
|
itemParams.push(siteId);
|
||||||
}
|
}
|
||||||
|
if (exceptPackage) {
|
||||||
|
itemSql += `
|
||||||
|
AND os.orderId IN (
|
||||||
|
SELECT orderId
|
||||||
|
FROM order_sale
|
||||||
|
GROUP BY orderId
|
||||||
|
HAVING COUNT(*) = 1
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
}
|
||||||
itemSql += nameCondition;
|
itemSql += nameCondition;
|
||||||
itemSql += `
|
itemSql += `
|
||||||
GROUP BY os.productId, os.name
|
GROUP BY os.productId, os.name
|
||||||
|
|
@ -802,17 +823,17 @@ export class OrderService {
|
||||||
const pcParams: any[] = [...productIds, startDate, endDate];
|
const pcParams: any[] = [...productIds, startDate, endDate];
|
||||||
if (siteId) pcParams.push(siteId);
|
if (siteId) pcParams.push(siteId);
|
||||||
|
|
||||||
const pcSql = `
|
let pcSql = `
|
||||||
SELECT
|
SELECT
|
||||||
os.productId,
|
os.productId,
|
||||||
SUM(CASE WHEN t.purchaseIndex = 1 THEN os.quantity ELSE 0 END) AS firstOrderYOONEBoxCount,
|
SUM(CASE WHEN t.purchaseIndex = 1 THEN os.quantity ELSE 0 END) AS firstOrderYOONEBoxCount,
|
||||||
COUNT(CASE WHEN t.purchaseIndex = 1 THEN 1 END) AS firstOrderCount,
|
COUNT(DISTINCT CASE WHEN t.purchaseIndex = 1 THEN os.orderId END) AS firstOrderCount,
|
||||||
SUM(CASE WHEN t.purchaseIndex = 2 THEN os.quantity ELSE 0 END) AS secondOrderYOONEBoxCount,
|
SUM(CASE WHEN t.purchaseIndex = 2 THEN os.quantity ELSE 0 END) AS secondOrderYOONEBoxCount,
|
||||||
COUNT(CASE WHEN t.purchaseIndex = 2 THEN 1 END) AS secondOrderCount,
|
COUNT(DISTINCT CASE WHEN t.purchaseIndex = 2 THEN os.orderId END) AS secondOrderCount,
|
||||||
SUM(CASE WHEN t.purchaseIndex = 3 THEN os.quantity ELSE 0 END) AS thirdOrderYOONEBoxCount,
|
SUM(CASE WHEN t.purchaseIndex = 3 THEN os.quantity ELSE 0 END) AS thirdOrderYOONEBoxCount,
|
||||||
COUNT(CASE WHEN t.purchaseIndex = 3 THEN 1 END) AS thirdOrderCount,
|
COUNT(DISTINCT CASE WHEN t.purchaseIndex = 3 THEN os.orderId END) AS thirdOrderCount,
|
||||||
SUM(CASE WHEN t.purchaseIndex > 3 THEN os.quantity ELSE 0 END) AS moreThirdOrderYOONEBoxCount,
|
SUM(CASE WHEN t.purchaseIndex > 3 THEN os.quantity ELSE 0 END) AS moreThirdOrderYOONEBoxCount,
|
||||||
COUNT(CASE WHEN t.purchaseIndex > 3 THEN 1 END) AS moreThirdOrderCount
|
COUNT(DISTINCT CASE WHEN t.purchaseIndex > 3 THEN os.orderId END) AS moreThirdOrderCount
|
||||||
FROM order_sale os
|
FROM order_sale os
|
||||||
INNER JOIN (
|
INNER JOIN (
|
||||||
SELECT o2.id AS orderId,
|
SELECT o2.id AS orderId,
|
||||||
|
|
@ -829,9 +850,22 @@ export class OrderService {
|
||||||
WHERE date_paid BETWEEN ? AND ?
|
WHERE date_paid BETWEEN ? AND ?
|
||||||
${siteId ? 'AND siteId = ?' : ''}
|
${siteId ? 'AND siteId = ?' : ''}
|
||||||
)
|
)
|
||||||
|
`;
|
||||||
|
if (exceptPackage) {
|
||||||
|
pcSql += `
|
||||||
|
AND os.orderId IN (
|
||||||
|
SELECT orderId
|
||||||
|
FROM order_sale
|
||||||
|
GROUP BY orderId
|
||||||
|
HAVING COUNT(*) = 1
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
pcSql += `
|
||||||
GROUP BY os.productId
|
GROUP BY os.productId
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
console.log('------3.5-----', pcSql, pcParams, exceptPackage);
|
||||||
const pcResults = await this.orderSaleModel.query(pcSql, pcParams);
|
const pcResults = await this.orderSaleModel.query(pcSql, pcParams);
|
||||||
|
|
||||||
const pcMap = new Map<number, any>();
|
const pcMap = new Map<number, any>();
|
||||||
|
|
@ -1286,7 +1320,8 @@ export class OrderService {
|
||||||
productId: product.id,
|
productId: product.id,
|
||||||
name: product.name,
|
name: product.name,
|
||||||
sku: sale.sku,
|
sku: sale.sku,
|
||||||
quantity: sale.quantity
|
quantity: sale.quantity,
|
||||||
|
// externalOrderItemId:
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ export class UserService {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
deviceId,
|
deviceId,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
|
isSuper: user.isSuper,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { WpSite } from '../interface';
|
||||||
import { WpProduct } from '../entity/wp_product.entity';
|
import { WpProduct } from '../entity/wp_product.entity';
|
||||||
import { Variation } from '../entity/variation.entity';
|
import { Variation } from '../entity/variation.entity';
|
||||||
import { UpdateVariationDTO, UpdateWpProductDTO } from '../dto/wp_product.dto';
|
import { UpdateVariationDTO, UpdateWpProductDTO } from '../dto/wp_product.dto';
|
||||||
|
import { ProductStatus, ProductStockStatus } from '../enums/base.enum';
|
||||||
|
|
||||||
@Provide()
|
@Provide()
|
||||||
export class WPService {
|
export class WPService {
|
||||||
|
|
@ -222,6 +223,26 @@ export class WPService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 WooCommerce 产品 上下架状态
|
||||||
|
* @param productId 产品 ID
|
||||||
|
* @param status 状态
|
||||||
|
* @param stock_status 上下架状态
|
||||||
|
*/
|
||||||
|
async updateProductStatus(
|
||||||
|
site: WpSite,
|
||||||
|
productId: string,
|
||||||
|
status: ProductStatus,
|
||||||
|
stock_status: ProductStockStatus
|
||||||
|
): Promise<Boolean> {
|
||||||
|
const res = await this.updateData(`/wc/v3/products/${productId}`, site, {
|
||||||
|
status,
|
||||||
|
stock_status,
|
||||||
|
});
|
||||||
|
console.log('res', res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新 WooCommerce 产品变体
|
* 更新 WooCommerce 产品变体
|
||||||
* @param productId 产品 ID
|
* @param productId 产品 ID
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
UpdateWpProductDTO,
|
UpdateWpProductDTO,
|
||||||
} from '../dto/wp_product.dto';
|
} from '../dto/wp_product.dto';
|
||||||
import { Product } from '../entity/product.entty';
|
import { Product } from '../entity/product.entty';
|
||||||
|
import { ProductStatus, ProductStockStatus } from '../enums/base.enum';
|
||||||
|
|
||||||
@Provide()
|
@Provide()
|
||||||
export class WpProductService {
|
export class WpProductService {
|
||||||
|
|
@ -26,7 +27,7 @@ export class WpProductService {
|
||||||
@InjectEntityModel(Variation)
|
@InjectEntityModel(Variation)
|
||||||
variationModel: Repository<Variation>;
|
variationModel: Repository<Variation>;
|
||||||
|
|
||||||
geSite(id: string): WpSite {
|
getSite(id: string): WpSite {
|
||||||
let idx = this.sites.findIndex(item => item.id === id);
|
let idx = this.sites.findIndex(item => item.id === id);
|
||||||
return this.sites[idx];
|
return this.sites[idx];
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +46,7 @@ export class WpProductService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncSite(siteId: string) {
|
async syncSite(siteId: string) {
|
||||||
const site = this.geSite(siteId);
|
const site = this.getSite(siteId);
|
||||||
const products = await this.wpApiService.getProducts(site);
|
const products = await this.wpApiService.getProducts(site);
|
||||||
for (const product of products) {
|
for (const product of products) {
|
||||||
const variations =
|
const variations =
|
||||||
|
|
@ -56,6 +57,21 @@ export class WpProductService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 控制产品上下架
|
||||||
|
async updateProductStatus(id: number, status: ProductStatus, stock_status: ProductStockStatus) {
|
||||||
|
const wpProduct = await this.wpProductModel.findOneBy({ id });
|
||||||
|
const site = await this.getSite(wpProduct.siteId);
|
||||||
|
wpProduct.status = status;
|
||||||
|
wpProduct.stockStatus = stock_status;
|
||||||
|
const res = await this.wpApiService.updateProductStatus(site, wpProduct.externalProductId, status, stock_status);
|
||||||
|
if (res === true) {
|
||||||
|
this.wpProductModel.save(wpProduct);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async findProduct(
|
async findProduct(
|
||||||
siteId: string,
|
siteId: string,
|
||||||
externalProductId: string
|
externalProductId: string
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
function camelToSnake(str: string): string {
|
||||||
|
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function snakeToCamel(str: string): string {
|
||||||
|
return str.replace(/(_[\w])/g, (match) =>
|
||||||
|
match[1].toUpperCase()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertKeysFromSnakeToCamel<T>(obj: T): T {
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return obj.map(convertKeysFromSnakeToCamel) as unknown as T;
|
||||||
|
} else if (obj !== null && typeof obj === 'object') {
|
||||||
|
return Object.keys(obj).reduce((acc, key) => {
|
||||||
|
const newKey = snakeToCamel(key);
|
||||||
|
acc[newKey] = convertKeysFromSnakeToCamel((obj as any)[key]);
|
||||||
|
return acc;
|
||||||
|
}, {} as any);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function convertKeysFromCamelToSnake<T>(obj: T): T {
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return obj.map(convertKeysFromCamelToSnake) as unknown as T;
|
||||||
|
} else if (obj !== null && typeof obj === 'object') {
|
||||||
|
return Object.keys(obj).reduce((acc, key) => {
|
||||||
|
const newKey = camelToSnake(key);
|
||||||
|
acc[newKey] = convertKeysFromCamelToSnake((obj as any)[key]);
|
||||||
|
return acc;
|
||||||
|
}, {} as any);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue