feat(area): 重构区域模块,使用i18n-iso-countries管理国家数据
refactor: 统一将productName字段重命名为name chore: 添加i18n-iso-countries依赖 style: 优化字典名称格式化逻辑
This commit is contained in:
parent
10b42eca7a
commit
f20f4727f6
|
|
@ -27,6 +27,7 @@
|
|||
"class-transformer": "^0.5.1",
|
||||
"csv-parse": "^6.1.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"i18n-iso-countries": "^7.14.0",
|
||||
"mysql2": "^3.15.3",
|
||||
"nodemailer": "^7.0.5",
|
||||
"npm-check-updates": "^19.1.2",
|
||||
|
|
@ -2044,6 +2045,12 @@
|
|||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/diacritics": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
|
||||
"integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
|
|
@ -2691,6 +2698,18 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/i18n-iso-countries": {
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.14.0.tgz",
|
||||
"integrity": "sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"diacritics": "1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.0.tgz",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"class-transformer": "^0.5.1",
|
||||
"csv-parse": "^6.1.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"i18n-iso-countries": "^7.14.0",
|
||||
"mysql2": "^3.15.3",
|
||||
"nodemailer": "^7.0.5",
|
||||
"npm-check-updates": "^19.1.2",
|
||||
|
|
|
|||
|
|
@ -1,15 +1,5 @@
|
|||
|
||||
import { Inject } from '@midwayjs/core';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Del,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Query,
|
||||
} from '@midwayjs/decorator';
|
||||
import { Body, Context, Controller, Del, Get, Inject, Param, Post, Put, Query } from '@midwayjs/core';
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiBody,
|
||||
|
|
@ -22,14 +12,39 @@ import { AreaService } from '../service/area.service';
|
|||
import { CreateAreaDTO, QueryAreaDTO, UpdateAreaDTO } from '../dto/area.dto';
|
||||
import { errorResponse, successResponse } from '../utils/response.util';
|
||||
import { Area } from '../entity/area.entity';
|
||||
import * as countries from 'i18n-iso-countries';
|
||||
|
||||
@ApiBearerAuth()
|
||||
@ApiTags('Area')
|
||||
@Controller('/api/area')
|
||||
@Controller('/area')
|
||||
export class AreaController {
|
||||
@Inject()
|
||||
ctx: Context;
|
||||
|
||||
@Inject()
|
||||
areaService: AreaService;
|
||||
|
||||
@ApiOperation({ summary: '获取国家列表' })
|
||||
@ApiOkResponse({ description: '国家列表' })
|
||||
@Get('/countries')
|
||||
async getCountries() {
|
||||
try {
|
||||
// 注册中文语言包
|
||||
countries.registerLocale(require('i18n-iso-countries/langs/zh.json'));
|
||||
// 获取所有国家的中文名称
|
||||
const countryNames = countries.getNames('zh', { select: 'official' });
|
||||
// 格式化为 { code, name } 的数组
|
||||
const countryList = Object.keys(countryNames).map(code => ({
|
||||
code,
|
||||
name: countryNames[code],
|
||||
}));
|
||||
return successResponse(countryList, '查询成功');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return errorResponse(error?.message || error);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: '创建区域' })
|
||||
@ApiBody({ type: CreateAreaDTO })
|
||||
@ApiOkResponse({ type: Area, description: '成功创建的区域' })
|
||||
|
|
@ -39,7 +54,8 @@ export class AreaController {
|
|||
const newArea = await this.areaService.createArea(area);
|
||||
return successResponse(newArea, '创建成功');
|
||||
} catch (error) {
|
||||
return errorResponse(error.message);
|
||||
console.log(error);
|
||||
return errorResponse(error?.message || error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +68,8 @@ export class AreaController {
|
|||
const updatedArea = await this.areaService.updateArea(id, area);
|
||||
return successResponse(updatedArea, '更新成功');
|
||||
} catch (error) {
|
||||
return errorResponse(error.message);
|
||||
console.log(error);
|
||||
return errorResponse(error?.message || error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +81,8 @@ export class AreaController {
|
|||
await this.areaService.deleteArea(id);
|
||||
return successResponse(null, '删除成功');
|
||||
} catch (error) {
|
||||
return errorResponse(error.message);
|
||||
console.log(error);
|
||||
return errorResponse(error?.message || error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,19 +95,8 @@ export class AreaController {
|
|||
const { list, total } = await this.areaService.getAreaList(query);
|
||||
return successResponse({ list, total }, '查询成功');
|
||||
} catch (error) {
|
||||
return errorResponse(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: '获取所有区域' })
|
||||
@ApiOkResponse({ type: [Area], description: '所有区域列表' })
|
||||
@Get('/all')
|
||||
async getAllAreas() {
|
||||
try {
|
||||
const areas = await this.areaService.getAllAreas();
|
||||
return successResponse(areas, '查询成功');
|
||||
} catch (error) {
|
||||
return errorResponse(error.message);
|
||||
console.log(error);
|
||||
return errorResponse(error?.message || error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +111,8 @@ export class AreaController {
|
|||
}
|
||||
return successResponse(area, '查询成功');
|
||||
} catch (error) {
|
||||
return errorResponse(error.message);
|
||||
console.log(error);
|
||||
return errorResponse(error?.message || error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,9 +134,9 @@ export class ProductController {
|
|||
|
||||
@ApiOkResponse({ type: ProductRes })
|
||||
@Put('updateNameCn/:id/:nameCn')
|
||||
async updateProductNameCn(@Param('id') id: number, @Param('nameCn') nameCn: string) {
|
||||
async updatenameCn(@Param('id') id: number, @Param('nameCn') nameCn: string) {
|
||||
try {
|
||||
const data = this.productService.updateProductNameCn(id, nameCn);
|
||||
const data = this.productService.updatenameCn(id, nameCn);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
|
|||
|
|
@ -8,18 +8,21 @@ export default class AreaSeeder implements Seeder {
|
|||
dataSource: DataSource,
|
||||
factoryManager: SeederFactoryManager
|
||||
): Promise<any> {
|
||||
const repository = dataSource.getRepository(Area);
|
||||
const areaRepository = dataSource.getRepository(Area);
|
||||
|
||||
const areas = [
|
||||
{ name: '加拿大' },
|
||||
{ name: '澳大利亚' },
|
||||
{ name: '欧洲' },
|
||||
{ name: 'Australia' },
|
||||
{ name: 'Canada' },
|
||||
{ name: 'United States' },
|
||||
{ name: 'Germany' },
|
||||
{ name: 'Poland' },
|
||||
];
|
||||
|
||||
for (const area of areas) {
|
||||
const existing = await repository.findOne({ where: { name: area.name } });
|
||||
if (!existing) {
|
||||
await repository.insert(area);
|
||||
for (const areaData of areas) {
|
||||
const existingArea = await areaRepository.findOne({ where: { name: areaData.name } });
|
||||
if (!existingArea) {
|
||||
const newArea = areaRepository.create(areaData);
|
||||
await areaRepository.save(newArea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ export default class DictSeeder implements Seeder {
|
|||
* @returns 格式化后的名称
|
||||
*/
|
||||
private formatName(name: string): string {
|
||||
// 将空格、下划线、点统一转换成中划线,并转为小写
|
||||
return String(name).replace(/[_\s.]+/g, '-').toLowerCase();
|
||||
// return String(name).replace(/[\_\s.]+/g, '-').toLowerCase();
|
||||
// 只替换空格和下划线
|
||||
return String(name).replace(/[\_\s]+/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
public async run(
|
||||
|
|
|
|||
|
|
@ -2,47 +2,28 @@
|
|||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
import { Rule, RuleType } from '@midwayjs/validate';
|
||||
|
||||
// 创建区域的数据传输对象
|
||||
export class CreateAreaDTO {
|
||||
@ApiProperty({ type: 'string', description: '区域名称', example: '欧洲' })
|
||||
@ApiProperty({ description: '编码' })
|
||||
@Rule(RuleType.string().required())
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ type: 'number', description: '纬度', example: 48.8566, required: false })
|
||||
@Rule(RuleType.number().min(-90).max(90).allow(null))
|
||||
latitude?: number;
|
||||
|
||||
@ApiProperty({ type: 'number', description: '经度', example: 2.3522, required: false })
|
||||
@Rule(RuleType.number().min(-180).max(180).allow(null))
|
||||
longitude?: number;
|
||||
code: string;
|
||||
}
|
||||
|
||||
// 更新区域的数据传输对象
|
||||
export class UpdateAreaDTO {
|
||||
@ApiProperty({ type: 'string', description: '区域名称', example: '欧洲' })
|
||||
@ApiProperty({ description: '编码', required: false })
|
||||
@Rule(RuleType.string())
|
||||
name?: string;
|
||||
|
||||
@ApiProperty({ type: 'number', description: '纬度', example: 48.8566, required: false })
|
||||
@Rule(RuleType.number().min(-90).max(90).allow(null))
|
||||
latitude?: number;
|
||||
|
||||
@ApiProperty({ type: 'number', description: '经度', example: 2.3522, required: false })
|
||||
@Rule(RuleType.number().min(-180).max(180).allow(null))
|
||||
longitude?: number;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
// 查询区域的数据传输对象
|
||||
export class QueryAreaDTO {
|
||||
@ApiProperty({ type: 'number', description: '当前页码', example: 1 })
|
||||
@Rule(RuleType.number().min(1).default(1))
|
||||
currentPage: number;
|
||||
@ApiProperty({ description: '当前页', required: false, default: 1 })
|
||||
@Rule(RuleType.number().integer().min(1).default(1))
|
||||
currentPage?: number;
|
||||
|
||||
@ApiProperty({ type: 'number', description: '每页数量', example: 10 })
|
||||
@Rule(RuleType.number().min(1).max(100).default(10))
|
||||
pageSize: number;
|
||||
@ApiProperty({ description: '每页数量', required: false, default: 10 })
|
||||
@Rule(RuleType.number().integer().min(1).default(10))
|
||||
pageSize?: number;
|
||||
|
||||
@ApiProperty({ type: 'string', description: '区域名称', example: '欧洲' })
|
||||
@ApiProperty({ description: '关键词(名称或编码)', required: false })
|
||||
@Rule(RuleType.string())
|
||||
name?: string;
|
||||
keyword?: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export class QueryStockDTO {
|
|||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
productName: string;
|
||||
name: string;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
|
|
@ -62,7 +62,7 @@ export class QueryStockRecordDTO {
|
|||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
productName: string;
|
||||
name: string;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
|
|
@ -96,7 +96,7 @@ export class QueryPurchaseOrderDTO {
|
|||
export class StockDTO extends Stock {
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
productName: string;
|
||||
name: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: 'object',
|
||||
|
|
|
|||
|
|
@ -1,43 +1,17 @@
|
|||
|
||||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
||||
|
||||
@Entity('area')
|
||||
export class Area {
|
||||
@ApiProperty({ type: 'number' })
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ type: 'string', description: '区域名称' })
|
||||
@Column({ unique: true })
|
||||
@ApiProperty({ description: '名称' })
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ type: 'number', description: '纬度', required: false })
|
||||
@Column({ type: 'decimal', precision: 10, scale: 6, nullable: true })
|
||||
latitude?: number;
|
||||
|
||||
@ApiProperty({ type: 'number', description: '经度', required: false })
|
||||
@Column({ type: 'decimal', precision: 10, scale: 6, nullable: true })
|
||||
longitude?: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: '2022-12-12 11:11:11',
|
||||
description: '创建时间',
|
||||
required: true,
|
||||
})
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@ApiProperty({
|
||||
example: '2022-12-12 11:11:11',
|
||||
description: '更新时间',
|
||||
required: true,
|
||||
})
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
@ApiProperty({ description: '编码' })
|
||||
@Column({ unique: true })
|
||||
code: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export class PurchaseOrderItem {
|
|||
|
||||
@ApiProperty({ type: String })
|
||||
@Column()
|
||||
productName: string;
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ type: Number })
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export class TransferItem {
|
|||
|
||||
@ApiProperty({ type: String })
|
||||
@Column()
|
||||
productName: string;
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ type: Number })
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -1,102 +1,80 @@
|
|||
|
||||
import { Provide } from '@midwayjs/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Like, Repository } from 'typeorm';
|
||||
import { Area } from '../entity/area.entity';
|
||||
import { CreateAreaDTO, QueryAreaDTO, UpdateAreaDTO } from '../dto/area.dto';
|
||||
import * as countries from 'i18n-iso-countries';
|
||||
|
||||
@Provide()
|
||||
export class AreaService {
|
||||
@InjectEntityModel(Area)
|
||||
areaModel: Repository<Area>;
|
||||
areaRepository: Repository<Area>;
|
||||
|
||||
/**
|
||||
* 创建区域
|
||||
* @param params 创建参数
|
||||
*/
|
||||
async createArea(params: CreateAreaDTO) {
|
||||
// 检查区域名称是否已存在
|
||||
const existing = await this.areaModel.findOne({ where: { name: params.name } });
|
||||
if (existing) {
|
||||
throw new Error('区域名称已存在');
|
||||
}
|
||||
const area = new Area();
|
||||
area.name = params.name;
|
||||
if (params.latitude !== undefined) {
|
||||
area.latitude = params.latitude;
|
||||
}
|
||||
if (params.longitude !== undefined) {
|
||||
area.longitude = params.longitude;
|
||||
}
|
||||
return await this.areaModel.save(area);
|
||||
constructor() {
|
||||
// 在服务初始化时注册中文语言包
|
||||
countries.registerLocale(require('i18n-iso-countries/langs/zh.json'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新区域
|
||||
* @param id 区域ID
|
||||
* @param params 更新参数
|
||||
*/
|
||||
async updateArea(id: number, params: UpdateAreaDTO) {
|
||||
const area = await this.areaModel.findOneBy({ id });
|
||||
if (!area) {
|
||||
throw new Error('区域不存在');
|
||||
}
|
||||
if (params.name) {
|
||||
// 检查新的区域名称是否已存在
|
||||
const existing = await this.areaModel.findOne({ where: { name: params.name } });
|
||||
if (existing && existing.id !== id) {
|
||||
throw new Error('区域名称已存在');
|
||||
}
|
||||
area.name = params.name;
|
||||
}
|
||||
if (params.latitude !== undefined) {
|
||||
area.latitude = params.latitude;
|
||||
}
|
||||
if (params.longitude !== undefined) {
|
||||
area.longitude = params.longitude;
|
||||
}
|
||||
return await this.areaModel.save(area);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除区域
|
||||
* @param id 区域ID
|
||||
*/
|
||||
async deleteArea(id: number) {
|
||||
const result = await this.areaModel.delete(id);
|
||||
return result.affected > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取区域列表(分页)
|
||||
* @param query 查询参数
|
||||
*/
|
||||
async getAreaList(query: QueryAreaDTO) {
|
||||
const { currentPage, pageSize, name } = query;
|
||||
const [list, total] = await this.areaModel.findAndCount({
|
||||
where: name ? { name } : {},
|
||||
const { currentPage = 1, pageSize = 10, keyword = '' } = query;
|
||||
const [list, total] = await this.areaRepository.findAndCount({
|
||||
where: [{ name: Like(`%${keyword}%`) }, { code: Like(`%${keyword}%`) }],
|
||||
skip: (currentPage - 1) * pageSize,
|
||||
take: pageSize,
|
||||
order: {
|
||||
id: 'DESC',
|
||||
},
|
||||
});
|
||||
|
||||
return { list, total };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有区域
|
||||
*/
|
||||
async getAllAreas() {
|
||||
return await this.areaModel.find();
|
||||
async getAreaById(id: number) {
|
||||
return this.areaRepository.findOne({ where: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取区域详情
|
||||
* @param id 区域ID
|
||||
*/
|
||||
async getAreaById(id: number) {
|
||||
return await this.areaModel.findOneBy({ id });
|
||||
async createArea(createAreaDTO: CreateAreaDTO) {
|
||||
// 根据 code 获取国家中文名称
|
||||
const name = countries.getName(createAreaDTO.code, 'zh', {
|
||||
select: 'official',
|
||||
});
|
||||
|
||||
// 如果找不到对应的国家,则抛出错误
|
||||
if (!name) {
|
||||
throw new Error(`无效的国家代码: ${createAreaDTO.code}`);
|
||||
}
|
||||
|
||||
const area = new Area();
|
||||
area.name = name;
|
||||
area.code = createAreaDTO.code;
|
||||
return this.areaRepository.save(area);
|
||||
}
|
||||
|
||||
async updateArea(id: number, updateAreaDTO: UpdateAreaDTO) {
|
||||
const area = await this.getAreaById(id);
|
||||
if (!area) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果 code 发生变化,则更新 name
|
||||
if (updateAreaDTO.code && updateAreaDTO.code !== area.code) {
|
||||
const name = countries.getName(updateAreaDTO.code, 'zh', {
|
||||
select: 'official',
|
||||
});
|
||||
|
||||
if (!name) {
|
||||
throw new Error(`无效的国家代码: ${updateAreaDTO.code}`);
|
||||
}
|
||||
area.name = name;
|
||||
area.code = updateAreaDTO.code;
|
||||
}
|
||||
|
||||
return this.areaRepository.save(area);
|
||||
}
|
||||
|
||||
async deleteArea(id: number) {
|
||||
const area = await this.getAreaById(id);
|
||||
if (!area) {
|
||||
return false;
|
||||
}
|
||||
await this.areaRepository.remove(area);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ export class DictService {
|
|||
|
||||
// 格式化名称为 kebab-case
|
||||
private formatName(name: string): string {
|
||||
return String(name).replace(/[_\s.]+/g, '-').toLowerCase();
|
||||
// 只替换空格和下划线
|
||||
return String(name).replace(/[_\s]+/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
// 生成并返回字典的XLSX模板
|
||||
|
|
|
|||
|
|
@ -509,7 +509,7 @@ export class ProductService {
|
|||
|
||||
// 重复定义的 getProductList 已合并到前面的实现(中文注释:移除重复)
|
||||
|
||||
async updateProductNameCn(id: number, nameCn: string): Promise<Product> {
|
||||
async updatenameCn(id: number, nameCn: string): Promise<Product> {
|
||||
// 确认产品是否存在
|
||||
const product = await this.productModel.findOneBy({ id });
|
||||
if (!product) {
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ export class StockService {
|
|||
qb
|
||||
.select([
|
||||
'poi.purchaseOrderId AS purchaseOrderId',
|
||||
"JSON_ARRAYAGG(JSON_OBJECT('id', poi.id, 'productName', poi.productName,'sku', poi.sku, 'quantity', poi.quantity, 'price', poi.price)) AS items",
|
||||
"JSON_ARRAYAGG(JSON_OBJECT('id', poi.id, 'name', poi.name,'sku', poi.sku, 'quantity', poi.quantity, 'price', poi.price)) AS items",
|
||||
])
|
||||
.from(PurchaseOrderItem, 'poi')
|
||||
.groupBy('poi.purchaseOrderId'),
|
||||
|
|
@ -240,9 +240,9 @@ export class StockService {
|
|||
|
||||
// 获取库存列表
|
||||
async getStocks(query: QueryStockDTO) {
|
||||
const { current = 1, pageSize = 10, productName, sku } = query;
|
||||
const nameKeywords = productName
|
||||
? productName.split(' ').filter(Boolean)
|
||||
const { current = 1, pageSize = 10, name, sku } = query;
|
||||
const nameKeywords = name
|
||||
? name.split(' ').filter(Boolean)
|
||||
: [];
|
||||
|
||||
let queryBuilder = this.stockModel
|
||||
|
|
@ -250,8 +250,8 @@ export class StockService {
|
|||
.select([
|
||||
// 'stock.id as id',
|
||||
'stock.sku as sku',
|
||||
'product.name as productName',
|
||||
'product.nameCn as productNameCn',
|
||||
'product.name as name',
|
||||
'product.nameCn as nameCn',
|
||||
'JSON_ARRAYAGG(JSON_OBJECT("id", stock.stockPointId, "quantity", stock.quantity)) as stockPoint',
|
||||
'MIN(stock.updatedAt) as updatedAt',
|
||||
'MAX(stock.createdAt) as createdAt',
|
||||
|
|
@ -264,33 +264,29 @@ export class StockService {
|
|||
.createQueryBuilder('stock')
|
||||
.select('COUNT(DISTINCT stock.sku)', 'count')
|
||||
.leftJoin(Product, 'product', 'product.sku = stock.sku');
|
||||
if (sku) {
|
||||
queryBuilder.andWhere('stock.sku = :sku', { sku });
|
||||
totalQueryBuilder.andWhere('stock.sku = :sku', { sku });
|
||||
}
|
||||
if (nameKeywords.length) {
|
||||
nameKeywords.forEach((name, index) => {
|
||||
queryBuilder.andWhere(
|
||||
`EXISTS (
|
||||
SELECT 1 FROM product p
|
||||
WHERE p.sku = stock.sku
|
||||
AND p.name LIKE :name${index}
|
||||
)`,
|
||||
{ [`name${index}`]: `%${name}%` }
|
||||
);
|
||||
totalQueryBuilder.andWhere(
|
||||
`EXISTS (
|
||||
SELECT 1 FROM product p
|
||||
WHERE p.sku = stock.sku
|
||||
AND p.name LIKE :name${index}
|
||||
)`,
|
||||
{ [`name${index}`]: `%${name}%` }
|
||||
);
|
||||
if (sku || nameKeywords.length) {
|
||||
const conditions = [];
|
||||
if (sku) {
|
||||
conditions.push(`stock.sku LIKE :sku`);
|
||||
}
|
||||
if (nameKeywords.length) {
|
||||
nameKeywords.forEach((name, index) => {
|
||||
conditions.push(`product.name LIKE :name${index}`);
|
||||
});
|
||||
}
|
||||
const whereClause = conditions.join(' OR ');
|
||||
queryBuilder.andWhere(`(${whereClause})`, {
|
||||
sku: `%${sku}%`,
|
||||
...nameKeywords.reduce((acc, name, index) => ({ ...acc, [`name${index}`]: `%${name}%` }), {}),
|
||||
});
|
||||
totalQueryBuilder.andWhere(`(${whereClause})`, {
|
||||
sku: `%${sku}%`,
|
||||
...nameKeywords.reduce((acc, name, index) => ({ ...acc, [`name${index}`]: `%${name}%` }), {}),
|
||||
});
|
||||
}
|
||||
if (query.order) {
|
||||
const sortFieldMap: Record<string, string> = {
|
||||
productName: 'product.name',
|
||||
name: 'product.name',
|
||||
sku: 'stock.sku',
|
||||
updatedAt: 'updatedAt',
|
||||
createdAt: 'createdAt',
|
||||
|
|
@ -422,7 +418,7 @@ export class StockService {
|
|||
pageSize = 10,
|
||||
stockPointId,
|
||||
sku,
|
||||
productName,
|
||||
name,
|
||||
operationType,
|
||||
startDate,
|
||||
endDate,
|
||||
|
|
@ -447,9 +443,9 @@ export class StockService {
|
|||
'sp.name as stockPointName',
|
||||
])
|
||||
.where(where);
|
||||
if (productName)
|
||||
if (name)
|
||||
queryBuilder.andWhere('product.name LIKE :name', {
|
||||
name: `%${productName}%`,
|
||||
name: `%${name}%`,
|
||||
});
|
||||
const items = await queryBuilder
|
||||
.orderBy('stock_record.createdAt', 'DESC')
|
||||
|
|
|
|||
Loading…
Reference in New Issue