쿠폰 자동 입력 사이트 작성
## 수정 내용 - **Sign Error 근본 원인 2가지 수정**: 1. `now()` 함수가 **초** 단위(`Date.now()/1000`)를 반환하고 있었음 → **밀리초**(`Date.now()`)로 수정 (설계서 명시 사항) 2. 세션 쿠키 추출 시 쿠키 이름에 'session'이 없으면 빈 값으로 보내고 있었음 → **모든 쿠키를 전체 전송**으로 수정 - **쿠폰 코드 대소문자 유지**: `toUpperCase()` 제거 - **FID localStorage 저장**: 한번 입력하면 다음 방문 시 자동으로 로드되어 바로 쿠폰 지급 시작 - **쿠폰 추가 시 자동 리딤**: 새 쿠폰 등록하면 DB에 저장된 **모든 유저**에게 백그라운드로 자동 지급
This commit is contained in:
parent
d8fc77789c
commit
107f1ffe84
@ -41,8 +41,12 @@ export class WosService {
|
|||||||
return crypto.createHash('md5').update(raw).digest('hex');
|
return crypto.createHash('md5').update(raw).digest('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private now(): number {
|
||||||
|
return Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
async getPlayer(fid: string) {
|
async getPlayer(fid: string) {
|
||||||
const timestamp = Date.now();
|
const timestamp = this.now();
|
||||||
const sign = this.makeSign(fid, timestamp);
|
const sign = this.makeSign(fid, timestamp);
|
||||||
|
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
@ -74,7 +78,7 @@ export class WosService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async acquireSession(fid: string): Promise<string> {
|
private async acquireSession(fid: string): Promise<string> {
|
||||||
const timestamp = Date.now();
|
const timestamp = this.now();
|
||||||
const sign = this.makeSign(fid, timestamp);
|
const sign = this.makeSign(fid, timestamp);
|
||||||
|
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
@ -84,12 +88,10 @@ export class WosService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const setCookie = response.headers['set-cookie'];
|
const setCookie = response.headers['set-cookie'];
|
||||||
let sessionValue = '';
|
if (setCookie && setCookie.length > 0) {
|
||||||
if (setCookie) {
|
return setCookie.map((c: string) => c.split(';')[0]).join('; ');
|
||||||
const found = setCookie.find((c: string) => c.toLowerCase().includes('session'));
|
|
||||||
if (found) sessionValue = found.split(';')[0];
|
|
||||||
}
|
}
|
||||||
return sessionValue;
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
private async redeemOne(
|
private async redeemOne(
|
||||||
@ -97,7 +99,7 @@ export class WosService {
|
|||||||
code: string,
|
code: string,
|
||||||
session: string,
|
session: string,
|
||||||
): Promise<{ success: boolean; message: string; err_code?: number }> {
|
): Promise<{ success: boolean; message: string; err_code?: number }> {
|
||||||
const timestamp = Date.now();
|
const timestamp = this.now();
|
||||||
const sign = this.makeSign(fid, timestamp);
|
const sign = this.makeSign(fid, timestamp);
|
||||||
|
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
@ -201,9 +203,50 @@ export class WosService {
|
|||||||
exists.name = name;
|
exists.name = name;
|
||||||
exists.is_active = true;
|
exists.is_active = true;
|
||||||
exists.is_expired = false;
|
exists.is_expired = false;
|
||||||
return this.wosCouponRepo.save(exists);
|
await this.wosCouponRepo.save(exists);
|
||||||
|
} else {
|
||||||
|
await this.wosCouponRepo.save({ name, code, is_active: true, is_expired: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.redeemCouponForAllUsers(code).catch((err) =>
|
||||||
|
this.logger.error(`신규 쿠폰 전체 지급 실패: ${err.message}`),
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.wosCouponRepo.findOne({ where: { code } });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async redeemCouponForAllUsers(code: string) {
|
||||||
|
const users = await this.wosUserRepo.find();
|
||||||
|
for (const user of users) {
|
||||||
|
const alreadyDone = await this.couponLogRepo.findOne({
|
||||||
|
where: { fid: user.fid, coupon_code: code, result_msg: In([SUCCESS_MSG, ALREADY_MSG]) },
|
||||||
|
});
|
||||||
|
if (alreadyDone) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const session = await this.acquireSession(user.fid);
|
||||||
|
const result = await this.redeemOne(user.fid, code, session);
|
||||||
|
|
||||||
|
const finalResult =
|
||||||
|
result.err_code === 40008
|
||||||
|
? { success: true, message: ALREADY_MSG }
|
||||||
|
: result;
|
||||||
|
|
||||||
|
if (result.err_code === 40007) {
|
||||||
|
const coupon = await this.wosCouponRepo.findOne({ where: { code } });
|
||||||
|
if (coupon) await this.wosCouponRepo.update(coupon.id, { is_expired: true, is_active: false });
|
||||||
|
await this.couponLogRepo.save({ fid: user.fid, coupon_code: code, result_msg: '만료된 쿠폰' });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.couponLogRepo.save({ fid: user.fid, coupon_code: code, result_msg: finalResult.message });
|
||||||
|
this.logger.log(`[${user.fid}] ${code} → ${finalResult.message}`);
|
||||||
|
} catch (err: any) {
|
||||||
|
this.logger.error(`[${user.fid}] ${code} 지급 실패: ${err.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise((r) => setTimeout(r, 1500));
|
||||||
}
|
}
|
||||||
return this.wosCouponRepo.save({ name, code, is_active: true, is_expired: false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleCoupon(id: number, is_active: boolean) {
|
async toggleCoupon(id: number, is_active: boolean) {
|
||||||
|
|||||||
@ -48,7 +48,7 @@ const activeCoupons = computed(() => store.coupons.filter((c) => c.is_active &&
|
|||||||
|
|
||||||
async function add() {
|
async function add() {
|
||||||
if (!newCode.value.trim()) return;
|
if (!newCode.value.trim()) return;
|
||||||
const code = newCode.value.trim().toUpperCase();
|
const code = newCode.value.trim();
|
||||||
await store.addCoupon(code, code);
|
await store.addCoupon(code, code);
|
||||||
newCode.value = '';
|
newCode.value = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,6 +60,7 @@ export const useWosStore = defineStore('wos', {
|
|||||||
const { data } = await axios.post('/api/player', { fid });
|
const { data } = await axios.post('/api/player', { fid });
|
||||||
this.player = data;
|
this.player = data;
|
||||||
this.fid = fid;
|
this.fid = fid;
|
||||||
|
localStorage.setItem('wos_fid', fid);
|
||||||
await Promise.all([this.loadHistory(), this.redeemAll()]);
|
await Promise.all([this.loadHistory(), this.redeemAll()]);
|
||||||
this.status = 'idle';
|
this.status = 'idle';
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
|||||||
@ -68,19 +68,28 @@ import CouponManager from '../components/CouponManager.vue';
|
|||||||
const store = useWosStore();
|
const store = useWosStore();
|
||||||
const fidInput = ref('');
|
const fidInput = ref('');
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
store.loadCoupons();
|
store.loadCoupons();
|
||||||
store.loadSavedUsers();
|
store.loadSavedUsers();
|
||||||
|
const savedFid = localStorage.getItem('wos_fid');
|
||||||
|
if (savedFid) {
|
||||||
|
fidInput.value = savedFid;
|
||||||
|
await store.searchPlayer(savedFid);
|
||||||
|
store.loadSavedUsers();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function searchPlayer() {
|
async function searchPlayer() {
|
||||||
if (!fidInput.value.trim()) return;
|
if (!fidInput.value.trim()) return;
|
||||||
await store.searchPlayer(fidInput.value.trim());
|
const fid = fidInput.value.trim();
|
||||||
|
localStorage.setItem('wos_fid', fid);
|
||||||
|
await store.searchPlayer(fid);
|
||||||
store.loadSavedUsers();
|
store.loadSavedUsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectUser(fid: string) {
|
async function selectUser(fid: string) {
|
||||||
fidInput.value = fid;
|
fidInput.value = fid;
|
||||||
|
localStorage.setItem('wos_fid', fid);
|
||||||
await store.searchPlayer(fid);
|
await store.searchPlayer(fid);
|
||||||
store.loadSavedUsers();
|
store.loadSavedUsers();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user