Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/models/responses/sendManyDetailResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,32 @@ export const detailGroupMessageResponseSchema = Schema.Struct({
export type DetailGroupMessageResponse = Schema.Schema.Type<
typeof detailGroupMessageResponseSchema
>;

// 고속접수(send-many/fast) 응답은 폐기된 친구톡(cta/cti) 과금 필드를 내려주지 않으므로
// 기존 send-many/detail 응답 계약 유지를 위해 빈 값으로 채운다.
export const withFastSendDefaults = (
response: DetailGroupMessageResponse,
): DetailGroupMessageResponse => {
const {groupInfo} = response;
const {countForCharge, app} = groupInfo;
return {
...response,
groupInfo: {
...groupInfo,
isRefunded: groupInfo.isRefunded ?? false,
countForCharge: {
...countForCharge,
cta: countForCharge.cta ?? {},
cti: countForCharge.cti ?? {},
},
app: {
...app,
profit: {
...app.profit,
cta: app.profit.cta ?? 0,
cti: app.profit.cti ?? 0,
},
},
},
};
};
3 changes: 2 additions & 1 deletion src/services/messages/groupService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,14 @@ export default class GroupService extends DefaultService {

/**
* 단일 그룹정보 조회
* 고속접수 조회 경로는 접수 직후에도 그룹을 반환하며, 일반 그룹도 동일한 형식으로 조회된다.
* @param groupId 그룹 ID
*/
async getGroup(groupId: GroupId): Promise<GroupMessageResponse> {
return runSafePromise(
this.requestEffect({
httpMethod: 'GET',
url: `messages/v4/groups/${groupId}`,
url: `messages/v4/groups/fast/${groupId}`,
responseSchema: groupMessageResponseSchema,
}),
);
Expand Down
23 changes: 14 additions & 9 deletions src/services/messages/messageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import {
getMessagesResponseSchema,
getStatisticsResponseSchema,
} from '@models/responses/messageResponses';
import {DetailGroupMessageResponse} from '@models/responses/sendManyDetailResponse';
import {
type DetailGroupMessageResponse,
withFastSendDefaults,
} from '@models/responses/sendManyDetailResponse';
import * as Effect from 'effect/Effect';
import {
BadRequestError,
Expand Down Expand Up @@ -84,14 +87,16 @@ export default class MessageService extends DefaultService {
parameterObject,
);

const response = yield* reqEffect<
MultipleMessageSendingRequestSchema,
DetailGroupMessageResponse
>({
httpMethod: 'POST',
url: 'messages/v4/send-many/detail',
body: parameter,
});
const response = withFastSendDefaults(
yield* reqEffect<
MultipleMessageSendingRequestSchema,
DetailGroupMessageResponse
>({
httpMethod: 'POST',
url: 'messages/v4/send-many/fast',
body: parameter,
}),
);

const {count} = response.groupInfo;
const failedAll =
Expand Down
14 changes: 10 additions & 4 deletions src/types/commonTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ const countryChargeStatusSchema = Schema.Record({
value: Schema.Number,
});

// 고속접수(v4-fast) 그룹은 폐기된 친구톡(cta/cti) 키 없이 저장되므로 조회 응답에서 기본값으로 채운다.
export const countForChargeSchema = Schema.Struct({
sms: countryChargeStatusSchema,
lms: countryChargeStatusSchema,
mms: countryChargeStatusSchema,
ata: countryChargeStatusSchema,
cta: countryChargeStatusSchema,
cti: countryChargeStatusSchema,
cta: Schema.optionalWith(countryChargeStatusSchema, {default: () => ({})}),
cti: Schema.optionalWith(countryChargeStatusSchema, {default: () => ({})}),
nsa: countryChargeStatusSchema,
rcs_sms: countryChargeStatusSchema,
rcs_lms: countryChargeStatusSchema,
Expand Down Expand Up @@ -56,7 +57,7 @@ export type CommonCashResponse = Schema.Schema.Type<
typeof commonCashResponseSchema
>;

export const messageTypeRecordSchema = Schema.Struct({
const messageTypeRecordFields = {
sms: Schema.Number,
lms: Schema.Number,
mms: Schema.Number,
Expand All @@ -81,6 +82,11 @@ export const messageTypeRecordSchema = Schema.Struct({
bms_commerce: Schema.optional(Schema.Number),
bms_carousel_commerce: Schema.optional(Schema.Number),
bms_free: Schema.optional(Schema.Number),
};
export const messageTypeRecordSchema = Schema.Struct({
...messageTypeRecordFields,
cta: Schema.optionalWith(Schema.Number, {default: () => 0}),
cti: Schema.optionalWith(Schema.Number, {default: () => 0}),
});
export type MessageTypeRecord = Schema.Schema.Type<
typeof messageTypeRecordSchema
Expand All @@ -92,7 +98,7 @@ export type MessageTypeRecord = Schema.Schema.Type<
* 모든 키를 optional로 풀어 서버가 한두 필드만 내려줘도 decode가 통과하도록 한다.
*/
export const partialMessageTypeRecordSchema = Schema.partial(
messageTypeRecordSchema,
Schema.Struct(messageTypeRecordFields),
);
export type PartialMessageTypeRecord = Schema.Schema.Type<
typeof partialMessageTypeRecordSchema
Expand Down
146 changes: 146 additions & 0 deletions test/assets/fastGroup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
{
"deposit": {
"requested": 0,
"replacement": 0,
"additional": 0,
"refund": 0,
"sum": 0
},
"isRefunded": false,
"workspaceId": null,
"_id": "G4V20260101000000TESTGROUP00000",
"groupId": "G4V20260101000000TESTGROUP00000",
"serviceMethod": "MT",
"accountId": "12345678901234",
"apiVersion": "v4-fast",
"sdkVersion": "nodejs/probe",
"osPlatform": "probe",
"count": {
"total": 1,
"sentTotal": 0,
"sentFailed": 0,
"sentSuccess": 0,
"sentPending": 0,
"sentReplacement": 0,
"refund": 0,
"registeredFailed": 0,
"registeredSuccess": 1
},
"countForCharge": {
"sms": {
"82": 1
},
"lms": {},
"mms": {},
"ata": {},
"nsa": {},
"rcs_sms": {},
"rcs_lms": {},
"rcs_mms": {},
"rcs_tpl": {},
"rcs_itpl": {},
"rcs_ltpl": {},
"fax": {},
"voice": {},
"bms_text": {},
"bms_image": {},
"bms_wide": {},
"bms_wide_item_list": {},
"bms_carousel_feed": {},
"bms_premium_video": {},
"bms_commerce": {},
"bms_carousel_commerce": {},
"bms_free": {}
},
"log": [
{
"createAt": "2026-01-01T00:00:00.000Z",
"message": "메시지 그룹이 생성되었습니다."
}
],
"status": "SEND-REQUESTED",
"dateSent": "2026-01-01T00:00:00.000Z",
"scheduledDate": null,
"dateCompleted": null,
"balance": {
"requested": 0,
"replacement": 0,
"additional": 0,
"refund": 0,
"sum": 0
},
"point": {
"requested": 13,
"replacement": 0,
"additional": 0,
"refund": 0,
"sum": 13
},
"clusterKey": null,
"app": {
"profit": {
"sms": 0,
"lms": 0,
"mms": 0,
"ata": 0,
"nsa": 0,
"rcs_sms": 0,
"rcs_lms": 0,
"rcs_mms": 0,
"rcs_tpl": 0,
"rcs_itpl": 0,
"rcs_ltpl": 0,
"fax": 0,
"voice": 0,
"bms_text": 0,
"bms_image": 0,
"bms_wide": 0,
"bms_wide_item_list": 0,
"bms_carousel_feed": 0,
"bms_premium_video": 0,
"bms_commerce": 0,
"bms_carousel_commerce": 0,
"bms_free": 0
},
"appId": null,
"version": null
},
"price": {
"82": {
"sms": 13,
"lms": 29,
"mms": 60,
"ata": 8,
"cta": 14,
"cti": 22,
"nsa": 6,
"rcs_sms": 11,
"rcs_lms": 28,
"rcs_mms": 58,
"rcs_tpl": 8,
"bms_free": 100,
"bms_carousel_commerce": 103,
"bms_carousel_feed": 103,
"bms_commerce": 103,
"bms_image": 83,
"bms_premium_video": 103,
"bms_text": 53,
"bms_wide": 88,
"bms_wide_item_list": 103,
"fax": 100,
"rcs_itpl": 80,
"rcs_ltpl": 32,
"voice": 200
}
},
"flagUpdated": false,
"customFields": {},
"prepaid": true,
"strict": false,
"hint": {},
"masterAccountId": null,
"allowDuplicates": false,
"originCode": null,
"dateCreated": "2026-01-01T00:00:00.000Z",
"dateUpdated": "2026-01-01T00:00:00.000Z"
}
Loading
Loading