Skip to content

Commit 69f627d

Browse files
committed
fix: type define
1 parent c9dd9ee commit 69f627d

File tree

5 files changed

+183
-89
lines changed

5 files changed

+183
-89
lines changed

scripts/transformers/data-transformer.ts

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,49 +9,52 @@ import { DataUtils } from '../utils/data-utils';
99
export class DataTransformer {
1010
static transformOrganization = (
1111
organization: Organization,
12-
): OrganizationData => ({
13-
contactUser: UserTransformer.transformUser(organization),
14-
name: organization['常用名称'] || organization.name || '',
15-
code: organization['机构信用代码'] || organization.code || '',
16-
entityType: DataUtils.transformEntityType(
17-
organization['实体类型'] || organization.entityType,
18-
),
19-
registrationCountry: DataUtils.transformRegistrationCountry(
20-
organization['注册国籍'] || organization.registrationCountry,
21-
),
22-
establishedDate: DateTransformer.parseDate(
23-
organization['成立时间'] || organization.establishedDate,
24-
),
25-
coverageArea: ServiceTransformer.extractCoverageFromDescription(
26-
organization['机构/项目简介'] || organization.description,
27-
),
28-
description: DataUtils.cleanDescription(
29-
organization['机构/项目简介'] || organization.description || '',
30-
),
31-
staffCount: DataUtils.parseStaffCount(
32-
organization['机构/项目全职人数'] || organization.staffCount,
33-
),
34-
address: AddressTransformer.transformAddress({
35-
province: AddressTransformer.extractProvinceFromAddress(
36-
organization['注册地'] || organization['具体地址'],
12+
): OrganizationData => {
13+
const contactUser = UserTransformer.transformUser(organization);
14+
return {
15+
contactUser,
16+
name: organization['常用名称'] || organization.name || '',
17+
code: organization['机构信用代码'] || organization.code || '',
18+
entityType: DataUtils.transformEntityType(
19+
organization['实体类型'] || organization.entityType,
3720
),
38-
city: AddressTransformer.extractCityFromAddress(
39-
organization['注册地'] || organization['具体地址'],
21+
registrationCountry: DataUtils.transformRegistrationCountry(
22+
organization['注册国籍'] || organization.registrationCountry,
4023
),
41-
district: AddressTransformer.extractDistrictFromAddress(
42-
organization['注册地'] || organization['具体地址'],
24+
establishedDate: DateTransformer.parseDate(
25+
organization['成立时间'] || organization.establishedDate,
4326
),
44-
street: organization['具体地址'] || organization.address?.street || '',
45-
}),
46-
services: ServiceTransformer.transformServices(organization),
47-
internetContact: ServiceTransformer.transformContacts(organization),
48-
qualifications:
49-
QualificationTransformer.transformQualifications(organization),
50-
});
27+
coverageArea: ServiceTransformer.extractCoverageFromDescription(
28+
organization['机构/项目简介'] || organization.description,
29+
),
30+
description: DataUtils.cleanDescription(
31+
organization['机构/项目简介'] || organization.description || '',
32+
),
33+
staffCount: DataUtils.parseStaffCount(
34+
organization['机构/项目全职人数'] || organization.staffCount,
35+
),
36+
address: AddressTransformer.transformAddress({
37+
province: AddressTransformer.extractProvinceFromAddress(
38+
organization['注册地'] || organization['具体地址'],
39+
),
40+
city: AddressTransformer.extractCityFromAddress(
41+
organization['注册地'] || organization['具体地址'],
42+
),
43+
district: AddressTransformer.extractDistrictFromAddress(
44+
organization['注册地'] || organization['具体地址'],
45+
),
46+
street: organization['具体地址'] || organization.address?.street || '',
47+
}),
48+
services: ServiceTransformer.transformServices(organization),
49+
internetContact: ServiceTransformer.transformContacts(organization),
50+
qualifications:
51+
QualificationTransformer.transformQualifications(organization),
52+
};
53+
};
5154

5255
static transformUser = (
5356
organization: Organization,
54-
organizationId?: number,
57+
_organizationId?: number,
5558
): ExtendedUserData | null => {
5659
return UserTransformer.transformUser(organization);
5760
};

scripts/transformers/user-transformer.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { ExtendedUserData } from '../types';
1+
import { ExtendedUserData, Organization } from '../types';
22

33
export class UserTransformer {
4-
static transformUser = (organization: any): ExtendedUserData | null => {
4+
static transformUser = (
5+
organization: Organization,
6+
): ExtendedUserData | null => {
57
// 获取用户信息
68
const contactName = organization['机构联系人联系人姓名'] || '';
79
const contactPhone = organization['机构联系人联系人电话'] || '';
@@ -42,12 +44,12 @@ export class UserTransformer {
4244
} as ExtendedUserData;
4345
};
4446

45-
static extractPrincipalName = (organization: any): string => {
47+
static extractPrincipalName = (organization: Organization): string => {
4648
return organization['负责人'] || '';
4749
};
4850

4951
static extractContactInfo = (
50-
organization: any,
52+
organization: Organization,
5153
): {
5254
name: string;
5355
phone: string;

scripts/types.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,38 @@ export {
1919
ServiceOrganizationServiceComponent as Service,
2020
ContactInternetContactComponent as InternetContact,
2121
QualificationCertificateComponent as Qualification,
22-
Organization as OrganizationData,
22+
// 注意:我们重新定义了 OrganizationData,所以不再从 '../types' 导出
2323
UsersPermissionsUser as UserData,
2424
} from '../types';
2525

2626
// 扩展的用户数据接口(包含自定义字段)
27-
export type ExtendedUserData = import('../types').UsersPermissionsUser & {
27+
export interface ExtendedUserData
28+
extends Omit<import('../types').UsersPermissionsUser, 'id'> {
29+
// 用户创建时不需要ID,但可以包含其他可选字段
30+
id?: number;
31+
// 自定义字段
2832
phone?: string;
29-
};
33+
// 其他可能需要的字段
34+
password?: string;
35+
// 角色可以是ID或完整对象
36+
role?: number | import('../types').UsersPermissionsRole;
37+
}
3038

31-
// 组织数据接口
32-
type OrganizationData = import('../types').Organization;
39+
// 组织数据接口 - 扩展以支持联系用户创建和引用
40+
type BaseOrganizationData = Omit<
41+
import('../types').Organization,
42+
'contactUser'
43+
>;
44+
type ExtendedOrganizationData = BaseOrganizationData & {
45+
// contactUser 可以是用户对象(用于创建)或用户ID(用于引用)
46+
contactUser?: ExtendedUserData | number | null;
47+
};
48+
export type OrganizationData = ExtendedOrganizationData;
3349

3450
// Excel行数据接口
3551
export interface Organization extends Partial<import('../types').Organization> {
52+
// 添加索引签名以支持动态中文属性名访问
53+
[key: string]: any;
3654
常用名称?: string;
3755
机构信用代码?: string;
3856
实体类型?: string;
@@ -46,6 +64,10 @@ export interface Organization extends Partial<import('../types').Organization> {
4664
机构微信公众号?: string;
4765
机构微博?: string;
4866
登记管理机关?: string;
67+
负责人?: string;
68+
机构联系人联系人姓名?: string;
69+
机构联系人联系人电话?: string;
70+
机构联系人联系人邮箱?: string;
4971
}
5072

5173
// 导入统计接口

scripts/utils/data-importer.ts

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
import { splitArray } from 'web-utility';
22

3-
import { OrganizationData, ImportStats } from '../types';
3+
import { OrganizationData, ImportStats, ExtendedUserData } from '../types';
44
import { StrapiAPI } from './strapi-api';
55
import { ImportLogger } from './import-logger';
66

7+
// 类型守卫函数
8+
function hasId(
9+
user: ExtendedUserData | null | undefined,
10+
): user is ExtendedUserData & { id: number } {
11+
return user !== null && user !== undefined && typeof user.id === 'number';
12+
}
13+
14+
function isExtendedUserData(obj: any): obj is ExtendedUserData {
15+
return obj && typeof obj === 'object' && 'email' in obj && 'username' in obj;
16+
}
17+
718
export class DataImporter {
819
public logger: ImportLogger;
920
private stats: ImportStats;
@@ -81,41 +92,67 @@ export class DataImporter {
8192
}
8293

8394
// 清理数据,移除内部字段
84-
const cleanOrgData = { ...org };
85-
delete (cleanOrgData as any)._originalData;
95+
const cleanOrgData: OrganizationData = { ...org };
96+
// 使用类型断言安全地移除内部字段
97+
if ('_originalData' in cleanOrgData) {
98+
delete (
99+
cleanOrgData as OrganizationData & { _originalData?: unknown }
100+
)._originalData;
101+
}
86102

87103
// 如果有联系人用户,先创建用户
88104
if (cleanOrgData.contactUser) {
89105
try {
90106
// 验证用户数据
91-
const contactUser = cleanOrgData.contactUser as any;
92-
if (!contactUser.email || !contactUser.username) {
93-
throw new Error('用户数据缺少必需字段:email 或 username');
94-
}
95-
96-
// 检查用户是否已存在
97-
const existingUser = await this.api.findUserByEmail(
98-
contactUser.email,
99-
);
100-
if (existingUser) {
101-
console.log(`✓ 使用现有用户: ${contactUser.username}`);
102-
cleanOrgData.contactUser = (existingUser as any).id;
103-
} else {
104-
const createdUser = await this.api.createUser(contactUser);
105-
console.log(`✓ 成功创建联系人用户: ${contactUser.username}`);
107+
const contactUser = cleanOrgData.contactUser;
108+
let userId: number;
109+
110+
if (typeof contactUser === 'number') {
111+
// 已经是用户ID,直接使用
112+
userId = contactUser;
113+
console.log(`✓ 使用现有用户ID: ${userId}`);
114+
} else if (isExtendedUserData(contactUser)) {
115+
// 是用户对象,检查必需字段
116+
if (!contactUser.email || !contactUser.username) {
117+
throw new Error('用户数据缺少必需字段:email 或 username');
118+
}
106119

107-
// 设置组织与用户的关联
108-
const userId = (createdUser as any).id;
109-
if (!userId) {
110-
throw new Error(`创建的用户缺少ID: ${contactUser.username}`);
120+
// 检查用户是否已存在
121+
const existingUser = await this.api.findUserByEmail(
122+
contactUser.email,
123+
);
124+
if (hasId(existingUser)) {
125+
console.log(`✓ 使用现有用户: ${contactUser.username}`);
126+
userId = existingUser.id;
127+
} else {
128+
const createdUser = await this.api.createUser(contactUser);
129+
console.log(`✓ 成功创建联系人用户: ${contactUser.username}`);
130+
131+
// 验证创建的用户有ID
132+
if (!hasId(createdUser)) {
133+
throw new Error(`创建的用户缺少ID: ${contactUser.username}`);
134+
}
135+
userId = createdUser.id;
111136
}
112-
cleanOrgData.contactUser = userId;
137+
} else {
138+
throw new Error('contactUser 字段类型无效');
113139
}
114-
} catch (userError: any) {
140+
141+
// 设置组织与用户的关联
142+
cleanOrgData.contactUser = userId;
143+
} catch (userError: unknown) {
144+
const errorMessage =
145+
userError instanceof Error
146+
? userError.message
147+
: String(userError);
115148
const username =
116-
(cleanOrgData.contactUser as any)?.username || 'unknown';
117-
console.error(`✗ 用户操作失败: ${username}`, userError.message);
118-
this.logger.logFailed(org, userError);
149+
typeof cleanOrgData.contactUser === 'object' &&
150+
cleanOrgData.contactUser &&
151+
'username' in cleanOrgData.contactUser
152+
? (cleanOrgData.contactUser as ExtendedUserData).username
153+
: 'unknown';
154+
console.error(`✗ 用户操作失败: ${username}`, errorMessage);
155+
this.logger.logFailed(org, userError as Error);
119156
this.stats.failed++;
120157
continue;
121158
}

scripts/utils/strapi-api.ts

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,31 @@ import { HTTPClient } from 'koajax';
22

33
import { OrganizationData, ExtendedUserData } from '../types';
44

5+
// 定义更精确的 API 响应类型
6+
interface StrapiResponse<T> {
7+
data: T;
8+
meta?: {
9+
pagination?: {
10+
page: number;
11+
pageSize: number;
12+
pageCount: number;
13+
total: number;
14+
};
15+
};
16+
}
17+
18+
interface StrapiListResponse<T> {
19+
data: T[];
20+
meta?: {
21+
pagination?: {
22+
page: number;
23+
pageSize: number;
24+
pageCount: number;
25+
total: number;
26+
};
27+
};
28+
}
29+
530
export class StrapiAPI {
631
constructor(
732
private baseURL: string,
@@ -18,32 +43,37 @@ export class StrapiAPI {
1843
return next();
1944
});
2045

21-
async createOrganization(data: OrganizationData) {
22-
const { body } = await this.client.post<OrganizationData>(
46+
async createOrganization(data: OrganizationData): Promise<OrganizationData> {
47+
const { body } = await this.client.post<StrapiResponse<OrganizationData>>(
2348
'/api/organizations',
2449
{ data },
2550
);
26-
return body;
51+
return body.data;
2752
}
2853

29-
async findOrganizationByName(name: string) {
30-
const { body } = await this.client.get<{ data: OrganizationData[] }>(
31-
`/api/organizations?filters[name][$eq]=${encodeURIComponent(name)}`,
32-
);
54+
async findOrganizationByName(
55+
name: string,
56+
): Promise<OrganizationData | undefined> {
57+
const { body } = await this.client.get<
58+
StrapiListResponse<OrganizationData>
59+
>(`/api/organizations?filters[name][$eq]=${encodeURIComponent(name)}`);
3360
return body.data?.[0];
3461
}
3562

36-
async findUserByEmail(email: string) {
37-
const { body } = await this.client.get<{ data: ExtendedUserData[] }>(
38-
`/api/users?filters[email][$eq]=${encodeURIComponent(email)}`,
39-
);
63+
async findUserByEmail(email: string): Promise<ExtendedUserData | undefined> {
64+
const { body } = await this.client.get<
65+
StrapiListResponse<ExtendedUserData>
66+
>(`/api/users?filters[email][$eq]=${encodeURIComponent(email)}`);
4067
return body.data?.[0];
4168
}
4269

43-
async createUser(userData: ExtendedUserData) {
44-
const { body } = await this.client.post<ExtendedUserData>('/api/users', {
45-
data: userData,
46-
});
47-
return body;
70+
async createUser(userData: ExtendedUserData): Promise<ExtendedUserData> {
71+
const { body } = await this.client.post<StrapiResponse<ExtendedUserData>>(
72+
'/api/users',
73+
{
74+
data: userData,
75+
},
76+
);
77+
return body.data;
4878
}
4979
}

0 commit comments

Comments
 (0)