From 0aa7c808ec298efe8af7ee9989380162f633f0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:18:21 +0900 Subject: [PATCH 01/25] =?UTF-8?q?[K5P-45][feat]=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 15 ++++ .../domain/common/BaseEntity.java | 29 ++++++++ .../domain/contract/Contract.java | 66 +++++++++++++++++ .../domain/contract/ContractStatus.java | 28 ++++++++ .../domain/contract/PaymentType.java | 34 +++++++++ .../server_batch/domain/invoice/Invoice.java | 51 +++++++++++++ .../domain/invoice/InvoiceType.java | 32 +++++++++ .../domain/invoice/PaymentStatus.java | 28 ++++++++ .../batch/server_batch/domain/item/Item.java | 46 ++++++++++++ .../domain/member/ConsentAccount.java | 34 +++++++++ .../server_batch/domain/member/Member.java | 48 +++++++++++++ .../server_batch/domain/payment/Payment.java | 36 ++++++++++ .../domain/payment/PaymentAccount.java | 32 +++++++++ .../domain/payment/PaymentCard.java | 31 ++++++++ .../server_batch/domain/user/Client.java | 42 +++++++++++ .../batch/server_batch/domain/user/User.java | 36 ++++++++++ src/main/resources/application-dev.yml | 71 +++++++++++++++++++ src/main/resources/application.properties | 1 - src/main/resources/application.yml | 12 ++++ 19 files changed, 671 insertions(+), 1 deletion(-) create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/common/BaseEntity.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/contract/Contract.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/contract/ContractStatus.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/invoice/Invoice.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/invoice/InvoiceType.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/invoice/PaymentStatus.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/item/Item.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/member/ConsentAccount.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/member/Member.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/payment/Payment.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentAccount.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentCard.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/user/Client.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/user/User.java create mode 100644 src/main/resources/application-dev.yml delete mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/application.yml diff --git a/build.gradle b/build.gradle index 987ee60..e8dde09 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '3.2.7' id 'io.spring.dependency-management' version '1.1.5' + id 'checkstyle' } group = 'site.billingwise.batch' @@ -37,6 +38,20 @@ dependencies { testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } +// Checkstyle 설정을 추가합니다. +tasks.withType(Checkstyle) { + reports { + xml.required = true + } +} + +checkstyle { + configFile = file("config/checkstyle/naver-checkstyle-rules.xml") + configProperties = ["suppressionFile": "config/checkstyle/naver-checkstyle-suppressions.xml"] +} + + + tasks.named('test') { useJUnitPlatform() } diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/common/BaseEntity.java b/src/main/java/site/billingwise/batch/server_batch/domain/common/BaseEntity.java new file mode 100644 index 0000000..6459b73 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/common/BaseEntity.java @@ -0,0 +1,29 @@ +package site.billingwise.batch.server_batch.domain.common; + + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseEntity { + + @CreatedDate + @Column(nullable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(nullable = false) + private LocalDateTime updatedAt; + + @Column(nullable = false) + private Boolean isDeleted = Boolean.FALSE; +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/contract/Contract.java b/src/main/java/site/billingwise/batch/server_batch/domain/contract/Contract.java new file mode 100644 index 0000000..ea6c622 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/contract/Contract.java @@ -0,0 +1,66 @@ +package site.billingwise.batch.server_batch.domain.contract; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.invoice.InvoiceType; +import site.billingwise.batch.server_batch.domain.item.Item; +import site.billingwise.batch.server_batch.domain.member.Member; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Contract extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "contract_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "item_id", nullable = false) + private Item item; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "invoice_type_id", nullable = false) + private InvoiceType invoiceType; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "payment_type_id", nullable = false) + private PaymentType paymentType; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "contract_status_id", nullable = false) + private ContractStatus contractStatus; + + @Column(nullable = false) + private Boolean isSubscription; + + @Column(nullable = false) + private Long itemPrice; + + @Column(nullable = false) + private Integer itemAmount; + + @Column(nullable = false) + private Integer contractCycle; + + @Column(nullable = false) + private Integer paymentDueCycle; + + @Column(nullable = false) + private Boolean isEasyConsent; + + @OneToMany(mappedBy = "contract") + private List invoiceList = new ArrayList<>(); + +} diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/contract/ContractStatus.java b/src/main/java/site/billingwise/batch/server_batch/domain/contract/ContractStatus.java new file mode 100644 index 0000000..8fcc9fc --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/contract/ContractStatus.java @@ -0,0 +1,28 @@ +package site.billingwise.batch.server_batch.domain.contract; + + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class ContractStatus extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "contract_status_id") + private Long id; + + @Column(length = 50, nullable = false) + private String name; + + @OneToMany(mappedBy = "contractStatus") + private List contractList = new ArrayList<>(); +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java b/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java new file mode 100644 index 0000000..5a1277d --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java @@ -0,0 +1,34 @@ +package site.billingwise.batch.server_batch.domain.contract; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentType extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "payment_type_id") + private Long id; + + @Column(length = 50, nullable = false) + private String name; + + @Column(name = "basic_payment_type",nullable = false) + private Boolean isBasic; + + @OneToMany(mappedBy = "paymentType") + private List contractList = new ArrayList<>(); + + @OneToMany(mappedBy = "paymentType") + private List invoiceList = new ArrayList<>(); +} diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/Invoice.java b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/Invoice.java new file mode 100644 index 0000000..591f3b3 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/Invoice.java @@ -0,0 +1,51 @@ +package site.billingwise.batch.server_batch.domain.invoice; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.contract.PaymentType; +import site.billingwise.batch.server_batch.domain.payment.Payment; + +import java.time.LocalDateTime; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Invoice extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "invoice_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "contract_id", nullable = false) + private Contract contract; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "invoice_type_id", nullable = false) + private InvoiceType invoiceType; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "payment_type_id", nullable = false) + private PaymentType paymentType; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "payment_status_id", nullable = false) + private PaymentStatus paymentStatus; + + @Column(nullable = false) + private Long chargeAmount; + + @Column(nullable = false) + private LocalDateTime contractDate; + + @Column(nullable = false) + private LocalDateTime dueDate; + + @OneToOne(mappedBy = "invoice", fetch = FetchType.LAZY) + private Payment payment; +} diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/InvoiceType.java b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/InvoiceType.java new file mode 100644 index 0000000..5a6dcce --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/InvoiceType.java @@ -0,0 +1,32 @@ +package site.billingwise.batch.server_batch.domain.invoice; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; +import site.billingwise.batch.server_batch.domain.contract.Contract; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class InvoiceType extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "invoice_type_id") + private Long id; + + @Column(length = 50, nullable = false) + private String name; + + @OneToMany(mappedBy = "invoiceType") + private List invoiceList = new ArrayList<>(); + + @OneToMany(mappedBy = "invoiceType") + private List contractList = new ArrayList<>(); + +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/PaymentStatus.java b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/PaymentStatus.java new file mode 100644 index 0000000..c18adda --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/PaymentStatus.java @@ -0,0 +1,28 @@ +package site.billingwise.batch.server_batch.domain.invoice; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentStatus extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "payment_status_id") + private Long id; + + @Column(length = 50, nullable = false) + private String name; + + @OneToMany(mappedBy = "paymentStatus") + private List invoiceList = new ArrayList<>(); +} + diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/item/Item.java b/src/main/java/site/billingwise/batch/server_batch/domain/item/Item.java new file mode 100644 index 0000000..855ce00 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/item/Item.java @@ -0,0 +1,46 @@ +package site.billingwise.batch.server_batch.domain.item; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.user.Client; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Item extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "item_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "client_id", nullable = false) + private Client client; + + @Column(length = 50, nullable = false) + private String name; + + @Column(nullable = false) + private String description; + + @Column(nullable = false) + private Long price; + + @Column(length = 512, nullable = false) + private String imageUrl; + + @Column(nullable = false) + private Boolean isBasic; + + @OneToMany(mappedBy = "item") + private List contractList = new ArrayList<>(); + +} diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/member/ConsentAccount.java b/src/main/java/site/billingwise/batch/server_batch/domain/member/ConsentAccount.java new file mode 100644 index 0000000..8932076 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/member/ConsentAccount.java @@ -0,0 +1,34 @@ +package site.billingwise.batch.server_batch.domain.member; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class ConsentAccount extends BaseEntity { + + @Id + @Column(name = "member_id") + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @MapsId + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @Column(length = 50, nullable = false) + private String owner; + + @Column(length = 50, nullable = false) + private String bank; + + @Column(length = 20, nullable = false) + private String number; + + @Column(length = 512, nullable = false) + private String signUrl; +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/member/Member.java b/src/main/java/site/billingwise/batch/server_batch/domain/member/Member.java new file mode 100644 index 0000000..edbc22a --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/member/Member.java @@ -0,0 +1,48 @@ +package site.billingwise.batch.server_batch.domain.member; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.user.Client; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Table(uniqueConstraints = { + @UniqueConstraint(columnNames = {"client_id", "email"}) +}) +public class Member extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "member_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "client_id", nullable = false) + private Client client; + + @Column(length = 50, nullable = false) + private String name; + + @Column(nullable = false) + private String description; + + @Column(nullable = false) + private String email; + + @Column(length = 20, nullable = false) + private String phone; + + @OneToOne(mappedBy = "member", fetch = FetchType.LAZY) + private ConsentAccount consentAccount; + + @OneToMany(mappedBy = "member") + private List contractList = new ArrayList<>(); +} diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/payment/Payment.java b/src/main/java/site/billingwise/batch/server_batch/domain/payment/Payment.java new file mode 100644 index 0000000..3f2eeae --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/payment/Payment.java @@ -0,0 +1,36 @@ +package site.billingwise.batch.server_batch.domain.payment; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Payment extends BaseEntity { + + @Id + @Column(name = "invoice_id") + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @MapsId + @JoinColumn(name = "invoice_id", nullable = false) + private Invoice invoice; + + @Column(length = 50, nullable = false) + private String paymentMethod; + + @Column(nullable = false) + private Long payAmount; + + @OneToOne(mappedBy = "payment", fetch = FetchType.LAZY) + private PaymentAccount paymentAccount; + + @OneToOne(mappedBy = "payment", fetch = FetchType.LAZY) + private PaymentCard paymentCard; + +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentAccount.java b/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentAccount.java new file mode 100644 index 0000000..2a554f7 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentAccount.java @@ -0,0 +1,32 @@ +package site.billingwise.batch.server_batch.domain.payment; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentAccount extends BaseEntity { + + @Id + @Column(name = "invoice_id") + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @MapsId + @JoinColumn(name = "invoice_id", nullable = false) + private Payment payment; + + @Column(length = 50, nullable = false) + private String owner; + + @Column(length = 50, nullable = false) + private String bank; + + @Column(length = 20, nullable = false) + private String number; +} + diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentCard.java b/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentCard.java new file mode 100644 index 0000000..d223317 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentCard.java @@ -0,0 +1,31 @@ +package site.billingwise.batch.server_batch.domain.payment; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentCard extends BaseEntity { + + @Id + @Column(name = "invoice_id") + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @MapsId + @JoinColumn(name = "invoice_id", nullable = false) + private Payment payment; + + @Column(length = 50, nullable = false) + private String owner; + + @Column(length = 50, nullable = false) + private String company; + + @Column(length = 20, nullable = false) + private String number; +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/user/Client.java b/src/main/java/site/billingwise/batch/server_batch/domain/user/Client.java new file mode 100644 index 0000000..2a5419c --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/user/Client.java @@ -0,0 +1,42 @@ +package site.billingwise.batch.server_batch.domain.user; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; +import site.billingwise.batch.server_batch.domain.item.Item; +import site.billingwise.batch.server_batch.domain.member.Member; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Client extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "client_id") + private Long id; + + @Column(length = 50, nullable = false) + private String name; + + @Column(length = 60, unique = true, nullable = false) + private String authCode; + + @Column(length = 20, nullable = false) + private String phone; + + @OneToMany(mappedBy = "client") + private List userList = new ArrayList<>(); + + @OneToMany(mappedBy = "client") + private List memberList = new ArrayList<>(); + + @OneToMany(mappedBy = "client") + private List itemList = new ArrayList<>(); + +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/user/User.java b/src/main/java/site/billingwise/batch/server_batch/domain/user/User.java new file mode 100644 index 0000000..2819295 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/user/User.java @@ -0,0 +1,36 @@ +package site.billingwise.batch.server_batch.domain.user; + +import jakarta.persistence.*; +import lombok.*; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class User extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "client_id", nullable = false) + private Client client; + + @Column(unique = true, nullable = false) + private String email; + + @Column(length = 60, nullable = false) + private String password; + + @Column(length = 50, nullable = false) + private String name; + + @Column(length = 20, nullable = false) + private String phone; + + +} \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..97e28d4 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,71 @@ +# 사용할 db +spring: + profiles: + active: mysql + +--- +# h2 db 연결 +spring: + config: + activate: + on-profile: default + + datasource: + hikari: + jdbc-url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + username: sa + password: + driver-class-name: org.h2.Driver + +--- +# mysql 연결 +spring: + config: + activate: + on-profile: mysql + + datasource: + hikari: + jdbc-url: jdbc:mysql://localhost:3308/batch_data?useUnicode=true&characterEncoding=utf8 + username: root + password: 1004 + driver-class-name: com.mysql.cj.jdbc.Driver + batch: + jdbc: + initialize-schema: always + +--- +#jpa ?? +spring: + jpa: + hibernate: + ddl-auto: validate + show-sql: true + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL8Dialect + format_sql: true + + +--- +# mail sender +spring: + mail: + host: smtp.gmail.com + port: 587 + username: bhjin42@gmail.com + password: uvrt oabq gqdy isbg + properties: + mail: + smtp: + starttls: + enable: true + auth: true + +--- + +# job 자동실행 방지 +spring: + batch: + job: + enabled: false diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 27b718c..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=server-batch diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..06bd493 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,12 @@ +server: + port: 9090 + +spring: + profiles: + active: dev + + +logging: + level: + org: + hibernate: info \ No newline at end of file From 057320f6017377b5fcb2483d94fb7aec148bbab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Tue, 9 Jul 2024 14:32:06 +0900 Subject: [PATCH 02/25] =?UTF-8?q?[K5P-49]=20[feat]=20=EC=B2=AD=EA=B5=AC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B0=B0=EC=B9=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server_batch/ServerBatchApplication.java | 2 + .../common/GenerateInvoiceScheduler.java | 39 +++++++++++++ .../GenerateInvoiceJobConfig.java | 58 +++++++++++++++++++ .../GenerateInvoiceWriter.java | 58 +++++++++++++++++++ .../invoice/repository/InvoiceRepository.java | 12 ++++ .../repository/PaymentStatusRepository.java | 10 ++++ src/main/resources/application-dev.yml | 35 +---------- src/main/resources/application.yml | 1 - 8 files changed, 180 insertions(+), 35 deletions(-) create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/invoice/repository/InvoiceRepository.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/invoice/repository/PaymentStatusRepository.java diff --git a/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java b/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java index 6baaead..9d0402b 100644 --- a/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java +++ b/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication +@EnableJpaAuditing public class ServerBatchApplication { public static void main(String[] args) { diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java new file mode 100644 index 0000000..e645e17 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java @@ -0,0 +1,39 @@ +package site.billingwise.batch.server_batch.batch.common; + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.JobParametersInvalidException; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; +import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; +import org.springframework.batch.core.repository.JobRestartException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import site.billingwise.batch.server_batch.domain.invoice.repository.InvoiceRepository; + +@Configuration +@EnableScheduling +@RequiredArgsConstructor +public class GenerateInvoiceScheduler { + + private final JobLauncher jobLauncher; + private final Job generateInvoiceJob; + + //일단 30초마다 실행하도록 실행 + @Scheduled(cron = "0,30 * * * * ?") + public void generateInvoice() { + JobParameters jobParameters = new JobParametersBuilder() + .addLong("invoice", System.currentTimeMillis()) + .toJobParameters(); + try { + jobLauncher.run(generateInvoiceJob,jobParameters); + }catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException | JobParametersInvalidException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java new file mode 100644 index 0000000..826d41b --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java @@ -0,0 +1,58 @@ +package site.billingwise.batch.server_batch.batch.generateinvoice; + + +import jakarta.persistence.EntityManagerFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.database.JpaItemWriter; +import org.springframework.batch.item.database.JpaPagingItemReader; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.PlatformTransactionManager; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.invoice.repository.InvoiceRepository; +import site.billingwise.batch.server_batch.domain.invoice.repository.PaymentStatusRepository; + +@Configuration +@RequiredArgsConstructor +public class GenerateInvoiceJobConfig { + + private final int CHUNK_SIZE = 100; + private final EntityManagerFactory entityManagerFactory; + + @Bean + public Job generateInvoiceJob(JobRepository jobRepository, Step generateInvoiceStep) { + return new JobBuilder("generateInvoiceJob", jobRepository) + .start(generateInvoiceStep) + .build(); + } + + @Bean + public Step generateInvoiceStep(JobRepository jobRepository, PlatformTransactionManager transactionManager, + JpaPagingItemReader contractItemReader, ItemWriter contractItemWriter) { + return new StepBuilder("generateInvoiceStep", jobRepository) + .chunk(CHUNK_SIZE, transactionManager) + .reader(contractItemReader) + .writer(contractItemWriter) + .build(); + } + + @Bean + public JpaPagingItemReader contractItemReader() { + JpaPagingItemReader reader = new JpaPagingItemReader<>(); + reader.setEntityManagerFactory(entityManagerFactory); + reader.setQueryString("SELECT c FROM Contract c WHERE c.contractStatus.name = '진행' ORDER BY c.id ASC"); + reader.setPageSize(CHUNK_SIZE); + return reader; + } + + @Bean + public ItemWriter contractItemWriter(PaymentStatusRepository paymentStatusRepository, InvoiceRepository invoiceRepository) { + return new GenerateInvoiceWriter(paymentStatusRepository, invoiceRepository); + } +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java new file mode 100644 index 0000000..dd5d267 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java @@ -0,0 +1,58 @@ +package site.billingwise.batch.server_batch.batch.generateinvoice; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.TypedQuery; +import lombok.RequiredArgsConstructor; +import org.springframework.batch.item.Chunk; +import org.springframework.batch.item.ItemWriter; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus; +import site.billingwise.batch.server_batch.domain.invoice.repository.InvoiceRepository; +import site.billingwise.batch.server_batch.domain.invoice.repository.PaymentStatusRepository; + +import java.time.LocalDate; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +@Component +@RequiredArgsConstructor +public class GenerateInvoiceWriter implements ItemWriter { + + private final PaymentStatusRepository paymentStatusRepository; + private final InvoiceRepository invoiceRepository; + + @Override + public void write(Chunk chunk) throws Exception { + LocalDate now = LocalDate.now(); + LocalDate nextMonth = now.plusMonths(1); + int nextMonthValue = nextMonth.getMonthValue(); + int yearValue = nextMonth.getYear(); + + PaymentStatus paymentStatusUnpaid = paymentStatusRepository.findByName("미납") + .orElseThrow(() -> new IllegalArgumentException("결제 미납 상태가 존재하지 않습니다.")); + + for (Contract contract : chunk) { + boolean exists = invoiceRepository.existsByContractAndMonthAndYear(contract, nextMonthValue, yearValue); + + if (!exists) { + LocalDate setContractDate = LocalDate.of(yearValue, nextMonthValue, contract.getContractCycle()); + Invoice invoice = Invoice.builder() + .contract(contract) + .invoiceType(contract.getInvoiceType()) + .paymentType(contract.getPaymentType()) + .paymentStatus(paymentStatusUnpaid) + .chargeAmount(contract.getItemPrice() * contract.getItemAmount()) + .contractDate(setContractDate.atStartOfDay()) + .dueDate(contract.getIsSubscription() ? setContractDate.atStartOfDay() : setContractDate.plusDays(contract.getPaymentDueCycle()).atStartOfDay()) + .build(); + invoiceRepository.save(invoice); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/repository/InvoiceRepository.java b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/repository/InvoiceRepository.java new file mode 100644 index 0000000..7d7d041 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/repository/InvoiceRepository.java @@ -0,0 +1,12 @@ +package site.billingwise.batch.server_batch.domain.invoice.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; + +public interface InvoiceRepository extends JpaRepository { + @Query("SELECT CASE WHEN COUNT(i) > 0 THEN true ELSE false END FROM Invoice i WHERE i.contract = :contract AND MONTH(i.contractDate) = :month AND YEAR(i.contractDate) = :year") + boolean existsByContractAndMonthAndYear(@Param("contract") Contract contract, @Param("month") int month, @Param("year") int year); +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/repository/PaymentStatusRepository.java b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/repository/PaymentStatusRepository.java new file mode 100644 index 0000000..b0a9a3c --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/repository/PaymentStatusRepository.java @@ -0,0 +1,10 @@ +package site.billingwise.batch.server_batch.domain.invoice.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus; + +import java.util.Optional; + +public interface PaymentStatusRepository extends JpaRepository { + Optional findByName(String name); +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 97e28d4..afe6d75 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,29 +1,4 @@ -# 사용할 db spring: - profiles: - active: mysql - ---- -# h2 db 연결 -spring: - config: - activate: - on-profile: default - - datasource: - hikari: - jdbc-url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE - username: sa - password: - driver-class-name: org.h2.Driver - ---- -# mysql 연결 -spring: - config: - activate: - on-profile: mysql - datasource: hikari: jdbc-url: jdbc:mysql://localhost:3308/batch_data?useUnicode=true&characterEncoding=utf8 @@ -33,9 +8,7 @@ spring: batch: jdbc: initialize-schema: always - --- -#jpa ?? spring: jpa: hibernate: @@ -45,10 +18,7 @@ spring: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect format_sql: true - - --- -# mail sender spring: mail: host: smtp.gmail.com @@ -61,11 +31,8 @@ spring: starttls: enable: true auth: true - --- - -# job 자동실행 방지 spring: batch: job: - enabled: false + enabled: false \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 06bd493..f368052 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,7 +5,6 @@ spring: profiles: active: dev - logging: level: org: From b3c6a5a20e0cd8079169ab669ab380f0f2d3616a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Tue, 9 Jul 2024 14:45:57 +0900 Subject: [PATCH 03/25] =?UTF-8?q?[K5P-49]=20[feat]=20=EC=B2=AD=EA=B5=AC=20?= =?UTF-8?q?=20JOB=EB=8F=99B=20=EC=8B=9C=EA=B0=84=20=EC=B8=A1=EC=A0=95=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=EB=84=88=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GenerateInvoiceJobConfig.java | 4 ++ .../listner/JobCompletionCheckListener.java | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/listner/JobCompletionCheckListener.java diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java index 826d41b..25acb5f 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java @@ -6,6 +6,7 @@ import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.listener.JobExecutionListenerSupport; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.item.ItemWriter; @@ -14,6 +15,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; +import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener; import site.billingwise.batch.server_batch.domain.contract.Contract; import site.billingwise.batch.server_batch.domain.invoice.repository.InvoiceRepository; import site.billingwise.batch.server_batch.domain.invoice.repository.PaymentStatusRepository; @@ -24,10 +26,12 @@ public class GenerateInvoiceJobConfig { private final int CHUNK_SIZE = 100; private final EntityManagerFactory entityManagerFactory; + private final JobCompletionCheckListener jobCompletionCheckListener; @Bean public Job generateInvoiceJob(JobRepository jobRepository, Step generateInvoiceStep) { return new JobBuilder("generateInvoiceJob", jobRepository) + .listener(jobCompletionCheckListener) .start(generateInvoiceStep) .build(); } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/listner/JobCompletionCheckListener.java b/src/main/java/site/billingwise/batch/server_batch/batch/listner/JobCompletionCheckListener.java new file mode 100644 index 0000000..58d3691 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/listner/JobCompletionCheckListener.java @@ -0,0 +1,38 @@ +package site.billingwise.batch.server_batch.batch.listner; + +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.listener.JobExecutionListenerSupport; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.logging.Logger; + + +@Component +public class JobCompletionCheckListener extends JobExecutionListenerSupport { + + private static final Logger log = Logger.getLogger(JobCompletionCheckListener.class.getName()); + private LocalDateTime startTime; + private LocalDateTime endTime; + + @Override + public void beforeJob(JobExecution jobExecution) { + startTime = LocalDateTime.now(); + log.info("Job started at: " + startTime); + } + + @Override + public void afterJob(JobExecution jobExecution) { + endTime = LocalDateTime.now(); + log.info("Job ended at: " + endTime); + + Duration duration = Duration.between(startTime, endTime); + long seconds = duration.getSeconds(); + long minutes = seconds / 60; + seconds = seconds % 60; + + log.info("Job duration: " + minutes + " minutes and " + seconds + " seconds"); + super.afterJob(jobExecution); + } +} \ No newline at end of file From b9b100840d1e380772d3445e5faadc134a776176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:31:57 +0900 Subject: [PATCH 04/25] =?UTF-8?q?[K5P-49]=20[fix]=20=EA=B2=B0=EC=A0=9C?= =?UTF-8?q?=EA=B8=B0=ED=95=9C=20=EB=A1=9C=EC=A7=81=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../generateinvoice/GenerateInvoiceWriter.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java index dd5d267..8e95997 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java @@ -16,6 +16,7 @@ import site.billingwise.batch.server_batch.domain.invoice.repository.PaymentStatusRepository; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -42,6 +43,8 @@ public void write(Chunk chunk) throws Exception { if (!exists) { LocalDate setContractDate = LocalDate.of(yearValue, nextMonthValue, contract.getContractCycle()); + LocalDateTime dueDate = calculateDueDate(contract, setContractDate); + Invoice invoice = Invoice.builder() .contract(contract) .invoiceType(contract.getInvoiceType()) @@ -49,10 +52,18 @@ public void write(Chunk chunk) throws Exception { .paymentStatus(paymentStatusUnpaid) .chargeAmount(contract.getItemPrice() * contract.getItemAmount()) .contractDate(setContractDate.atStartOfDay()) - .dueDate(contract.getIsSubscription() ? setContractDate.atStartOfDay() : setContractDate.plusDays(contract.getPaymentDueCycle()).atStartOfDay()) + .dueDate(dueDate) .build(); invoiceRepository.save(invoice); } } } + + private LocalDateTime calculateDueDate(Contract contract, LocalDate setContractDate) { + if (contract.getPaymentType().getName().equals("납부자 결제")) { + return setContractDate.plusDays(contract.getPaymentDueCycle()).atStartOfDay(); + } else { + return setContractDate.atStartOfDay(); + } + } } \ No newline at end of file From 765bf2a0e60e4a8dc79359206627812466b0e778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:41:10 +0900 Subject: [PATCH 05/25] =?UTF-8?q?[K5P-49]=20[feat]=20=EC=B2=AD=EA=B5=AC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=B0=B0=EC=B9=98=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(JDBC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/GenerateInvoiceScheduler.java | 49 ++++++- .../GenerateInvoiceJobConfig.java | 2 - .../GenerateInvoiceWriter.java | 14 +- .../jdbc/JdbcGenerateInvoiceJobConfig.java | 73 +++++++++++ .../jdbc/JdbcGenerateInvoiceWriter.java | 121 ++++++++++++++++++ .../rowmapper/JdbcContractRowMapper.java | 35 +++++ src/main/resources/application-dev.yml | 2 +- .../jdbc/JdbcGenerateInvoiceWriterTest.java | 5 + 8 files changed, 281 insertions(+), 20 deletions(-) create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java create mode 100644 src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java index e645e17..329a072 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java @@ -1,6 +1,8 @@ package site.billingwise.batch.server_batch.batch.common; import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; @@ -9,31 +11,66 @@ import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; import org.springframework.batch.core.repository.JobRestartException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; -import site.billingwise.batch.server_batch.domain.invoice.repository.InvoiceRepository; + @Configuration @EnableScheduling @RequiredArgsConstructor public class GenerateInvoiceScheduler { + private static final Logger logger = LoggerFactory.getLogger(GenerateInvoiceScheduler.class); private final JobLauncher jobLauncher; private final Job generateInvoiceJob; + private final Job jdbcGenerateInvoiceJob; - //일단 30초마다 실행하도록 실행 + // 일단 0,30초마다 실행하도록 실행 @Scheduled(cron = "0,30 * * * * ?") public void generateInvoice() { JobParameters jobParameters = new JobParametersBuilder() .addLong("invoice", System.currentTimeMillis()) .toJobParameters(); try { - jobLauncher.run(generateInvoiceJob,jobParameters); - }catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException | JobParametersInvalidException e) { - throw new RuntimeException(e); + logger.info("JPA 배치 프로그램 실행 시작"); + jobLauncher.run(generateInvoiceJob, jobParameters); + logger.info("JPA 배치 프로그램 실행 완료"); + } catch (JobExecutionAlreadyRunningException e) { + logger.error("JobExecutionAlreadyRunningException 발생: ", e); + } catch (JobRestartException e) { + logger.error("JobRestartException 발생: ", e); + } catch (JobInstanceAlreadyCompleteException e) { + logger.error("JobInstanceAlreadyCompleteException 발생: ", e); + } catch (JobParametersInvalidException e) { + logger.error("JobParametersInvalidException 발생: ", e); + } catch (Exception e) { + logger.error("예기치 않은 오류 발생: ", e); } } + + // 일단 15,45초마다 실행하도록 실행 + @Scheduled(cron = "15,45 * * * * ?") + public void jdbcgenerateInvoice() { + JobParameters jobParameters = new JobParametersBuilder() + .addLong("jdbcInvoice", System.currentTimeMillis()) + .toJobParameters(); + try { + logger.info("JDBC 배치 프로그램 실행 시작"); + jobLauncher.run(jdbcGenerateInvoiceJob, jobParameters); + logger.info("JDBC 배치 프로그램 실행 완료"); + } catch (JobExecutionAlreadyRunningException e) { + logger.error("JobExecutionAlreadyRunningException 발생: ", e); + } catch (JobRestartException e) { + logger.error("JobRestartException 발생: ", e); + } catch (JobInstanceAlreadyCompleteException e) { + logger.error("JobInstanceAlreadyCompleteException 발생: ", e); + } catch (JobParametersInvalidException e) { + logger.error("JobParametersInvalidException 발생: ", e); + } catch (Exception e) { + logger.error("예기치 않은 오류 발생: ", e); + } + } } + diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java index 25acb5f..9427f99 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceJobConfig.java @@ -6,11 +6,9 @@ import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.job.builder.JobBuilder; -import org.springframework.batch.core.listener.JobExecutionListenerSupport; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.item.ItemWriter; -import org.springframework.batch.item.database.JpaItemWriter; import org.springframework.batch.item.database.JpaPagingItemReader; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java index 8e95997..f2ff6f0 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/GenerateInvoiceWriter.java @@ -1,14 +1,9 @@ package site.billingwise.batch.server_batch.batch.generateinvoice; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.TypedQuery; import lombok.RequiredArgsConstructor; import org.springframework.batch.item.Chunk; import org.springframework.batch.item.ItemWriter; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import site.billingwise.batch.server_batch.domain.contract.Contract; import site.billingwise.batch.server_batch.domain.invoice.Invoice; import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus; @@ -17,10 +12,8 @@ import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; + + @Component @RequiredArgsConstructor public class GenerateInvoiceWriter implements ItemWriter { @@ -62,8 +55,7 @@ public void write(Chunk chunk) throws Exception { private LocalDateTime calculateDueDate(Contract contract, LocalDate setContractDate) { if (contract.getPaymentType().getName().equals("납부자 결제")) { return setContractDate.plusDays(contract.getPaymentDueCycle()).atStartOfDay(); - } else { - return setContractDate.atStartOfDay(); } + return setContractDate.atStartOfDay(); } } \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java new file mode 100644 index 0000000..f967c49 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java @@ -0,0 +1,73 @@ +package site.billingwise.batch.server_batch.batch.generateinvoice.jdbc; + + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.PlatformTransactionManager; +import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener; +import site.billingwise.batch.server_batch.batch.generateinvoice.rowmapper.JdbcContractRowMapper; +import site.billingwise.batch.server_batch.domain.contract.Contract; + +import javax.sql.DataSource; + +@Configuration +@RequiredArgsConstructor +public class JdbcGenerateInvoiceJobConfig { + + private final int CHUNK_SIZE = 100; + private final DataSource dataSource; + private final JdbcTemplate jdbcTemplate; + private final JobCompletionCheckListener jobCompletionCheckListener; + + @Bean + public Job jdbcGenerateInvoiceJob(JobRepository jobRepository, Step jdbcGenerateInvoiceStep) { + return new JobBuilder("jdbcGenerateInvoiceJob", jobRepository) + .listener(jobCompletionCheckListener) + .start(jdbcGenerateInvoiceStep) + .build(); + } + + + @Bean + public Step jdbcGenerateInvoiceStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) { + return new StepBuilder("jdbcGenerateInvoiceStep", jobRepository) + .chunk(CHUNK_SIZE, transactionManager) + .reader(jdbcContractItemReader()) + .writer(jdbcContractItemWriter()) + .build(); + } + + @Bean + public ItemReader jdbcContractItemReader() { + return new JdbcCursorItemReaderBuilder() + .name("jdbcContractItemReader") + .fetchSize(CHUNK_SIZE) + .sql("select c.*, pt.payment_type_id, pt.name as payment_type_name, it.invoice_type_id, it.name as invoice_type_name " + + "from contract c " + + "join contract_status cs ON c.contract_status_id = cs.contract_status_id " + + "join payment_type pt ON c.payment_type_id = pt.payment_type_id " + + "join invoice_type it ON c.invoice_type_id = it.invoice_type_id " + + "where cs.name = '진행'") + .rowMapper(new JdbcContractRowMapper()) + .dataSource(dataSource) + .build(); + } + + + + + @Bean + public ItemWriter jdbcContractItemWriter() { + return new JdbcGenerateInvoiceWriter(jdbcTemplate); + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java new file mode 100644 index 0000000..175622f --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java @@ -0,0 +1,121 @@ +package site.billingwise.batch.server_batch.batch.generateinvoice.jdbc; + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.item.Chunk; +import org.springframework.batch.item.ItemWriter; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + + +@Component +@RequiredArgsConstructor +public class JdbcGenerateInvoiceWriter implements ItemWriter { + + private final JdbcTemplate jdbcTemplate; + private static final String PAYER_PAYMENT = "납부자 결제"; + + @Override + public void write(Chunk chunk) throws Exception { + + LocalDateTime now = LocalDateTime.now(); + LocalDateTime nextMonth = now.plusMonths(1); + int nextMonthValue = nextMonth.getMonthValue(); + int yearValue = nextMonth.getYear(); + + PaymentStatus unpaidPaymentStatus = findUnpaidPaymentStatus(); + + List invoices = new ArrayList<>(); + + for(Contract contract : chunk) { + // 수동 청구가 이미 만들어지면, 청구 생성 X + if(!invoiceExists(contract, nextMonthValue, yearValue)){ + // 약정일 + LocalDateTime setInvoiceDate = LocalDateTime.of(yearValue, nextMonthValue, contract.getContractCycle(), 0, 0); + // 결제기한 + LocalDateTime payDueDate = calculateDueDate(contract, setInvoiceDate); + + Invoice invoice = Invoice.builder() + .contract(contract) + .invoiceType(contract.getInvoiceType()) + .paymentType(contract.getPaymentType()) + .paymentStatus(unpaidPaymentStatus) + .chargeAmount(contract.getItemPrice() * contract.getItemAmount()) + .contractDate(setInvoiceDate) + .dueDate(payDueDate) + .build(); + + invoices.add(invoice); + + + } + } + + if (!invoices.isEmpty()) { + bulkInsertInvoices(invoices); + } + } + + private void bulkInsertInvoices(List invoices) { + String sql = "insert into invoice (contract_id, invoice_type_id, payment_type_id, payment_status_id, charge_amount, contract_date, due_date, is_deleted, created_at, updated_at)" + + " values (?, ?, ?, ?, ?, ?, ?, false, NOW(), NOW())"; + + jdbcTemplate.batchUpdate(sql,new BatchPreparedStatementSetter() { + + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + Invoice invoice = invoices.get(i); + ps.setLong(1, invoice.getContract().getId()); + ps.setLong(2, invoice.getInvoiceType().getId()); + ps.setLong(3, invoice.getPaymentType().getId()); + ps.setLong(4, invoice.getPaymentStatus().getId()); + ps.setLong(5, invoice.getChargeAmount()); + ps.setTimestamp(6, java.sql.Timestamp.valueOf(invoice.getContractDate())); + ps.setTimestamp(7, java.sql.Timestamp.valueOf(invoice.getDueDate())); + } + + @Override + public int getBatchSize() { + return invoices.size(); + } + }); + } + + private PaymentStatus findUnpaidPaymentStatus() { + String sql = "select payment_status_id, name from payment_status where name = '미납'"; + return jdbcTemplate.queryForObject(sql, (ResultSet rs, int rowNum) -> + PaymentStatus.builder() + .id(rs.getLong("payment_status_id")) + .name(rs.getString("name")) + .build() + ); + } + + private LocalDateTime calculateDueDate(Contract contract, LocalDateTime setInvoiceDate) { + if (PAYER_PAYMENT.equals(contract.getPaymentType().getName())) { + return setInvoiceDate.plusDays(3); + } + return setInvoiceDate; + } + + private boolean invoiceExists(Contract contract, int month, int year) { + LocalDateTime startDate = LocalDateTime.of(year, month, 1, 0, 0); + LocalDateTime endDate = startDate.plusMonths(1).minusSeconds(1); + + String sql = "select count(*) from invoice where contract_id = ? " + + "and contract_date >= ? and contract_date <= ?"; + + Integer count = jdbcTemplate.queryForObject(sql, new Object[]{contract.getId(), startDate, endDate}, Integer.class); + return count != null && count > 0; + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java new file mode 100644 index 0000000..1b13bb8 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java @@ -0,0 +1,35 @@ +package site.billingwise.batch.server_batch.batch.generateinvoice.rowmapper; + +import org.springframework.jdbc.core.RowMapper; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.contract.PaymentType; +import site.billingwise.batch.server_batch.domain.invoice.InvoiceType; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class JdbcContractRowMapper implements RowMapper { + @Override + public Contract mapRow(ResultSet rs, int rowNum) throws SQLException { + return Contract.builder() + .id(rs.getLong("contract_id")) + .itemPrice(rs.getLong("item_price")) + .itemAmount(rs.getInt("item_amount")) + .contractCycle(rs.getInt("contract_cycle")) + .paymentDueCycle(rs.getInt("payment_due_cycle")) + .paymentType( + PaymentType.builder() + .id(rs.getLong("payment_type_id")) + .name(rs.getString("payment_type_name")) + .build() + ) + .invoiceType( + InvoiceType.builder() + .id(rs.getLong("invoice_type_id")) + .name(rs.getString("invoice_type_name")) + .build() + ) + + .build(); + } +} \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index afe6d75..380d310 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -13,7 +13,7 @@ spring: jpa: hibernate: ddl-auto: validate - show-sql: true + show-sql: false properties: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect diff --git a/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java b/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java new file mode 100644 index 0000000..82d424a --- /dev/null +++ b/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java @@ -0,0 +1,5 @@ +package site.billingwise.batch.server_batch.batch.generateinvoice.jdbc; + + +class JdbcGenerateInvoiceWriterTest { +} From f17ca8845d1e7c76dd80781bacf2f83d236d975e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Wed, 10 Jul 2024 17:05:29 +0900 Subject: [PATCH 06/25] =?UTF-8?q?[K5P-49]=20[env]=20DB=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=BD=94=EB=93=9C=20=EB=B0=8F=20=EB=8D=94=EB=AF=B8=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/dummy-contract.sql | 1003 +++++++++++++++++++++++++ src/main/resources/dummy.sql | 52 ++ src/main/resources/status.sql | 30 + 3 files changed, 1085 insertions(+) create mode 100644 src/main/resources/dummy-contract.sql create mode 100644 src/main/resources/dummy.sql create mode 100644 src/main/resources/status.sql diff --git a/src/main/resources/dummy-contract.sql b/src/main/resources/dummy-contract.sql new file mode 100644 index 0000000..2d6de4e --- /dev/null +++ b/src/main/resources/dummy-contract.sql @@ -0,0 +1,1003 @@ +-- 계약 더미 데이터 (1001건 -> 1건은 계약 종료된 것) +INSERT INTO contract (contract_id, member_id, item_id, invoice_type_id, payment_type_id, contract_status_id, is_subscription, item_price, item_amount, contract_cycle, payment_due_cycle, is_easy_consent, is_deleted, created_at, updated_at) VALUES + (1, 5, 4, 1, 2, 2, True, 44873, 3, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (2, 5, 4, 2, 2, 2, True, 22344, 5, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (3, 4, 3, 1, 1, 2, False, 11770, 4, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (4, 4, 1, 1, 2, 2, False, 19177, 3, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (5, 2, 5, 2, 2, 2, True, 26045, 1, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (6, 3, 1, 1, 1, 2, False, 35871, 2, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (7, 2, 5, 2, 2, 2, False, 37773, 5, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (8, 4, 2, 2, 2, 2, False, 27437, 1, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (9, 4, 4, 2, 1, 2, True, 27782, 2, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (10, 5, 4, 1, 2, 2, True, 46910, 2, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (11, 1, 3, 1, 1, 2, False, 24570, 4, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (12, 4, 3, 1, 1, 2, True, 14566, 3, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (13, 3, 5, 2, 1, 2, True, 21491, 3, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (14, 5, 2, 1, 2, 2, False, 38706, 5, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (15, 3, 1, 2, 2, 2, False, 35531, 4, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (16, 3, 5, 2, 2, 2, True, 23144, 2, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (17, 3, 5, 2, 1, 2, False, 48889, 5, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (18, 1, 5, 2, 1, 2, True, 41203, 2, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (19, 4, 4, 1, 1, 2, True, 30688, 4, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (20, 1, 1, 2, 2, 2, False, 40763, 1, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (21, 5, 5, 2, 2, 2, True, 18826, 2, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (22, 3, 1, 1, 1, 2, True, 14128, 3, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (23, 2, 1, 1, 1, 2, True, 46224, 4, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (24, 5, 2, 2, 1, 2, False, 47497, 3, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (25, 3, 3, 1, 1, 2, False, 28866, 2, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (26, 1, 2, 2, 1, 2, False, 42204, 1, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (27, 3, 5, 2, 2, 2, True, 47511, 2, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (28, 5, 4, 2, 1, 2, False, 43504, 4, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (29, 3, 2, 1, 1, 2, True, 33497, 4, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (30, 5, 1, 1, 1, 2, True, 47367, 1, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (31, 1, 1, 1, 1, 2, True, 46886, 3, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (32, 2, 5, 1, 1, 2, False, 40822, 2, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (33, 4, 3, 2, 2, 2, True, 43198, 2, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (34, 1, 5, 2, 2, 2, True, 25883, 2, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (35, 2, 4, 1, 1, 2, True, 47700, 2, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (36, 5, 1, 1, 1, 2, False, 11492, 4, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (37, 5, 5, 2, 2, 2, False, 20108, 3, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (38, 5, 2, 1, 1, 2, True, 49981, 1, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (39, 2, 1, 1, 2, 2, False, 25187, 2, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (40, 3, 4, 2, 2, 2, True, 28386, 3, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (41, 1, 4, 1, 1, 2, False, 43792, 2, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (42, 2, 2, 1, 2, 2, True, 16880, 3, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (43, 2, 1, 2, 2, 2, True, 26613, 4, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (44, 5, 1, 1, 2, 2, True, 40445, 5, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (45, 2, 5, 1, 2, 2, True, 11114, 2, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (46, 1, 4, 2, 2, 2, True, 23357, 4, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (47, 1, 4, 2, 1, 2, False, 10459, 4, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (48, 2, 1, 2, 2, 2, False, 42776, 3, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (49, 3, 4, 1, 2, 2, True, 33051, 1, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (50, 4, 1, 1, 2, 2, True, 45206, 2, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (51, 4, 5, 1, 1, 2, False, 36893, 4, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (52, 1, 1, 1, 1, 2, False, 34235, 2, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (53, 3, 2, 2, 2, 2, True, 18222, 2, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (54, 2, 2, 2, 2, 2, False, 44502, 1, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (55, 1, 3, 1, 2, 2, False, 38450, 2, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (56, 2, 4, 2, 1, 2, False, 32015, 3, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (57, 3, 4, 2, 2, 2, False, 44444, 2, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (58, 1, 3, 1, 1, 2, True, 44815, 1, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (59, 4, 1, 1, 2, 2, False, 44961, 5, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (60, 5, 2, 1, 1, 2, True, 47953, 4, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (61, 4, 5, 1, 2, 2, False, 39066, 4, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (62, 2, 1, 2, 2, 2, True, 16142, 5, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (63, 5, 1, 2, 1, 2, False, 40551, 4, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (64, 4, 4, 2, 1, 2, True, 18412, 5, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (65, 2, 1, 2, 2, 2, True, 49438, 5, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (66, 3, 1, 2, 1, 2, False, 18661, 1, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (67, 5, 3, 1, 2, 2, False, 49426, 1, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (68, 5, 2, 1, 1, 2, False, 29276, 4, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (69, 2, 4, 1, 1, 2, True, 49553, 3, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (70, 3, 2, 1, 2, 2, True, 24199, 1, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (71, 3, 2, 1, 1, 2, True, 45863, 1, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (72, 4, 5, 1, 2, 2, True, 47297, 3, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (73, 5, 4, 2, 2, 2, False, 43271, 1, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (74, 2, 1, 1, 2, 2, False, 19893, 4, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (75, 2, 1, 1, 1, 2, True, 21094, 1, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (76, 1, 5, 1, 1, 2, True, 12540, 4, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (77, 4, 4, 1, 1, 2, True, 20119, 5, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (78, 5, 3, 1, 1, 2, True, 12807, 2, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (79, 5, 1, 2, 2, 2, True, 30266, 1, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (80, 4, 3, 1, 2, 2, False, 39833, 2, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (81, 1, 1, 2, 2, 2, True, 36729, 1, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (82, 2, 2, 1, 1, 2, False, 47356, 4, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (83, 1, 3, 2, 1, 2, True, 24770, 5, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (84, 5, 3, 1, 1, 2, False, 19497, 3, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (85, 4, 3, 2, 2, 2, False, 39050, 3, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (86, 4, 1, 1, 2, 2, True, 16170, 4, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (87, 5, 2, 2, 2, 2, False, 24594, 1, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (88, 4, 1, 2, 1, 2, True, 34105, 2, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (89, 3, 3, 1, 1, 2, True, 47179, 1, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (90, 2, 2, 2, 1, 2, True, 17530, 5, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (91, 1, 1, 1, 2, 2, True, 35551, 2, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (92, 4, 1, 1, 2, 2, False, 14814, 5, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (93, 4, 1, 2, 2, 2, False, 38700, 3, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (94, 1, 3, 2, 2, 2, True, 45990, 3, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (95, 4, 1, 2, 1, 2, True, 23781, 1, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (96, 1, 1, 2, 2, 2, True, 16491, 5, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (97, 3, 1, 2, 1, 2, True, 37143, 2, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (98, 3, 3, 1, 2, 2, False, 35498, 3, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (99, 1, 3, 2, 2, 2, False, 36540, 5, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (100, 3, 4, 1, 1, 2, False, 43011, 1, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (101, 1, 3, 1, 1, 2, False, 43293, 3, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (102, 2, 5, 2, 1, 2, True, 21318, 3, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (103, 1, 1, 1, 1, 2, True, 44390, 1, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (104, 2, 2, 1, 2, 2, True, 45301, 4, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (105, 1, 5, 2, 2, 2, False, 14981, 2, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (106, 5, 4, 2, 1, 2, True, 36374, 1, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (107, 4, 4, 2, 1, 2, True, 47883, 5, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (108, 2, 4, 2, 2, 2, False, 21400, 4, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (109, 3, 5, 1, 1, 2, True, 48517, 3, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (110, 1, 2, 2, 1, 2, True, 26496, 5, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (111, 4, 5, 1, 2, 2, True, 29683, 1, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (112, 2, 4, 2, 2, 2, False, 17516, 3, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (113, 4, 5, 2, 2, 2, False, 25018, 2, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (114, 5, 2, 1, 2, 2, True, 48789, 1, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (115, 2, 5, 2, 1, 2, False, 33249, 4, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (116, 5, 2, 1, 2, 2, True, 43393, 5, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (117, 1, 4, 2, 2, 2, True, 30035, 2, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (118, 2, 5, 2, 2, 2, False, 36380, 4, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (119, 3, 2, 2, 2, 2, True, 15182, 2, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (120, 5, 5, 2, 1, 2, True, 29596, 3, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (121, 4, 2, 2, 1, 2, True, 48394, 2, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (122, 2, 1, 2, 1, 2, False, 30843, 1, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (123, 3, 4, 2, 2, 2, True, 19024, 4, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (124, 5, 2, 1, 1, 2, False, 40329, 3, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (125, 2, 1, 1, 1, 2, True, 44116, 1, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (126, 2, 5, 1, 2, 2, True, 30771, 4, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (127, 4, 3, 2, 2, 2, True, 19184, 4, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (128, 2, 4, 2, 1, 2, False, 17811, 2, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (129, 4, 4, 1, 1, 2, True, 25906, 3, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (130, 4, 4, 1, 2, 2, True, 45093, 3, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (131, 2, 3, 2, 1, 2, True, 10124, 3, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (132, 4, 2, 1, 2, 2, True, 32249, 1, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (133, 2, 1, 2, 2, 2, True, 48801, 3, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (134, 5, 2, 2, 2, 2, False, 11163, 3, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (135, 1, 3, 2, 2, 2, True, 18184, 2, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (136, 4, 5, 1, 2, 2, False, 18071, 3, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (137, 1, 2, 1, 2, 2, True, 16071, 2, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (138, 2, 2, 2, 2, 2, False, 38704, 2, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (139, 1, 4, 1, 1, 2, True, 30075, 2, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (140, 1, 5, 1, 2, 2, False, 24160, 5, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (141, 1, 4, 2, 1, 2, False, 35680, 2, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (142, 1, 3, 2, 2, 2, True, 18872, 5, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (143, 5, 1, 2, 2, 2, True, 11154, 1, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (144, 1, 5, 2, 2, 2, True, 17962, 4, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (145, 4, 4, 2, 1, 2, False, 25286, 5, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (146, 1, 4, 2, 2, 2, False, 23215, 3, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (147, 3, 2, 2, 2, 2, True, 34529, 4, 22, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (148, 4, 2, 1, 1, 2, False, 19340, 4, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (149, 2, 2, 2, 2, 2, True, 19688, 2, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (150, 1, 4, 1, 2, 2, False, 25620, 4, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (151, 1, 4, 2, 2, 2, True, 33384, 1, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (152, 4, 1, 1, 2, 2, False, 34310, 3, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (153, 3, 4, 1, 1, 2, False, 46392, 4, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (154, 3, 4, 2, 2, 2, False, 30968, 5, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (155, 1, 2, 1, 2, 2, False, 49903, 4, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (156, 1, 2, 1, 1, 2, True, 49606, 3, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (157, 4, 3, 2, 1, 2, False, 38545, 3, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (158, 1, 2, 1, 1, 2, True, 34966, 1, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (159, 2, 3, 1, 2, 2, True, 14403, 2, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (160, 3, 2, 1, 2, 2, True, 47386, 5, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (161, 4, 5, 2, 1, 2, True, 32086, 2, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (162, 1, 2, 2, 2, 2, False, 29301, 4, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (163, 2, 3, 2, 2, 2, True, 27945, 1, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (164, 4, 5, 2, 2, 2, False, 46692, 2, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (165, 2, 5, 2, 1, 2, False, 17765, 2, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (166, 3, 5, 2, 2, 2, True, 10492, 3, 22, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (167, 5, 5, 2, 1, 2, True, 38540, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (168, 1, 4, 2, 1, 2, False, 35535, 1, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (169, 1, 2, 1, 1, 2, True, 12160, 4, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (170, 1, 3, 1, 1, 2, True, 33977, 5, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (171, 3, 2, 2, 1, 2, False, 38251, 5, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (172, 3, 1, 1, 1, 2, False, 32740, 1, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (173, 4, 5, 2, 2, 2, True, 34461, 5, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (174, 1, 1, 2, 1, 2, False, 31825, 2, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (175, 2, 5, 2, 2, 2, True, 45608, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (176, 5, 1, 1, 2, 2, True, 43931, 2, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (177, 1, 1, 2, 1, 2, False, 46468, 2, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (178, 4, 3, 1, 1, 2, False, 17881, 2, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (179, 1, 5, 1, 2, 2, True, 23586, 4, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (180, 3, 4, 1, 1, 2, True, 23950, 5, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (181, 3, 1, 2, 2, 2, True, 23829, 2, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (182, 2, 4, 1, 2, 2, True, 15979, 5, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (183, 1, 5, 2, 1, 2, False, 16025, 5, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (184, 3, 5, 1, 2, 2, False, 37144, 5, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (185, 4, 4, 1, 1, 2, True, 27650, 2, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (186, 1, 2, 2, 2, 2, False, 35610, 2, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (187, 4, 1, 1, 1, 2, False, 46152, 1, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (188, 5, 5, 1, 1, 2, True, 42546, 1, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (189, 3, 5, 2, 1, 2, True, 24400, 3, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (190, 3, 5, 2, 1, 2, True, 17906, 5, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (191, 4, 2, 1, 2, 2, False, 22050, 4, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (192, 2, 1, 1, 2, 2, False, 27779, 1, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (193, 1, 4, 1, 1, 2, True, 12893, 5, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (194, 1, 2, 2, 1, 2, False, 41953, 3, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (195, 2, 4, 1, 1, 2, True, 14406, 3, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (196, 4, 4, 1, 2, 2, True, 34951, 5, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (197, 2, 2, 1, 2, 2, False, 28527, 5, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (198, 3, 1, 1, 2, 2, False, 21773, 5, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (199, 2, 1, 2, 1, 2, True, 44576, 1, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (200, 4, 4, 2, 2, 2, True, 38154, 3, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (201, 3, 5, 1, 1, 2, True, 45955, 1, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (202, 3, 5, 2, 1, 2, True, 29990, 4, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (203, 2, 3, 1, 1, 2, False, 40419, 1, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (204, 2, 5, 1, 1, 2, False, 12658, 2, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (205, 3, 5, 1, 1, 2, False, 20374, 4, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (206, 2, 3, 1, 2, 2, True, 27860, 1, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (207, 5, 1, 2, 2, 2, False, 43445, 5, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (208, 1, 4, 2, 1, 2, False, 15331, 3, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (209, 1, 1, 1, 2, 2, True, 17826, 3, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (210, 5, 4, 2, 1, 2, True, 46452, 1, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (211, 5, 2, 2, 1, 2, True, 25718, 3, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (212, 2, 5, 1, 2, 2, True, 40270, 3, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (213, 5, 2, 2, 2, 2, True, 39526, 2, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (214, 5, 5, 2, 1, 2, False, 47878, 5, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (215, 2, 2, 1, 1, 2, True, 36757, 2, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (216, 4, 1, 2, 2, 2, True, 16577, 3, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (217, 5, 2, 1, 1, 2, False, 16047, 2, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (218, 5, 5, 1, 1, 2, False, 20437, 4, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (219, 3, 4, 1, 2, 2, False, 35367, 5, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (220, 2, 1, 2, 1, 2, False, 29304, 1, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (221, 3, 4, 2, 1, 2, True, 46507, 1, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (222, 3, 1, 1, 2, 2, True, 21709, 4, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (223, 4, 2, 2, 1, 2, False, 39241, 2, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (224, 1, 4, 2, 2, 2, True, 28263, 3, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (225, 3, 5, 1, 2, 2, True, 30173, 2, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (226, 3, 1, 2, 2, 2, False, 41178, 4, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (227, 1, 4, 2, 2, 2, True, 20237, 2, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (228, 3, 2, 2, 1, 2, False, 14271, 1, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (229, 1, 4, 2, 1, 2, True, 18255, 1, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (230, 2, 5, 1, 1, 2, False, 34182, 3, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (231, 2, 1, 2, 2, 2, True, 46825, 4, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (232, 3, 3, 2, 2, 2, True, 36278, 1, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (233, 3, 5, 2, 2, 2, False, 38822, 1, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (234, 1, 4, 1, 2, 2, False, 42297, 4, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (235, 1, 5, 2, 2, 2, False, 44475, 4, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (236, 5, 5, 1, 1, 2, True, 10147, 2, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (237, 2, 5, 1, 2, 2, False, 27645, 3, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (238, 3, 2, 2, 1, 2, False, 22650, 5, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (239, 2, 1, 2, 2, 2, False, 38222, 4, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (240, 1, 3, 1, 1, 2, True, 37547, 1, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (241, 1, 1, 2, 1, 2, False, 27138, 2, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (242, 3, 5, 1, 2, 2, False, 46708, 3, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (243, 2, 1, 1, 1, 2, False, 41878, 2, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (244, 2, 1, 2, 1, 2, True, 26068, 3, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (245, 5, 1, 1, 1, 2, False, 34929, 4, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (246, 4, 3, 1, 2, 2, False, 26073, 2, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (247, 1, 1, 1, 2, 2, True, 35748, 4, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (248, 3, 3, 2, 1, 2, True, 20021, 1, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (249, 2, 5, 1, 2, 2, True, 47342, 1, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (250, 4, 5, 2, 2, 2, False, 19190, 5, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (251, 2, 3, 1, 1, 2, False, 10386, 2, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (252, 5, 1, 1, 1, 2, True, 43479, 2, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (253, 3, 1, 2, 2, 2, False, 35215, 2, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (254, 3, 1, 2, 1, 2, False, 27391, 3, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (255, 3, 1, 2, 1, 2, False, 21434, 1, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (256, 5, 2, 2, 2, 2, False, 27516, 1, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (257, 1, 4, 2, 2, 2, True, 41775, 5, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (258, 5, 2, 1, 1, 2, True, 15282, 5, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (259, 5, 4, 2, 1, 2, True, 26065, 4, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (260, 5, 2, 1, 2, 2, False, 20281, 5, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (261, 5, 4, 2, 1, 2, True, 45793, 1, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (262, 4, 2, 1, 1, 2, False, 47700, 4, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (263, 2, 4, 1, 1, 2, False, 36024, 2, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (264, 5, 2, 1, 2, 2, False, 37355, 3, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (265, 2, 5, 2, 2, 2, False, 19130, 3, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (266, 2, 1, 2, 1, 2, True, 46242, 3, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (267, 2, 2, 2, 1, 2, True, 40827, 5, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (268, 4, 5, 2, 1, 2, True, 36634, 3, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (269, 4, 2, 1, 2, 2, True, 43201, 2, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (270, 4, 5, 1, 2, 2, False, 17518, 2, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (271, 5, 4, 2, 2, 2, True, 19433, 2, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (272, 2, 4, 1, 2, 2, False, 37483, 5, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (273, 4, 5, 2, 1, 2, False, 31655, 4, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (274, 3, 2, 2, 2, 2, True, 22557, 1, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (275, 2, 1, 1, 1, 2, False, 49447, 4, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (276, 1, 3, 2, 1, 2, True, 35163, 5, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (277, 1, 3, 2, 2, 2, True, 27656, 5, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (278, 4, 4, 1, 2, 2, False, 33494, 1, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (279, 2, 2, 1, 1, 2, False, 17737, 5, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (280, 1, 2, 1, 1, 2, True, 32111, 2, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (281, 4, 3, 2, 1, 2, True, 15687, 2, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (282, 5, 5, 2, 2, 2, False, 24544, 2, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (283, 4, 4, 1, 2, 2, False, 44500, 5, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (284, 1, 1, 1, 2, 2, True, 16103, 4, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (285, 2, 2, 1, 2, 2, False, 19542, 3, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (286, 4, 5, 1, 2, 2, True, 43907, 4, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (287, 5, 2, 1, 1, 2, False, 26358, 3, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (288, 1, 4, 2, 2, 2, True, 10304, 2, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (289, 2, 1, 2, 2, 2, False, 42571, 3, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (290, 2, 4, 2, 2, 2, True, 34993, 1, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (291, 2, 5, 2, 2, 2, False, 19572, 1, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (292, 2, 3, 2, 2, 2, True, 23342, 3, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (293, 1, 1, 2, 1, 2, True, 37885, 5, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (294, 1, 2, 1, 2, 2, False, 31183, 2, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (295, 1, 2, 2, 1, 2, False, 14785, 3, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (296, 4, 1, 2, 1, 2, True, 25626, 4, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (297, 4, 1, 1, 2, 2, False, 42412, 1, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (298, 1, 1, 1, 2, 2, False, 32398, 4, 22, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (299, 1, 1, 1, 1, 2, False, 49877, 3, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (300, 4, 2, 2, 2, 2, False, 25211, 4, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (301, 5, 4, 1, 1, 2, False, 36963, 2, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (302, 2, 5, 2, 1, 2, True, 29994, 5, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (303, 2, 5, 1, 2, 2, True, 21654, 4, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (304, 2, 1, 1, 2, 2, True, 21402, 4, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (305, 1, 2, 1, 1, 2, True, 22757, 2, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (306, 1, 5, 1, 1, 2, True, 10594, 5, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (307, 2, 4, 1, 1, 2, False, 28984, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (308, 1, 4, 1, 1, 2, False, 26835, 1, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (309, 1, 2, 1, 1, 2, True, 14461, 2, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (310, 2, 4, 1, 1, 2, False, 49648, 3, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (311, 2, 2, 1, 1, 2, False, 37922, 5, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (312, 1, 4, 2, 2, 2, False, 28767, 4, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (313, 5, 1, 2, 2, 2, True, 22847, 4, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (314, 3, 4, 1, 1, 2, False, 19787, 4, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (315, 5, 2, 1, 2, 2, False, 32013, 3, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (316, 5, 3, 2, 2, 2, False, 31434, 5, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (317, 3, 5, 2, 2, 2, True, 27369, 3, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (318, 3, 4, 2, 1, 2, True, 26984, 5, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (319, 2, 5, 2, 2, 2, True, 23879, 2, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (320, 4, 4, 2, 2, 2, True, 39849, 3, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (321, 5, 4, 2, 2, 2, True, 49068, 2, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (322, 5, 3, 2, 2, 2, True, 12019, 2, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (323, 5, 5, 1, 1, 2, False, 20385, 4, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (324, 3, 1, 2, 2, 2, False, 38723, 1, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (325, 3, 1, 2, 1, 2, False, 45658, 3, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (326, 2, 3, 1, 2, 2, False, 27783, 3, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (327, 4, 3, 1, 2, 2, True, 10644, 5, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (328, 4, 5, 2, 2, 2, False, 46181, 3, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (329, 3, 4, 1, 1, 2, True, 48495, 5, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (330, 3, 2, 2, 2, 2, True, 25350, 5, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (331, 2, 2, 2, 2, 2, False, 38310, 3, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (332, 5, 1, 2, 2, 2, True, 27030, 3, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (333, 1, 2, 1, 2, 2, True, 19501, 2, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (334, 5, 5, 1, 1, 2, True, 28305, 1, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (335, 4, 5, 2, 2, 2, False, 43337, 4, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (336, 1, 2, 1, 1, 2, False, 13011, 4, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (337, 4, 4, 2, 2, 2, True, 13936, 5, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (338, 4, 4, 1, 2, 2, True, 28988, 4, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (339, 3, 3, 2, 1, 2, True, 21238, 3, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (340, 2, 1, 2, 1, 2, True, 42306, 1, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (341, 2, 4, 2, 2, 2, False, 44195, 2, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (342, 3, 5, 1, 1, 2, True, 15886, 4, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (343, 5, 3, 2, 1, 2, True, 15750, 2, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (344, 1, 3, 1, 2, 2, False, 34223, 5, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (345, 4, 4, 2, 1, 2, True, 23722, 2, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (346, 5, 3, 1, 2, 2, False, 13818, 2, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (347, 2, 3, 1, 2, 2, True, 22993, 5, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (348, 5, 4, 1, 1, 2, False, 29114, 2, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (349, 2, 1, 1, 1, 2, False, 31087, 1, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (350, 2, 3, 1, 1, 2, True, 14707, 5, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (351, 4, 1, 1, 2, 2, False, 23528, 2, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (352, 2, 3, 1, 2, 2, False, 42859, 3, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (353, 2, 5, 2, 1, 2, False, 12674, 3, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (354, 1, 5, 1, 1, 2, False, 21357, 3, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (355, 4, 3, 2, 1, 2, True, 31555, 5, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (356, 3, 4, 1, 1, 2, True, 30497, 4, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (357, 4, 1, 2, 2, 2, True, 47162, 2, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (358, 2, 4, 1, 2, 2, False, 37204, 1, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (359, 4, 3, 2, 2, 2, True, 23395, 4, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (360, 1, 2, 1, 2, 2, True, 34350, 5, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (361, 5, 2, 1, 1, 2, True, 41644, 5, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (362, 3, 5, 2, 1, 2, False, 45850, 3, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (363, 3, 3, 1, 1, 2, False, 47026, 3, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (364, 3, 2, 2, 1, 2, False, 34729, 5, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (365, 4, 1, 2, 2, 2, False, 18994, 2, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (366, 5, 4, 2, 2, 2, False, 49387, 2, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (367, 2, 2, 1, 1, 2, True, 48670, 2, 22, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (368, 2, 4, 2, 1, 2, False, 12397, 3, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (369, 1, 5, 1, 2, 2, False, 48793, 4, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (370, 3, 2, 1, 1, 2, True, 27658, 2, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (371, 4, 2, 1, 2, 2, False, 43642, 3, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (372, 2, 1, 2, 2, 2, False, 20430, 2, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (373, 4, 1, 2, 2, 2, True, 46366, 1, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (374, 1, 1, 1, 2, 2, False, 38841, 5, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (375, 2, 3, 1, 2, 2, False, 10543, 1, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (376, 4, 4, 1, 2, 2, True, 28890, 2, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (377, 4, 5, 1, 1, 2, True, 18298, 3, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (378, 5, 5, 2, 2, 2, True, 17082, 5, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (379, 4, 2, 2, 2, 2, False, 35443, 4, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (380, 2, 1, 2, 2, 2, False, 34942, 2, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (381, 2, 3, 1, 1, 2, True, 17066, 5, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (382, 5, 3, 2, 2, 2, True, 16905, 3, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (383, 1, 2, 1, 1, 2, True, 45442, 5, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (384, 4, 1, 2, 1, 2, False, 16818, 5, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (385, 3, 3, 2, 1, 2, False, 13674, 5, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (386, 2, 1, 1, 1, 2, True, 12543, 4, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (387, 4, 5, 1, 1, 2, False, 12831, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (388, 3, 1, 1, 1, 2, False, 43022, 3, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (389, 3, 5, 1, 1, 2, False, 36308, 1, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (390, 4, 2, 1, 1, 2, False, 47067, 2, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (391, 4, 3, 1, 2, 2, True, 38004, 4, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (392, 4, 3, 1, 1, 2, False, 38982, 5, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (393, 4, 5, 2, 1, 2, False, 32175, 5, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (394, 1, 2, 2, 1, 2, False, 49996, 4, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (395, 4, 4, 1, 2, 2, True, 42213, 4, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (396, 5, 3, 1, 2, 2, False, 14487, 2, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (397, 2, 2, 2, 1, 2, False, 28207, 1, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (398, 2, 2, 1, 1, 2, True, 16605, 1, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (399, 5, 3, 1, 1, 2, True, 35007, 1, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (400, 3, 5, 1, 2, 2, True, 10436, 1, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (401, 3, 2, 1, 2, 2, True, 17205, 3, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (402, 2, 1, 1, 2, 2, True, 47808, 2, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (403, 4, 1, 1, 1, 2, True, 45615, 2, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (404, 3, 3, 2, 1, 2, False, 22042, 1, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (405, 4, 5, 2, 2, 2, True, 32961, 1, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (406, 1, 2, 2, 1, 2, False, 14092, 2, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (407, 5, 1, 1, 2, 2, False, 42774, 2, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (408, 2, 5, 2, 2, 2, False, 26276, 2, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (409, 5, 1, 2, 1, 2, False, 23698, 4, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (410, 5, 1, 1, 1, 2, False, 49754, 4, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (411, 2, 2, 1, 1, 2, True, 10435, 4, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (412, 2, 5, 2, 1, 2, False, 48544, 3, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (413, 1, 1, 1, 2, 2, True, 34409, 3, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (414, 2, 3, 1, 2, 2, True, 15766, 4, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (415, 4, 4, 1, 2, 2, True, 23513, 2, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (416, 2, 4, 2, 1, 2, True, 42728, 3, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (417, 1, 1, 2, 1, 2, True, 47507, 3, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (418, 1, 2, 1, 1, 2, True, 26121, 3, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (419, 5, 2, 1, 2, 2, True, 12871, 1, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (420, 5, 4, 1, 1, 2, False, 31571, 3, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (421, 3, 4, 2, 1, 2, False, 41037, 5, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (422, 2, 1, 2, 2, 2, False, 46817, 1, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (423, 5, 1, 2, 1, 2, False, 49553, 5, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (424, 5, 2, 2, 2, 2, True, 10783, 1, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (425, 1, 2, 2, 2, 2, True, 15984, 3, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (426, 1, 4, 2, 2, 2, False, 31757, 1, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (427, 2, 5, 1, 1, 2, False, 17381, 1, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (428, 2, 4, 2, 2, 2, True, 24100, 5, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (429, 1, 2, 2, 2, 2, False, 37381, 5, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (430, 5, 4, 1, 2, 2, True, 45917, 2, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (431, 2, 2, 1, 1, 2, False, 24093, 1, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (432, 2, 2, 1, 2, 2, True, 48493, 3, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (433, 1, 2, 1, 1, 2, False, 18162, 1, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (434, 3, 1, 1, 2, 2, False, 27844, 2, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (435, 1, 2, 2, 2, 2, True, 39639, 5, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (436, 5, 1, 2, 1, 2, True, 19565, 2, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (437, 2, 3, 1, 2, 2, False, 22504, 1, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (438, 3, 4, 2, 2, 2, True, 36276, 5, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (439, 2, 4, 1, 1, 2, False, 18556, 5, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (440, 1, 5, 2, 1, 2, True, 34236, 5, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (441, 5, 5, 2, 2, 2, False, 15079, 1, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (442, 5, 5, 2, 2, 2, False, 15747, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (443, 4, 1, 2, 1, 2, False, 40835, 1, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (444, 3, 1, 1, 1, 2, False, 16149, 4, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (445, 5, 4, 2, 2, 2, True, 17216, 1, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (446, 1, 2, 2, 2, 2, False, 41811, 1, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (447, 2, 3, 1, 1, 2, True, 47454, 3, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (448, 1, 3, 2, 2, 2, False, 25821, 1, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (449, 1, 1, 2, 1, 2, True, 11015, 1, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (450, 1, 4, 2, 2, 2, False, 43106, 5, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (451, 5, 1, 2, 1, 2, True, 35545, 1, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (452, 4, 1, 2, 2, 2, True, 13759, 3, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (453, 5, 4, 1, 1, 2, False, 32384, 2, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (454, 5, 4, 2, 2, 2, False, 44236, 5, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (455, 4, 4, 1, 1, 2, False, 40281, 1, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (456, 4, 2, 1, 1, 2, True, 46545, 1, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (457, 4, 5, 1, 1, 2, True, 40297, 4, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (458, 4, 3, 1, 1, 2, False, 45127, 4, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (459, 1, 5, 2, 2, 2, True, 37679, 2, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (460, 2, 5, 1, 1, 2, True, 11401, 5, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (461, 1, 5, 2, 2, 2, True, 22277, 3, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (462, 5, 1, 2, 1, 2, True, 27817, 3, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (463, 4, 4, 2, 2, 2, True, 43907, 1, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (464, 4, 3, 1, 2, 2, True, 13201, 2, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (465, 4, 5, 1, 1, 2, False, 22436, 4, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (466, 4, 2, 2, 2, 2, True, 12350, 1, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (467, 3, 2, 1, 2, 2, False, 29460, 2, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (468, 2, 4, 1, 2, 2, False, 39704, 1, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (469, 2, 5, 2, 2, 2, False, 14302, 2, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (470, 5, 3, 2, 2, 2, True, 25049, 2, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (471, 4, 4, 1, 1, 2, False, 47658, 4, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (472, 5, 3, 1, 1, 2, False, 22845, 1, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (473, 3, 5, 1, 2, 2, False, 17849, 1, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (474, 5, 5, 1, 1, 2, True, 27932, 4, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (475, 2, 3, 1, 1, 2, True, 35103, 3, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (476, 4, 2, 2, 2, 2, True, 39632, 1, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (477, 5, 1, 1, 1, 2, True, 24022, 4, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (478, 3, 5, 2, 1, 2, True, 11728, 3, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (479, 5, 1, 1, 2, 2, True, 49106, 5, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (480, 1, 1, 1, 2, 2, True, 10461, 2, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (481, 4, 5, 2, 1, 2, False, 34283, 4, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (482, 1, 4, 1, 1, 2, False, 42351, 2, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (483, 3, 4, 1, 1, 2, True, 46707, 5, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (484, 4, 4, 2, 1, 2, True, 43570, 5, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (485, 4, 2, 1, 1, 2, False, 16158, 4, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (486, 3, 2, 1, 1, 2, True, 38053, 3, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (487, 3, 4, 1, 2, 2, False, 11061, 4, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (488, 5, 2, 1, 1, 2, True, 40385, 4, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (489, 3, 2, 1, 1, 2, True, 20423, 4, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (490, 5, 4, 2, 1, 2, True, 21357, 2, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (491, 2, 3, 2, 1, 2, False, 40373, 5, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (492, 2, 4, 1, 1, 2, False, 13888, 2, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (493, 1, 4, 1, 2, 2, False, 21708, 1, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (494, 2, 4, 1, 2, 2, True, 12868, 2, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (495, 2, 2, 2, 2, 2, False, 10592, 5, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (496, 2, 1, 1, 1, 2, True, 35360, 5, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (497, 3, 1, 2, 1, 2, False, 20913, 2, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (498, 1, 1, 2, 1, 2, False, 31045, 5, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (499, 3, 3, 1, 2, 2, True, 46211, 3, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (500, 2, 2, 2, 2, 2, True, 30123, 4, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (501, 3, 3, 2, 1, 2, True, 43085, 5, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (502, 3, 5, 1, 2, 2, False, 39360, 1, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (503, 3, 3, 2, 2, 2, False, 42817, 4, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (504, 3, 3, 1, 1, 2, True, 18187, 3, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (505, 5, 4, 2, 2, 2, False, 31444, 5, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (506, 1, 4, 2, 2, 2, False, 35033, 5, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (507, 2, 1, 1, 1, 2, False, 47449, 5, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (508, 2, 5, 1, 1, 2, False, 16616, 2, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (509, 1, 1, 1, 1, 2, False, 35230, 1, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (510, 5, 3, 1, 1, 2, False, 34652, 3, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (511, 1, 5, 1, 1, 2, True, 46289, 5, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (512, 2, 4, 1, 1, 2, False, 17570, 4, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (513, 3, 4, 2, 1, 2, False, 32467, 2, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (514, 5, 4, 2, 1, 2, False, 47912, 3, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (515, 3, 3, 2, 2, 2, False, 11876, 5, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (516, 1, 3, 1, 1, 2, False, 42482, 3, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (517, 4, 4, 2, 1, 2, False, 16215, 5, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (518, 4, 4, 2, 1, 2, False, 16439, 5, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (519, 2, 3, 1, 2, 2, True, 19070, 1, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (520, 4, 1, 1, 2, 2, True, 12516, 4, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (521, 4, 3, 2, 1, 2, True, 20223, 2, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (522, 5, 1, 1, 1, 2, True, 37983, 4, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (523, 4, 4, 1, 1, 2, True, 33199, 5, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (524, 5, 2, 1, 1, 2, True, 47224, 1, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (525, 4, 3, 2, 2, 2, True, 48943, 2, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (526, 5, 3, 2, 1, 2, True, 45808, 5, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (527, 5, 4, 2, 2, 2, True, 11806, 3, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (528, 5, 4, 1, 2, 2, True, 27925, 5, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (529, 3, 1, 2, 2, 2, True, 43157, 4, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (530, 4, 2, 2, 2, 2, True, 22449, 3, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (531, 5, 3, 1, 1, 2, False, 28566, 5, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (532, 5, 5, 1, 1, 2, False, 13352, 2, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (533, 2, 5, 1, 2, 2, True, 14187, 5, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (534, 3, 2, 2, 2, 2, False, 30446, 2, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (535, 3, 2, 1, 1, 2, True, 35009, 5, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (536, 2, 4, 2, 2, 2, True, 10010, 3, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (537, 2, 3, 2, 1, 2, False, 25729, 1, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (538, 5, 3, 1, 2, 2, False, 32997, 3, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (539, 3, 3, 1, 2, 2, True, 15571, 2, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (540, 3, 4, 1, 1, 2, True, 21889, 4, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (541, 2, 3, 2, 1, 2, False, 43805, 1, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (542, 2, 3, 2, 1, 2, True, 47387, 3, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (543, 2, 3, 2, 2, 2, False, 48458, 1, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (544, 5, 5, 2, 2, 2, False, 28901, 2, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (545, 2, 3, 1, 1, 2, True, 13428, 1, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (546, 2, 5, 1, 2, 2, True, 21646, 1, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (547, 2, 1, 1, 2, 2, False, 35499, 4, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (548, 5, 1, 2, 2, 2, False, 12651, 3, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (549, 5, 3, 2, 1, 2, False, 28829, 4, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (550, 1, 1, 2, 1, 2, True, 11605, 4, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (551, 1, 1, 2, 1, 2, False, 15261, 1, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (552, 5, 4, 2, 2, 2, False, 49221, 2, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (553, 2, 3, 1, 1, 2, True, 35776, 1, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (554, 4, 3, 2, 2, 2, False, 39679, 2, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (555, 2, 2, 1, 2, 2, False, 49292, 4, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (556, 2, 4, 1, 1, 2, False, 17240, 5, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (557, 3, 1, 2, 2, 2, True, 19131, 2, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (558, 3, 4, 2, 2, 2, True, 22827, 1, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (559, 2, 3, 1, 1, 2, False, 24876, 1, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (560, 2, 1, 1, 2, 2, True, 21318, 5, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (561, 5, 2, 2, 1, 2, False, 43237, 2, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (562, 1, 2, 2, 1, 2, False, 28221, 2, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (563, 4, 4, 2, 1, 2, True, 11425, 1, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (564, 1, 2, 1, 2, 2, False, 12357, 4, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (565, 4, 1, 1, 2, 2, True, 36246, 5, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (566, 5, 3, 1, 1, 2, False, 30762, 5, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (567, 5, 4, 2, 2, 2, False, 15500, 5, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (568, 5, 1, 2, 1, 2, False, 10999, 4, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (569, 3, 5, 1, 1, 2, False, 47106, 2, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (570, 5, 5, 2, 1, 2, True, 19858, 1, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (571, 5, 5, 1, 2, 2, True, 13858, 3, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (572, 1, 3, 2, 1, 2, True, 12658, 5, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (573, 3, 4, 2, 2, 2, True, 41431, 3, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (574, 1, 5, 2, 2, 2, True, 21174, 2, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (575, 1, 1, 1, 1, 2, True, 11697, 4, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (576, 2, 2, 2, 2, 2, True, 44567, 3, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (577, 3, 2, 1, 2, 2, False, 27978, 5, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (578, 1, 3, 1, 2, 2, False, 16438, 1, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (579, 5, 1, 1, 2, 2, False, 25620, 2, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (580, 3, 5, 1, 1, 2, True, 43561, 3, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (581, 3, 5, 1, 1, 2, False, 44958, 3, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (582, 5, 5, 2, 1, 2, False, 29741, 2, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (583, 3, 4, 1, 1, 2, False, 23784, 4, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (584, 2, 3, 1, 2, 2, True, 24671, 5, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (585, 5, 1, 1, 1, 2, False, 30508, 5, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (586, 2, 3, 1, 1, 2, True, 23564, 3, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (587, 2, 1, 2, 1, 2, False, 48875, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (588, 3, 2, 1, 1, 2, False, 18046, 3, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (589, 4, 2, 1, 2, 2, True, 48176, 4, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (590, 5, 2, 1, 1, 2, False, 14383, 5, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (591, 1, 4, 1, 1, 2, True, 48136, 2, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (592, 4, 1, 1, 2, 2, True, 10633, 5, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (593, 3, 3, 2, 1, 2, True, 34139, 5, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (594, 2, 1, 1, 2, 2, False, 46338, 5, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (595, 5, 2, 1, 1, 2, False, 17474, 5, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (596, 2, 3, 2, 2, 2, True, 45910, 2, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (597, 5, 3, 2, 1, 2, True, 33796, 5, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (598, 1, 5, 1, 2, 2, False, 49138, 1, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (599, 3, 3, 1, 2, 2, False, 11760, 2, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (600, 1, 5, 1, 2, 2, False, 14997, 5, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (601, 2, 4, 2, 1, 2, True, 19072, 1, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (602, 3, 5, 2, 1, 2, False, 28298, 5, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (603, 4, 2, 1, 2, 2, False, 45421, 4, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (604, 2, 4, 1, 1, 2, False, 17130, 4, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (605, 4, 5, 1, 2, 2, False, 47886, 5, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (606, 3, 5, 2, 1, 2, True, 45434, 3, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (607, 3, 4, 1, 1, 2, False, 22043, 5, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (608, 1, 1, 2, 2, 2, True, 49112, 1, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (609, 2, 3, 1, 2, 2, True, 13209, 4, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (610, 2, 5, 2, 1, 2, False, 14114, 3, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (611, 1, 3, 2, 2, 2, True, 22007, 4, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (612, 5, 2, 2, 1, 2, False, 30667, 4, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (613, 4, 1, 2, 2, 2, False, 20463, 5, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (614, 4, 2, 2, 1, 2, True, 44264, 3, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (615, 5, 5, 1, 2, 2, True, 45891, 5, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (616, 5, 5, 2, 1, 2, False, 33736, 2, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (617, 4, 5, 1, 1, 2, False, 30538, 3, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (618, 4, 4, 1, 2, 2, False, 15093, 2, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (619, 5, 5, 1, 2, 2, True, 49987, 4, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (620, 1, 3, 2, 1, 2, False, 13053, 2, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (621, 1, 4, 1, 2, 2, False, 23291, 2, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (622, 4, 1, 2, 2, 2, True, 42968, 1, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (623, 5, 5, 2, 2, 2, True, 46660, 1, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (624, 2, 3, 2, 1, 2, True, 30103, 2, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (625, 4, 1, 1, 2, 2, False, 18537, 1, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (626, 4, 3, 1, 2, 2, True, 41002, 1, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (627, 4, 4, 1, 1, 2, False, 12302, 4, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (628, 2, 5, 2, 2, 2, False, 11949, 5, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (629, 5, 1, 2, 1, 2, True, 12136, 1, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (630, 5, 4, 2, 1, 2, False, 40677, 5, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (631, 2, 4, 1, 2, 2, True, 27497, 5, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (632, 5, 2, 2, 2, 2, False, 18228, 5, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (633, 3, 2, 1, 1, 2, False, 29755, 5, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (634, 5, 3, 1, 2, 2, False, 18525, 5, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (635, 1, 5, 2, 1, 2, False, 39876, 3, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (636, 3, 5, 2, 2, 2, False, 19414, 5, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (637, 3, 5, 2, 2, 2, True, 44715, 1, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (638, 1, 4, 1, 1, 2, True, 48257, 4, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (639, 3, 4, 1, 1, 2, False, 46219, 5, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (640, 4, 2, 2, 1, 2, True, 10891, 4, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (641, 1, 5, 2, 1, 2, True, 39004, 4, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (642, 5, 1, 1, 1, 2, True, 32377, 5, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (643, 5, 1, 1, 1, 2, True, 26400, 2, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (644, 4, 2, 2, 1, 2, True, 13681, 5, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (645, 2, 5, 1, 1, 2, False, 29282, 4, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (646, 1, 1, 2, 2, 2, True, 43435, 4, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (647, 4, 2, 1, 2, 2, False, 20662, 1, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (648, 3, 4, 2, 2, 2, False, 45223, 1, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (649, 2, 5, 2, 2, 2, False, 27446, 2, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (650, 1, 2, 1, 2, 2, False, 24063, 2, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (651, 2, 3, 2, 2, 2, False, 44663, 4, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (652, 5, 1, 2, 1, 2, True, 41380, 2, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (653, 1, 1, 2, 2, 2, False, 45255, 4, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (654, 2, 1, 1, 1, 2, False, 35020, 2, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (655, 1, 5, 2, 1, 2, False, 43343, 3, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (656, 1, 1, 2, 2, 2, True, 41941, 4, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (657, 5, 5, 1, 2, 2, True, 34248, 5, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (658, 3, 5, 2, 1, 2, False, 20141, 4, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (659, 2, 3, 1, 2, 2, False, 21445, 4, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (660, 2, 2, 1, 2, 2, False, 25014, 3, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (661, 1, 3, 2, 2, 2, False, 42041, 4, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (662, 2, 3, 1, 1, 2, True, 20867, 2, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (663, 2, 2, 1, 1, 2, True, 25975, 4, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (664, 4, 4, 1, 2, 2, False, 42112, 3, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (665, 1, 4, 2, 2, 2, False, 17785, 4, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (666, 1, 1, 1, 2, 2, False, 43170, 1, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (667, 3, 3, 1, 2, 2, True, 37255, 4, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (668, 4, 4, 1, 1, 2, False, 13162, 3, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (669, 1, 3, 2, 2, 2, False, 24666, 1, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (670, 1, 1, 1, 2, 2, False, 23272, 4, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (671, 5, 4, 1, 2, 2, True, 10143, 3, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (672, 1, 2, 1, 1, 2, True, 16559, 4, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (673, 2, 1, 1, 1, 2, True, 25245, 5, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (674, 2, 3, 2, 1, 2, True, 46641, 2, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (675, 1, 4, 2, 2, 2, True, 24456, 3, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (676, 2, 3, 2, 2, 2, False, 26008, 5, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (677, 5, 5, 1, 2, 2, False, 26454, 1, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (678, 1, 5, 1, 1, 2, False, 21835, 3, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (679, 5, 1, 1, 1, 2, True, 10553, 3, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (680, 5, 1, 1, 2, 2, True, 16226, 1, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (681, 5, 1, 1, 1, 2, False, 18035, 3, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (682, 1, 1, 2, 1, 2, False, 19624, 1, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (683, 3, 4, 2, 2, 2, False, 32093, 3, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (684, 4, 2, 1, 2, 2, True, 44137, 5, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (685, 3, 1, 2, 1, 2, False, 36070, 3, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (686, 3, 3, 2, 2, 2, True, 46075, 3, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (687, 4, 4, 1, 2, 2, True, 41003, 5, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (688, 2, 3, 1, 1, 2, False, 11764, 5, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (689, 3, 4, 2, 2, 2, True, 26040, 2, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (690, 5, 5, 1, 2, 2, True, 39937, 3, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (691, 1, 5, 2, 2, 2, False, 34862, 5, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (692, 5, 3, 1, 1, 2, True, 32182, 4, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (693, 4, 4, 2, 2, 2, False, 44576, 2, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (694, 2, 2, 1, 1, 2, False, 21106, 2, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (695, 2, 4, 2, 2, 2, False, 35048, 2, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (696, 1, 5, 2, 2, 2, False, 14507, 5, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (697, 1, 3, 1, 2, 2, True, 29766, 2, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (698, 5, 1, 2, 2, 2, False, 27279, 3, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (699, 4, 1, 2, 1, 2, True, 11800, 5, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (700, 5, 5, 1, 1, 2, True, 40503, 5, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (701, 5, 5, 2, 2, 2, True, 34472, 4, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (702, 2, 3, 2, 2, 2, False, 42117, 2, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (703, 5, 2, 2, 2, 2, False, 13994, 2, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (704, 1, 3, 2, 2, 2, False, 39787, 2, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (705, 3, 4, 1, 1, 2, True, 12529, 3, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (706, 5, 5, 2, 1, 2, True, 31886, 3, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (707, 1, 1, 2, 2, 2, True, 12197, 1, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (708, 5, 2, 1, 2, 2, False, 21041, 1, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (709, 3, 1, 2, 1, 2, True, 43562, 5, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (710, 2, 4, 1, 2, 2, False, 25339, 1, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (711, 5, 4, 1, 2, 2, True, 49362, 4, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (712, 4, 1, 1, 2, 2, False, 25607, 4, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (713, 2, 4, 1, 2, 2, False, 24329, 4, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (714, 1, 2, 1, 1, 2, True, 34187, 2, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (715, 2, 4, 2, 2, 2, False, 34983, 2, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (716, 1, 3, 2, 1, 2, True, 33477, 3, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (717, 3, 3, 2, 2, 2, True, 13148, 2, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (718, 5, 3, 1, 2, 2, False, 36537, 2, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (719, 5, 4, 2, 2, 2, True, 14794, 1, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (720, 1, 2, 1, 2, 2, True, 43858, 2, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (721, 2, 4, 2, 2, 2, False, 48115, 3, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (722, 3, 2, 1, 2, 2, True, 37386, 1, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (723, 5, 5, 1, 2, 2, True, 10714, 5, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (724, 5, 4, 2, 1, 2, False, 13134, 5, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (725, 1, 2, 1, 2, 2, False, 29302, 1, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (726, 2, 5, 1, 2, 2, True, 44835, 3, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (727, 4, 5, 1, 1, 2, False, 35926, 4, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (728, 4, 3, 1, 1, 2, False, 28239, 5, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (729, 5, 1, 2, 2, 2, True, 12453, 1, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (730, 3, 3, 2, 1, 2, True, 30053, 4, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (731, 2, 4, 2, 1, 2, True, 36499, 3, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (732, 3, 5, 1, 2, 2, True, 30784, 5, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (733, 4, 4, 2, 1, 2, True, 22093, 4, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (734, 3, 5, 1, 2, 2, False, 39061, 3, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (735, 5, 5, 2, 1, 2, True, 31332, 5, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (736, 3, 2, 1, 2, 2, False, 17453, 4, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (737, 2, 5, 2, 2, 2, False, 13770, 3, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (738, 5, 5, 1, 1, 2, False, 42925, 1, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (739, 5, 3, 1, 2, 2, True, 11223, 2, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (740, 3, 3, 1, 1, 2, True, 39554, 1, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (741, 1, 2, 1, 2, 2, False, 33249, 2, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (742, 1, 5, 1, 1, 2, False, 17210, 3, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (743, 5, 3, 1, 2, 2, True, 21086, 3, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (744, 2, 5, 2, 2, 2, True, 14904, 1, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (745, 5, 1, 1, 2, 2, False, 48381, 3, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (746, 1, 4, 1, 1, 2, False, 45415, 4, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (747, 1, 3, 2, 1, 2, True, 25023, 3, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (748, 5, 1, 2, 2, 2, True, 15987, 3, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (749, 2, 4, 2, 1, 2, True, 37641, 1, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (750, 2, 1, 2, 1, 2, False, 27306, 4, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (751, 5, 5, 2, 1, 2, True, 48224, 2, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (752, 5, 5, 2, 1, 2, False, 11560, 1, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (753, 2, 3, 2, 1, 2, False, 16332, 5, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (754, 2, 2, 1, 2, 2, False, 42233, 4, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (755, 5, 2, 1, 2, 2, False, 33840, 1, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (756, 3, 5, 2, 1, 2, False, 39563, 4, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (757, 4, 5, 1, 2, 2, False, 13845, 5, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (758, 5, 4, 1, 1, 2, True, 12803, 2, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (759, 2, 5, 2, 1, 2, False, 30195, 4, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (760, 2, 4, 1, 2, 2, False, 25535, 4, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (761, 3, 3, 1, 1, 2, True, 17922, 5, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (762, 5, 1, 2, 1, 2, True, 18417, 2, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (763, 2, 4, 1, 1, 2, True, 37135, 5, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (764, 4, 4, 1, 2, 2, False, 13967, 1, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (765, 4, 4, 1, 1, 2, True, 21723, 5, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (766, 1, 5, 1, 1, 2, True, 33336, 1, 21, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (767, 4, 5, 2, 2, 2, True, 39556, 3, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (768, 2, 5, 2, 1, 2, False, 43305, 4, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (769, 2, 2, 1, 2, 2, True, 18405, 2, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (770, 4, 2, 2, 1, 2, True, 14575, 3, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (771, 4, 5, 1, 2, 2, False, 15671, 5, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (772, 4, 3, 2, 1, 2, True, 46395, 2, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (773, 2, 2, 2, 1, 2, False, 25748, 2, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (774, 1, 4, 1, 2, 2, False, 39455, 1, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (775, 2, 4, 1, 2, 2, False, 14628, 4, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (776, 2, 4, 1, 1, 2, False, 22130, 3, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (777, 2, 3, 2, 2, 2, True, 24559, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (778, 5, 3, 1, 1, 2, True, 19735, 2, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (779, 4, 1, 1, 1, 2, False, 21535, 4, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (780, 1, 3, 2, 1, 2, False, 45026, 1, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (781, 4, 1, 1, 1, 2, False, 12403, 4, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (782, 2, 1, 1, 1, 2, False, 14767, 3, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (783, 2, 5, 1, 2, 2, False, 14390, 4, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (784, 5, 4, 1, 2, 2, True, 29692, 1, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (785, 1, 4, 2, 2, 2, False, 15034, 1, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (786, 5, 4, 2, 1, 2, False, 18176, 2, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (787, 1, 2, 2, 2, 2, False, 44618, 2, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (788, 4, 5, 2, 2, 2, True, 37058, 3, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (789, 5, 1, 1, 2, 2, False, 43803, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (790, 3, 1, 1, 2, 2, False, 30609, 5, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (791, 2, 5, 1, 2, 2, False, 13918, 5, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (792, 1, 2, 1, 2, 2, True, 21185, 1, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (793, 5, 5, 1, 1, 2, False, 25071, 5, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (794, 3, 2, 1, 2, 2, False, 19402, 3, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (795, 2, 5, 2, 1, 2, True, 31586, 2, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (796, 3, 2, 1, 1, 2, False, 39144, 5, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (797, 3, 2, 2, 1, 2, True, 29449, 3, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (798, 3, 4, 2, 2, 2, False, 16565, 3, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (799, 5, 5, 1, 2, 2, False, 29123, 3, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (800, 2, 5, 1, 2, 2, True, 34573, 4, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (801, 1, 4, 2, 2, 2, True, 40488, 1, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (802, 1, 3, 2, 2, 2, True, 47707, 2, 22, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (803, 4, 1, 2, 1, 2, True, 24816, 5, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (804, 1, 3, 2, 2, 2, True, 34161, 2, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (805, 5, 5, 1, 1, 2, False, 32533, 3, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (806, 3, 1, 1, 2, 2, True, 31937, 1, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (807, 3, 1, 1, 1, 2, True, 40990, 5, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (808, 4, 2, 1, 1, 2, False, 18971, 2, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (809, 1, 1, 2, 1, 2, True, 37856, 2, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (810, 2, 1, 1, 2, 2, True, 47500, 3, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (811, 5, 2, 2, 1, 2, True, 18382, 3, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (812, 5, 5, 1, 1, 2, True, 40426, 3, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (813, 5, 5, 2, 1, 2, False, 24444, 2, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (814, 2, 2, 2, 2, 2, True, 13615, 5, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (815, 2, 3, 1, 2, 2, True, 21198, 3, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (816, 2, 3, 1, 2, 2, False, 45061, 3, 17, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (817, 2, 2, 1, 1, 2, True, 27630, 2, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (818, 3, 2, 1, 2, 2, True, 44957, 4, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (819, 5, 1, 1, 1, 2, True, 29732, 2, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (820, 2, 3, 1, 1, 2, True, 24603, 3, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (821, 5, 5, 2, 1, 2, True, 34472, 3, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (822, 3, 3, 1, 1, 2, False, 48452, 2, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (823, 3, 3, 2, 2, 2, False, 34682, 4, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (824, 5, 3, 1, 1, 2, True, 22425, 1, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (825, 5, 4, 2, 1, 2, False, 13019, 2, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (826, 4, 4, 1, 2, 2, True, 19883, 1, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (827, 2, 1, 2, 2, 2, True, 10980, 3, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (828, 5, 3, 1, 2, 2, True, 43884, 1, 2, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (829, 1, 4, 2, 2, 2, False, 16433, 1, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (830, 2, 2, 2, 1, 2, True, 45319, 5, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (831, 4, 3, 2, 1, 2, False, 24514, 4, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (832, 2, 2, 1, 2, 2, True, 15637, 3, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (833, 3, 2, 1, 1, 2, False, 18579, 4, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (834, 1, 5, 1, 1, 2, True, 16233, 4, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (835, 3, 5, 2, 1, 2, False, 27550, 5, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (836, 2, 4, 2, 2, 2, True, 14582, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (837, 4, 5, 1, 2, 2, False, 43988, 4, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (838, 5, 4, 1, 1, 2, True, 16133, 2, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (839, 1, 2, 2, 2, 2, False, 40568, 3, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (840, 2, 2, 1, 1, 2, True, 20516, 2, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (841, 4, 3, 2, 1, 2, True, 35258, 3, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (842, 4, 2, 1, 2, 2, False, 16273, 2, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (843, 4, 4, 2, 2, 2, False, 40926, 3, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (844, 2, 3, 2, 1, 2, True, 37331, 5, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (845, 2, 2, 1, 2, 2, True, 41152, 2, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (846, 5, 5, 2, 2, 2, True, 38113, 5, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (847, 4, 5, 2, 1, 2, True, 41972, 1, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (848, 2, 4, 2, 2, 2, True, 49679, 2, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (849, 1, 1, 1, 2, 2, False, 47627, 5, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (850, 4, 2, 1, 2, 2, False, 49655, 2, 14, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (851, 2, 1, 1, 2, 2, True, 16225, 5, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (852, 3, 1, 2, 2, 2, False, 31444, 5, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (853, 1, 2, 1, 2, 2, False, 10718, 3, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (854, 4, 2, 2, 2, 2, True, 17646, 5, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (855, 2, 1, 2, 1, 2, False, 29441, 5, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (856, 1, 2, 1, 2, 2, False, 14413, 2, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (857, 3, 3, 2, 1, 2, True, 33053, 1, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (858, 3, 3, 1, 1, 2, False, 14635, 5, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (859, 3, 4, 2, 1, 2, True, 43295, 1, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (860, 3, 2, 2, 2, 2, True, 21450, 2, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (861, 2, 3, 1, 2, 2, False, 47580, 3, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (862, 2, 2, 1, 1, 2, True, 15247, 1, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (863, 1, 2, 1, 2, 2, False, 41473, 5, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (864, 4, 3, 1, 1, 2, False, 11051, 4, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (865, 1, 4, 2, 1, 2, True, 46301, 2, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (866, 3, 3, 2, 2, 2, True, 36141, 1, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (867, 5, 1, 2, 2, 2, False, 21239, 1, 3, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (868, 5, 1, 1, 2, 2, False, 15659, 2, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (869, 3, 4, 1, 1, 2, False, 35695, 3, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (870, 5, 3, 2, 1, 2, True, 17955, 3, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (871, 5, 5, 1, 2, 2, False, 38057, 1, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (872, 2, 4, 2, 2, 2, False, 48473, 1, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (873, 4, 1, 1, 2, 2, True, 13399, 3, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (874, 1, 5, 2, 2, 2, False, 42652, 4, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (875, 1, 5, 2, 1, 2, False, 11073, 2, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (876, 2, 1, 1, 2, 2, True, 47770, 2, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (877, 3, 3, 2, 1, 2, True, 10942, 3, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (878, 1, 2, 2, 1, 2, False, 41530, 3, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (879, 5, 3, 1, 1, 2, False, 35351, 3, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (880, 2, 3, 1, 2, 2, False, 35409, 2, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (881, 2, 5, 2, 1, 2, False, 46643, 2, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (882, 5, 1, 2, 2, 2, True, 20402, 4, 29, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (883, 5, 1, 2, 1, 2, False, 44422, 3, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (884, 4, 1, 1, 2, 2, False, 47098, 4, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (885, 3, 5, 2, 1, 2, True, 29337, 2, 6, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (886, 2, 4, 1, 1, 2, True, 28274, 2, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (887, 2, 1, 2, 1, 2, True, 11860, 4, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (888, 4, 2, 2, 2, 2, False, 25045, 1, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (889, 5, 4, 1, 1, 2, False, 24733, 3, 18, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (890, 3, 2, 1, 1, 2, True, 29255, 1, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (891, 2, 5, 2, 2, 2, True, 49007, 2, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (892, 4, 3, 2, 1, 2, False, 12361, 3, 7, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (893, 5, 5, 2, 1, 2, False, 48243, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (894, 2, 4, 1, 2, 2, False, 22805, 1, 25, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (895, 3, 1, 2, 1, 2, False, 46384, 5, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (896, 5, 2, 1, 1, 2, False, 28976, 5, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (897, 1, 3, 1, 1, 2, True, 47171, 3, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (898, 3, 1, 2, 1, 2, True, 39951, 3, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (899, 4, 3, 2, 1, 2, False, 45143, 1, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (900, 1, 3, 1, 2, 2, False, 42498, 4, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (901, 5, 2, 1, 1, 2, False, 19413, 1, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (902, 2, 3, 1, 1, 2, True, 40627, 5, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (903, 4, 4, 2, 2, 2, False, 49875, 2, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (904, 4, 2, 2, 2, 2, True, 34331, 5, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (905, 2, 4, 1, 1, 2, True, 29413, 4, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (906, 2, 1, 2, 2, 2, True, 31042, 5, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (907, 2, 1, 1, 2, 2, False, 31872, 3, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (908, 5, 2, 2, 2, 2, True, 23434, 5, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (909, 1, 1, 1, 2, 2, True, 34429, 1, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (910, 3, 4, 2, 2, 2, True, 34913, 3, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (911, 1, 2, 2, 2, 2, False, 13807, 1, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (912, 3, 3, 2, 1, 2, False, 18990, 5, 14, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (913, 1, 2, 1, 2, 2, True, 45373, 2, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (914, 4, 3, 1, 1, 2, True, 44493, 4, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (915, 3, 1, 1, 2, 2, True, 25256, 4, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (916, 5, 5, 1, 2, 2, True, 17598, 4, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (917, 2, 3, 1, 1, 2, True, 18824, 2, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (918, 5, 2, 2, 2, 2, True, 41182, 2, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (919, 2, 3, 2, 2, 2, False, 39191, 1, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (920, 5, 4, 1, 1, 2, True, 10708, 4, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (921, 5, 1, 1, 2, 2, False, 37042, 4, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (922, 4, 3, 2, 1, 2, True, 42651, 4, 8, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (923, 2, 3, 1, 2, 2, False, 23691, 5, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (924, 1, 2, 1, 1, 2, True, 38321, 5, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (925, 1, 5, 2, 1, 2, True, 17087, 2, 26, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (926, 1, 3, 1, 2, 2, False, 27526, 5, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (927, 1, 4, 1, 2, 2, True, 44458, 4, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (928, 4, 5, 2, 1, 2, False, 26741, 3, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (929, 3, 3, 2, 1, 2, False, 32528, 3, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (930, 3, 5, 2, 2, 2, False, 30312, 1, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (931, 2, 5, 1, 1, 2, False, 23718, 3, 22, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (932, 3, 4, 1, 1, 2, False, 29053, 4, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (933, 2, 4, 1, 2, 2, False, 30410, 1, 8, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (934, 5, 3, 2, 1, 2, False, 14389, 1, 29, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (935, 5, 1, 1, 2, 2, False, 33003, 4, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (936, 5, 3, 2, 2, 2, False, 41672, 4, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (937, 4, 3, 1, 1, 2, False, 13311, 5, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (938, 1, 2, 2, 1, 2, False, 37194, 1, 18, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (939, 4, 1, 1, 1, 2, True, 12894, 2, 9, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (940, 4, 2, 1, 1, 2, True, 22200, 2, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (941, 5, 3, 2, 2, 2, False, 31102, 5, 3, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (942, 4, 3, 2, 2, 2, False, 49101, 5, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (943, 1, 1, 1, 1, 2, False, 36051, 3, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (944, 5, 2, 2, 1, 2, False, 36643, 1, 23, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (945, 3, 3, 2, 2, 2, False, 40316, 3, 2, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (946, 4, 1, 1, 2, 2, False, 48240, 4, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (947, 5, 2, 2, 1, 2, False, 18228, 3, 10, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (948, 4, 3, 2, 1, 2, True, 13279, 5, 1, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (949, 4, 2, 2, 1, 2, True, 47445, 3, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (950, 3, 3, 2, 1, 2, True, 40348, 4, 16, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (951, 5, 1, 1, 2, 2, True, 34079, 1, 24, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (952, 5, 5, 1, 1, 2, False, 44281, 5, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (953, 5, 2, 2, 1, 2, True, 29697, 4, 15, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (954, 5, 2, 2, 2, 2, False, 20782, 2, 21, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (955, 1, 1, 1, 1, 2, False, 45453, 2, 15, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (956, 3, 4, 1, 2, 2, False, 18526, 4, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (957, 2, 1, 2, 2, 2, False, 10508, 2, 12, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (958, 2, 5, 2, 2, 2, True, 38992, 1, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (959, 4, 4, 1, 1, 2, False, 21960, 2, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (960, 3, 5, 1, 2, 2, False, 33541, 3, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (961, 1, 5, 1, 2, 2, False, 28391, 3, 27, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (962, 3, 2, 1, 1, 2, True, 32991, 4, 24, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (963, 4, 5, 1, 1, 2, False, 14055, 2, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (964, 1, 1, 2, 1, 2, False, 42524, 4, 25, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (965, 2, 4, 1, 1, 2, True, 19940, 5, 22, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (966, 2, 3, 2, 2, 2, False, 20817, 4, 5, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (967, 5, 3, 1, 2, 2, False, 15602, 1, 16, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (968, 1, 5, 2, 2, 2, True, 25189, 1, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (969, 2, 5, 2, 1, 2, True, 14968, 4, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (970, 5, 5, 1, 2, 2, True, 28832, 4, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (971, 4, 4, 2, 1, 2, False, 47027, 4, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (972, 1, 1, 1, 1, 2, True, 15514, 2, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (973, 2, 3, 2, 2, 2, True, 18797, 3, 19, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (974, 3, 5, 2, 2, 2, False, 29310, 4, 13, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (975, 5, 1, 1, 2, 2, False, 26859, 2, 30, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (976, 4, 5, 1, 1, 2, True, 29053, 3, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (977, 1, 4, 2, 2, 2, True, 21832, 4, 27, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (978, 2, 2, 2, 2, 2, False, 15948, 2, 20, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (979, 5, 5, 1, 2, 2, False, 39206, 3, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (980, 4, 5, 1, 1, 2, False, 36253, 1, 26, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (981, 4, 5, 2, 2, 2, False, 44201, 1, 23, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (982, 1, 3, 2, 2, 2, True, 30093, 1, 11, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (983, 1, 3, 2, 2, 2, False, 16938, 1, 10, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (984, 1, 2, 2, 2, 2, False, 24599, 2, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (985, 4, 5, 2, 1, 2, True, 31738, 1, 30, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (986, 4, 2, 1, 2, 2, False, 11205, 1, 1, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (987, 2, 1, 2, 1, 2, True, 48590, 5, 11, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (988, 4, 5, 1, 1, 2, False, 49761, 3, 28, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (989, 3, 4, 2, 1, 2, True, 16648, 1, 4, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (990, 2, 5, 1, 1, 2, False, 18204, 3, 6, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (991, 3, 2, 1, 2, 2, True, 48371, 1, 5, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (992, 2, 4, 1, 1, 2, False, 15764, 3, 28, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (993, 2, 2, 2, 2, 2, True, 19029, 2, 12, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (994, 1, 3, 2, 1, 2, True, 13498, 5, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (995, 5, 1, 1, 2, 2, True, 16065, 4, 19, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (996, 3, 5, 1, 1, 2, False, 39077, 3, 4, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (997, 5, 3, 1, 2, 2, True, 38048, 5, 9, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (998, 1, 4, 1, 1, 2, False, 34708, 4, 7, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (999, 1, 1, 2, 1, 2, False, 24548, 5, 17, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (1000, 2, 1, 1, 1, 2, False, 15858, 4, 20, 3, True, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'), + (1001, 5, 5, 1, 1, 3, True, 19619, 4, 13, 3, False, False, '2024-07-08 19:00:19', '2024-07-08 19:00:19'); \ No newline at end of file diff --git a/src/main/resources/dummy.sql b/src/main/resources/dummy.sql new file mode 100644 index 0000000..66d3103 --- /dev/null +++ b/src/main/resources/dummy.sql @@ -0,0 +1,52 @@ + +-- 고객(업체) +INSERT INTO client (client_id, name, auth_code, phone, is_deleted, created_at, updated_at) +VALUES + (1, '업체 A', 'AUTH12345', '010-1234-5678', false, NOW(), NOW()); + + + +-- 사용자(업체의 직원) +INSERT INTO user (user_id, client_id, email, password, name, phone, is_deleted, created_at, updated_at) +VALUES + (1, 1, 'employee1@companyA.com', 'password1', '직원1', '010-1111-2222', false, NOW(), NOW()), + (2, 1, 'employee2@companyA.com', 'password2', '직원2', '010-3333-4444', false, NOW(), NOW()), + (3, 1, 'employee3@companyA.com', 'password3', '직원3', '010-5555-6666', false, NOW(), NOW()), + (4, 1, 'employee4@companyA.com', 'password4', '직원4', '010-7777-8888', false, NOW(), NOW()), + (5, 1, 'employee5@companyA.com', 'password5', '직원5', '010-9999-0000', false, NOW(), NOW()); + + + +-- 회원 데이터 +INSERT INTO member (member_id, client_id, name, description, email, phone, is_deleted, created_at, updated_at) +VALUES + (1, 1, '회원1', 'Description for Member 1', 'member1@companyA.com', '010-1111-2222', false, NOW(), NOW()), + (2, 1, '회원2', 'Description for Member 2', 'member2@companyA.com', '010-3333-4444', false, NOW(), NOW()), + (3, 1, '회원3', 'Description for Member 3', 'member3@companyA.com', '010-5555-6666', false, NOW(), NOW()), + (4, 1, '회원4', 'Description for Member 4', 'member4@companyA.com', '010-7777-8888', false, NOW(), NOW()), + (5, 1, '회원5', 'Description for Member 5', 'member5@companyA.com', '010-9999-0000', false, NOW(), NOW()); + + + +-- 동의정보 계좌 +INSERT INTO consent_account (member_id, owner, bank, number, sign_url, is_deleted, created_at, updated_at) +VALUES + (1, '회원1', 'Bank A', '1234567890', CONCAT('http://signurl.com/account/', 1), false, NOW(), NOW()), + (2, '회원2', 'Bank B', '2345678901', CONCAT('http://signurl.com/account/', 2), false, NOW(), NOW()), + (3, '회원3', 'Bank C', '3456789012', CONCAT('http://signurl.com/account/', 3), false, NOW(), NOW()), + (4, '회원4', 'Bank D', '4567890123', CONCAT('http://signurl.com/account/', 4), false, NOW(), NOW()), + (5, '회원5', 'Bank E', '5678901234', CONCAT('http://signurl.com/account/', 5), false, NOW(), NOW()); + + + +-- 상품 정보 +INSERT INTO item (item_id, client_id, name, description, price, image_url, is_basic, is_deleted, created_at, updated_at) +VALUES + (1, 1, 'Item 1', 'Description for Item 1', 1000, 'http://imageurl1.com', true, false, NOW(), NOW()), + (2, 1, 'Item 2', 'Description for Item 2', 2000, 'http://imageurl2.com', false, false, NOW(), NOW()), + (3, 1, 'Item 3', 'Description for Item 3', 3000, 'http://imageurl3.com', true, false, NOW(), NOW()), + (4, 1, 'Item 4', 'Description for Item 4', 4000, 'http://imageurl4.com', false, false, NOW(), NOW()), + (5, 1, 'Item 5', 'Description for Item 5', 5000, 'http://imageurl5.com', true, false, NOW(), NOW()); + + + diff --git a/src/main/resources/status.sql b/src/main/resources/status.sql new file mode 100644 index 0000000..ad3e3a8 --- /dev/null +++ b/src/main/resources/status.sql @@ -0,0 +1,30 @@ + +-- 계약 상태 +INSERT INTO contract_status (contract_status_id, name, is_deleted, created_at, updated_at) +VALUES + (1, '대기', false, NOW(), NOW()), + (2, '진행', false, NOW(), NOW()), + (3, '종료', false, NOW(), NOW()); + + +-- 결제 수단 +INSERT INTO payment_type (payment_type_id, name, basic_payment_type, is_deleted, created_at, updated_at) +VALUES + (1, '납부자 결제', false, false, NOW(), NOW()), + (2, '자동 이체', true, false, NOW(), NOW()); + + + +-- 청구 타입 +INSERT INTO invoice_type (invoice_type_id, name, is_deleted, created_at, updated_at) +VALUES + (1, '자동청구', false, NOW(), NOW()), + (2, '수동청구', false, NOW(), NOW()); + + +-- 납부 상태 +INSERT INTO payment_status (payment_status_id, name, is_deleted, created_at, updated_at) +VALUES + (1, '미납', false, NOW(), NOW()), + (2, '완납', false, NOW(), NOW()), + (3, '대기', false, NOW(), NOW()); \ No newline at end of file From 5c59e5948d2429c2ea67639b11371dc12fd0ccff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Wed, 10 Jul 2024 17:55:13 +0900 Subject: [PATCH 07/25] =?UTF-8?q?[K5P-49]=20[test]=20JdbcGenerateInvoiceWr?= =?UTF-8?q?iter=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yml | 3 + .../jdbc/JdbcGenerateInvoiceWriterTest.java | 92 ++++++++++++++++++- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 380d310..d1f8410 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -18,6 +18,9 @@ spring: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect format_sql: true + defer-datasource-initialization: false + datasource: + initialization-mode: never --- spring: mail: diff --git a/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java b/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java index 82d424a..2054435 100644 --- a/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java +++ b/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java @@ -1,5 +1,95 @@ package site.billingwise.batch.server_batch.batch.generateinvoice.jdbc; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.batch.item.Chunk; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.contract.PaymentType; +import site.billingwise.batch.server_batch.domain.invoice.InvoiceType; +import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; class JdbcGenerateInvoiceWriterTest { -} + + @Mock + private JdbcTemplate jdbcTemplate; + + @InjectMocks + private JdbcGenerateInvoiceWriter jdbcGenerateInvoiceWriter; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + @DisplayName("청구 생성(Write) 로직 테스트") + void testWrite() throws Exception { + // Given + InvoiceType autoInvoiceType = InvoiceType.builder() + .id(1L) + .name("자동청구") + .build(); + + InvoiceType manualInvoiceType = InvoiceType.builder() + .id(2L) + .name("수동청구") + .build(); + + PaymentType paymentType = PaymentType.builder() + .id(1L) + .name("납부자 결제") + .isBasic(true) + .build(); + + Contract contract = Contract.builder() + .id(1L) + .contractCycle(15) + .itemPrice(1000L) + .itemAmount(2) + .paymentType(paymentType) + .invoiceType(autoInvoiceType) + .build(); + + List contracts = Arrays.asList(contract); + + PaymentStatus unpaidPaymentStatus = PaymentStatus.builder() + .id(1L) + .name("미납") + .build(); + + when(jdbcTemplate.queryForObject(anyString(), any(Object[].class), any(RowMapper.class))) + .thenReturn(unpaidPaymentStatus); + + // When + jdbcGenerateInvoiceWriter.write(new Chunk<>(contracts)); + + // Then + ArgumentCaptor queryCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor pssCaptor = ArgumentCaptor.forClass(BatchPreparedStatementSetter.class); + + verify(jdbcTemplate, times(1)).batchUpdate(queryCaptor.capture(), pssCaptor.capture()); + + BatchPreparedStatementSetter pss = pssCaptor.getValue(); + assertNotNull(pss); + + String expectedSql = "insert into invoice (contract_id, invoice_type_id, payment_type_id, payment_status_id, charge_amount, contract_date, due_date, is_deleted, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, false, NOW(), NOW())"; + + assertEquals(expectedSql, queryCaptor.getValue()); + } +} \ No newline at end of file From d66d7b529f9c617dc7ac54252b7aba1915f4fd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Thu, 11 Jul 2024 14:04:33 +0900 Subject: [PATCH 08/25] =?UTF-8?q?[K5P-49]=20[fix,=20env]=20PaymentType?= =?UTF-8?q?=EC=B9=BC=EB=9F=BC=EB=AA=85=20=EB=B3=80=EA=B2=BD(basic=5Fpaymen?= =?UTF-8?q?t=5Ftype=20->=20is=5Fbasic)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/server_batch/domain/contract/PaymentType.java | 2 +- src/main/resources/status.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java b/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java index 5a1277d..e80181b 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java @@ -23,7 +23,7 @@ public class PaymentType extends BaseEntity { @Column(length = 50, nullable = false) private String name; - @Column(name = "basic_payment_type",nullable = false) + @Column(name = "is_basic",nullable = false) private Boolean isBasic; @OneToMany(mappedBy = "paymentType") diff --git a/src/main/resources/status.sql b/src/main/resources/status.sql index ad3e3a8..b697314 100644 --- a/src/main/resources/status.sql +++ b/src/main/resources/status.sql @@ -8,7 +8,7 @@ VALUES -- 결제 수단 -INSERT INTO payment_type (payment_type_id, name, basic_payment_type, is_deleted, created_at, updated_at) +INSERT INTO payment_type (payment_type_id, name, is_basic, is_deleted, created_at, updated_at) VALUES (1, '납부자 결제', false, false, NOW(), NOW()), (2, '자동 이체', true, false, NOW(), NOW()); From 79a811707a0447d9980eb6e5dc98daeb8567ea78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:30:14 +0900 Subject: [PATCH 09/25] =?UTF-8?q?[K5P-49]=20[feat,fix]=20=EC=86=8C?= =?UTF-8?q?=ED=94=84=ED=8A=B8=20=EB=94=9C=EB=A6=AC=ED=8A=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=9C=84=ED=95=9C=20=EA=B5=AC=ED=98=84=20=EB=B0=8F?= =?UTF-8?q?=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jdbc/JdbcGenerateInvoiceJobConfig.java | 2 +- .../generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java | 8 ++++++++ .../generateinvoice/rowmapper/JdbcContractRowMapper.java | 5 ++++- .../batch/server_batch/domain/common/BaseEntity.java | 4 ++++ .../batch/server_batch/domain/contract/Contract.java | 3 ++- .../server_batch/domain/contract/ContractStatus.java | 3 ++- .../batch/server_batch/domain/contract/PaymentType.java | 3 ++- .../batch/server_batch/domain/invoice/Invoice.java | 3 ++- .../batch/server_batch/domain/invoice/InvoiceType.java | 3 ++- .../batch/server_batch/domain/invoice/PaymentStatus.java | 3 ++- .../billingwise/batch/server_batch/domain/item/Item.java | 3 ++- .../batch/server_batch/domain/member/ConsentAccount.java | 3 ++- .../batch/server_batch/domain/member/Member.java | 3 ++- .../batch/server_batch/domain/payment/Payment.java | 3 ++- .../batch/server_batch/domain/payment/PaymentAccount.java | 3 ++- .../batch/server_batch/domain/payment/PaymentCard.java | 3 ++- .../batch/server_batch/domain/user/Client.java | 3 ++- .../billingwise/batch/server_batch/domain/user/User.java | 3 ++- 18 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java index f967c49..9ab1731 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java @@ -52,7 +52,7 @@ public ItemReader jdbcContractItemReader() { return new JdbcCursorItemReaderBuilder() .name("jdbcContractItemReader") .fetchSize(CHUNK_SIZE) - .sql("select c.*, pt.payment_type_id, pt.name as payment_type_name, it.invoice_type_id, it.name as invoice_type_name " + + .sql("select c.*, pt.payment_type_id, pt.name as payment_type_name, it.invoice_type_id, it.name as invoice_type_name, c.is_deleted " + "from contract c " + "join contract_status cs ON c.contract_status_id = cs.contract_status_id " + "join payment_type pt ON c.payment_type_id = pt.payment_type_id " + diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java index 175622f..834b693 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java @@ -38,6 +38,11 @@ public void write(Chunk chunk) throws Exception { List invoices = new ArrayList<>(); for(Contract contract : chunk) { + + if(contract.getIsDeleted()){ + continue; + } + // 수동 청구가 이미 만들어지면, 청구 생성 X if(!invoiceExists(contract, nextMonthValue, yearValue)){ // 약정일 @@ -53,6 +58,9 @@ public void write(Chunk chunk) throws Exception { .chargeAmount(contract.getItemPrice() * contract.getItemAmount()) .contractDate(setInvoiceDate) .dueDate(payDueDate) + .isDeleted(false) // isDeleted 필드 값을 설정 + .createdAt(now) // createdAt 필드 값을 설정 + .updatedAt(now) // updatedAt 필드 값을 설정 .build(); invoices.add(invoice); diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java index 1b13bb8..5d903fd 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java @@ -17,6 +17,7 @@ public Contract mapRow(ResultSet rs, int rowNum) throws SQLException { .itemAmount(rs.getInt("item_amount")) .contractCycle(rs.getInt("contract_cycle")) .paymentDueCycle(rs.getInt("payment_due_cycle")) + .isDeleted(rs.getBoolean("is_deleted")) .paymentType( PaymentType.builder() .id(rs.getLong("payment_type_id")) @@ -29,7 +30,9 @@ public Contract mapRow(ResultSet rs, int rowNum) throws SQLException { .name(rs.getString("invoice_type_name")) .build() ) - + .createdAt(rs.getTimestamp("created_at").toLocalDateTime()) + .updatedAt(rs.getTimestamp("updated_at").toLocalDateTime()) + .isDeleted(rs.getBoolean("is_deleted")) .build(); } } \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/common/BaseEntity.java b/src/main/java/site/billingwise/batch/server_batch/domain/common/BaseEntity.java index 6459b73..06423a7 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/common/BaseEntity.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/common/BaseEntity.java @@ -5,6 +5,8 @@ import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -14,6 +16,8 @@ @Getter @MappedSuperclass @EntityListeners(AuditingEntityListener.class) +@SuperBuilder +@NoArgsConstructor public abstract class BaseEntity { @CreatedDate diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/contract/Contract.java b/src/main/java/site/billingwise/batch/server_batch/domain/contract/Contract.java index ea6c622..1c7a325 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/contract/Contract.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/contract/Contract.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import site.billingwise.batch.server_batch.domain.invoice.Invoice; import site.billingwise.batch.server_batch.domain.invoice.InvoiceType; @@ -13,7 +14,7 @@ @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Contract extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/contract/ContractStatus.java b/src/main/java/site/billingwise/batch/server_batch/domain/contract/ContractStatus.java index 8fcc9fc..37dd843 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/contract/ContractStatus.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/contract/ContractStatus.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import java.util.ArrayList; @@ -10,7 +11,7 @@ @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class ContractStatus extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java b/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java index e80181b..e18b630 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/contract/PaymentType.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import site.billingwise.batch.server_batch.domain.invoice.Invoice; @@ -10,7 +11,7 @@ @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class PaymentType extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/Invoice.java b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/Invoice.java index 591f3b3..45d5407 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/Invoice.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/Invoice.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import site.billingwise.batch.server_batch.domain.contract.Contract; import site.billingwise.batch.server_batch.domain.contract.PaymentType; @@ -11,7 +12,7 @@ @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Invoice extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/InvoiceType.java b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/InvoiceType.java index 5a6dcce..0de3051 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/InvoiceType.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/InvoiceType.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import site.billingwise.batch.server_batch.domain.contract.Contract; @@ -10,7 +11,7 @@ @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class InvoiceType extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/PaymentStatus.java b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/PaymentStatus.java index c18adda..92fbb5c 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/invoice/PaymentStatus.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/invoice/PaymentStatus.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import java.util.ArrayList; @@ -9,7 +10,7 @@ @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class PaymentStatus extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/item/Item.java b/src/main/java/site/billingwise/batch/server_batch/domain/item/Item.java index 855ce00..18a7fff 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/item/Item.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/item/Item.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import site.billingwise.batch.server_batch.domain.contract.Contract; import site.billingwise.batch.server_batch.domain.user.Client; @@ -11,7 +12,7 @@ @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Item extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/member/ConsentAccount.java b/src/main/java/site/billingwise/batch/server_batch/domain/member/ConsentAccount.java index 8932076..f3684f5 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/member/ConsentAccount.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/member/ConsentAccount.java @@ -2,11 +2,12 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class ConsentAccount extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/member/Member.java b/src/main/java/site/billingwise/batch/server_batch/domain/member/Member.java index edbc22a..7f078a1 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/member/Member.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/member/Member.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import site.billingwise.batch.server_batch.domain.contract.Contract; import site.billingwise.batch.server_batch.domain.user.Client; @@ -11,7 +12,7 @@ @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) @Table(uniqueConstraints = { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/payment/Payment.java b/src/main/java/site/billingwise/batch/server_batch/domain/payment/Payment.java index 3f2eeae..8db30b6 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/payment/Payment.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/payment/Payment.java @@ -2,12 +2,13 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import site.billingwise.batch.server_batch.domain.invoice.Invoice; @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Payment extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentAccount.java b/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentAccount.java index 2a554f7..1e7f7df 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentAccount.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentAccount.java @@ -2,11 +2,12 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class PaymentAccount extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentCard.java b/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentCard.java index d223317..81242fa 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentCard.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/payment/PaymentCard.java @@ -2,11 +2,12 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class PaymentCard extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/user/Client.java b/src/main/java/site/billingwise/batch/server_batch/domain/user/Client.java index 2a5419c..e00da95 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/user/Client.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/user/Client.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; import site.billingwise.batch.server_batch.domain.item.Item; import site.billingwise.batch.server_batch.domain.member.Member; @@ -11,7 +12,7 @@ @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class Client extends BaseEntity { diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/user/User.java b/src/main/java/site/billingwise/batch/server_batch/domain/user/User.java index 2819295..befc307 100644 --- a/src/main/java/site/billingwise/batch/server_batch/domain/user/User.java +++ b/src/main/java/site/billingwise/batch/server_batch/domain/user/User.java @@ -2,11 +2,12 @@ import jakarta.persistence.*; import lombok.*; +import lombok.experimental.SuperBuilder; import site.billingwise.batch.server_batch.domain.common.BaseEntity; @Entity @Getter -@Builder +@SuperBuilder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor(access = AccessLevel.PRIVATE) public class User extends BaseEntity { From 1a66f5842bb09348a73c9dfb69a5341ec7a2cd53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:41:34 +0900 Subject: [PATCH 10/25] =?UTF-8?q?[K5P-49]=20[test,=20fix]=20JdbcGenerateIn?= =?UTF-8?q?voiceWriter=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java b/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java index 2054435..0538bc0 100644 --- a/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java +++ b/src/test/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriterTest.java @@ -16,6 +16,7 @@ import site.billingwise.batch.server_batch.domain.invoice.InvoiceType; import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus; +import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; @@ -64,6 +65,9 @@ void testWrite() throws Exception { .itemAmount(2) .paymentType(paymentType) .invoiceType(autoInvoiceType) + .isDeleted(false) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) .build(); List contracts = Arrays.asList(contract); From ebc47f1850bf28e67a4b22c7c061e2d7e74ad4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:20:27 +0900 Subject: [PATCH 11/25] =?UTF-8?q?[K5P-53]=20[feat]=20=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B2=AD=EA=B5=AC=EC=84=9C=20=EB=82=A9=EB=B6=80?= =?UTF-8?q?=EC=84=9C=20=EC=83=9D=EC=84=B1(=EB=B0=9C=EC=86=A1)=20=EB=B0=B0?= =?UTF-8?q?=EC=B9=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../common/GenerateInvoiceScheduler.java | 97 +++++++++------ .../config/InvoiceProcessingJobConfig.java | 74 ++++++++++++ .../rowmapper/InvoiceRowMapper.java | 53 +++++++++ .../InvoiceSendingAndPaymentManageWriter.java | 98 +++++++++++++++ .../listner/JobCompletionCheckListener.java | 10 +- .../batch/service/EmailService.java | 112 ++++++++++++++++++ .../batch/util/StatusConstants.java | 23 ++++ src/main/resources/application-dev.yml | 3 +- ...oiceSendingAndPaymentManageWriterTest.java | 106 +++++++++++++++++ 10 files changed, 534 insertions(+), 44 deletions(-) create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/service/EmailService.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/util/StatusConstants.java create mode 100644 src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriterTest.java diff --git a/build.gradle b/build.gradle index e8dde09..2a9ae4a 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,8 @@ dependencies { runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.junit.jupiter:junit-jupiter-engine' + testImplementation 'org.mockito:mockito-junit-jupiter' testImplementation 'org.springframework.batch:spring-batch-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java index 329a072..c7803b9 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java @@ -1,8 +1,7 @@ package site.billingwise.batch.server_batch.batch.common; import lombok.RequiredArgsConstructor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; @@ -15,61 +14,83 @@ import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; - @Configuration @EnableScheduling @RequiredArgsConstructor +@Slf4j public class GenerateInvoiceScheduler { - private static final Logger logger = LoggerFactory.getLogger(GenerateInvoiceScheduler.class); private final JobLauncher jobLauncher; private final Job generateInvoiceJob; private final Job jdbcGenerateInvoiceJob; + private final Job invoiceProcessingJob; - // 일단 0,30초마다 실행하도록 실행 - @Scheduled(cron = "0,30 * * * * ?") - public void generateInvoice() { - JobParameters jobParameters = new JobParametersBuilder() - .addLong("invoice", System.currentTimeMillis()) - .toJobParameters(); - try { - logger.info("JPA 배치 프로그램 실행 시작"); - jobLauncher.run(generateInvoiceJob, jobParameters); - logger.info("JPA 배치 프로그램 실행 완료"); - } catch (JobExecutionAlreadyRunningException e) { - logger.error("JobExecutionAlreadyRunningException 발생: ", e); - } catch (JobRestartException e) { - logger.error("JobRestartException 발생: ", e); - } catch (JobInstanceAlreadyCompleteException e) { - logger.error("JobInstanceAlreadyCompleteException 발생: ", e); - } catch (JobParametersInvalidException e) { - logger.error("JobParametersInvalidException 발생: ", e); - } catch (Exception e) { - logger.error("예기치 않은 오류 발생: ", e); - } - } - +// // 0, 30초마다 실행 +// @Scheduled(cron = "0,30 * * * * ?") +// public void generateInvoice() { +// JobParameters jobParameters = new JobParametersBuilder() +// .addLong("invoice", System.currentTimeMillis()) +// .toJobParameters(); +// try { +// log.info("JPA 배치 프로그램 실행 시작"); +// jobLauncher.run(generateInvoiceJob, jobParameters); +// log.info("JPA 배치 프로그램 실행 완료"); +// } catch (JobExecutionAlreadyRunningException e) { +// log.error("JobExecutionAlreadyRunningException 발생: ", e); +// } catch (JobRestartException e) { +// log.error("JobRestartException 발생: ", e); +// } catch (JobInstanceAlreadyCompleteException e) { +// log.error("JobInstanceAlreadyCompleteException 발생: ", e); +// } catch (JobParametersInvalidException e) { +// log.error("JobParametersInvalidException 발생: ", e); +// } catch (Exception e) { +// log.error("예기치 않은 오류 발생: ", e); +// } +// } +// +// // 15, 45초마다 실행 +// @Scheduled(cron = "15,45 * * * * ?") +// public void jdbcGenerateInvoice() { +// JobParameters jobParameters = new JobParametersBuilder() +// .addLong("jdbcInvoice", System.currentTimeMillis()) +// .toJobParameters(); +// try { +// log.info("JDBC 배치 프로그램 실행 시작"); +// jobLauncher.run(jdbcGenerateInvoiceJob, jobParameters); +// log.info("JDBC 배치 프로그램 실행 완료"); +// } catch (JobExecutionAlreadyRunningException e) { +// log.error("JobExecutionAlreadyRunningException 발생: ", e); +// } catch (JobRestartException e) { +// log.error("JobRestartException 발생: ", e); +// } catch (JobInstanceAlreadyCompleteException e) { +// log.error("JobInstanceAlreadyCompleteException 발생: ", e); +// } catch (JobParametersInvalidException e) { +// log.error("JobParametersInvalidException 발생: ", e); +// } catch (Exception e) { +// log.error("예기치 않은 오류 발생: ", e); +// } +// } - // 일단 15,45초마다 실행하도록 실행 + // 15, 45초마다 실행 @Scheduled(cron = "15,45 * * * * ?") - public void jdbcgenerateInvoice() { + public void runInvoiceProcessingJob() { JobParameters jobParameters = new JobParametersBuilder() - .addLong("jdbcInvoice", System.currentTimeMillis()) + .addLong("InvoiceProcessingJob", System.currentTimeMillis()) .toJobParameters(); try { - logger.info("JDBC 배치 프로그램 실행 시작"); - jobLauncher.run(jdbcGenerateInvoiceJob, jobParameters); - logger.info("JDBC 배치 프로그램 실행 완료"); + log.info("Invoice Processing Job 실행 시작"); + jobLauncher.run(invoiceProcessingJob, jobParameters); + log.info("Invoice Processing Job 실행 완료"); } catch (JobExecutionAlreadyRunningException e) { - logger.error("JobExecutionAlreadyRunningException 발생: ", e); + log.error("JobExecutionAlreadyRunningException 발생: ", e); } catch (JobRestartException e) { - logger.error("JobRestartException 발생: ", e); + log.error("JobRestartException 발생: ", e); } catch (JobInstanceAlreadyCompleteException e) { - logger.error("JobInstanceAlreadyCompleteException 발생: ", e); + log.error("JobInstanceAlreadyCompleteException 발생: ", e); } catch (JobParametersInvalidException e) { - logger.error("JobParametersInvalidException 발생: ", e); + log.error("JobParametersInvalidException 발생: ", e); } catch (Exception e) { - logger.error("예기치 않은 오류 발생: ", e); + log.error("예기치 않은 오류 발생: ", e); } } } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java new file mode 100644 index 0000000..410da03 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java @@ -0,0 +1,74 @@ +package site.billingwise.batch.server_batch.batch.invoiceprocessing.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.PlatformTransactionManager; +import site.billingwise.batch.server_batch.batch.invoiceprocessing.rowmapper.InvoiceRowMapper; +import site.billingwise.batch.server_batch.batch.invoiceprocessing.writer.InvoiceSendingAndPaymentManageWriter; +import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener; +import site.billingwise.batch.server_batch.batch.service.EmailService; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; + +import javax.sql.DataSource; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class InvoiceProcessingJobConfig { + + private final int CHUNK_SIZE = 100; + private final DataSource dataSource; + private final JdbcTemplate jdbcTemplate; + private final JobCompletionCheckListener jobCompletionCheckListener; + private final EmailService emailService; + + // 결제 기한 체크 로직 step 아직 개발 x + @Bean + public Job invoiceProcessingJob(JobRepository jobRepository, Step invoiceSendingAndPaymentManageStep) { + return new JobBuilder("InvoiceProcessingJob", jobRepository) + .listener(jobCompletionCheckListener) + .start(invoiceSendingAndPaymentManageStep) +// .next(invoiceDueDateUpdateStep) + .build(); + } + + @Bean + public Step invoiceSendingAndPaymentManageStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) { + return new StepBuilder("InvoiceSendingAndPaymentManageStep", jobRepository) + .chunk(CHUNK_SIZE, transactionManager) + .reader(invoiceSendingAndPaymentManageReader()) + .writer(invoiceSendingAndPaymentManageWriter()) + .build(); + } + + private ItemReader invoiceSendingAndPaymentManageReader() { + + return new JdbcCursorItemReaderBuilder() + .name("invoiceSendingAndPaymentManageReader") + .fetchSize(CHUNK_SIZE) + .sql("select i.*, c.member_id, m.email, m.name, ca.number, ca.bank, ca.owner, i.is_deleted " + + "from invoice i " + + "join contract c ON i.contract_id = c.contract_id " + + "join member m ON c.member_id = m.member_id " + + "left join consent_account ca ON m.member_id = ca.member_id " + + "where i.contract_date >= curdate() AND i.contract_date < curdate() + interval 1 day") + .rowMapper(new InvoiceRowMapper()) + .dataSource(dataSource) + .build(); + } + + private ItemWriter invoiceSendingAndPaymentManageWriter() { + return new InvoiceSendingAndPaymentManageWriter(jdbcTemplate, emailService); + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java new file mode 100644 index 0000000..d42709a --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java @@ -0,0 +1,53 @@ +package site.billingwise.batch.server_batch.batch.invoiceprocessing.rowmapper; + +import org.springframework.jdbc.core.RowMapper; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.contract.PaymentType; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.member.ConsentAccount; +import site.billingwise.batch.server_batch.domain.member.Member; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class InvoiceRowMapper implements RowMapper { + + @Override + public Invoice mapRow(ResultSet rs, int rowNum) throws SQLException { + + + ConsentAccount consentAccount = ConsentAccount.builder() + .number(rs.getString("number")) + .bank(rs.getString("bank")) + .owner(rs.getString("owner")) + .build(); + + + Member member = Member.builder() + .id(rs.getLong("member_id")) + .email(rs.getString("email")) + .name(rs.getString("name")) + .consentAccount(consentAccount) + .build(); + + + Contract contract = Contract.builder() + .member(member) + .build(); + + PaymentType paymentType = PaymentType.builder() + .id(rs.getLong("payment_type_id")) + .build(); + + + return Invoice.builder() + .id(rs.getLong("invoice_id")) + .contract(contract) + .chargeAmount(rs.getLong("charge_amount")) + .contractDate(rs.getTimestamp("contract_date").toLocalDateTime()) + .dueDate(rs.getTimestamp("due_date").toLocalDateTime()) + .paymentType(paymentType) + .isDeleted(rs.getBoolean("is_deleted")) + .build(); + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java new file mode 100644 index 0000000..ac94726 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java @@ -0,0 +1,98 @@ +package site.billingwise.batch.server_batch.batch.invoiceprocessing.writer; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.item.Chunk; +import org.springframework.batch.item.ItemWriter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import site.billingwise.batch.server_batch.batch.service.EmailService; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.member.ConsentAccount; + +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.*; + +@Component +@RequiredArgsConstructor +@Slf4j +public class InvoiceSendingAndPaymentManageWriter implements ItemWriter { + + private final JdbcTemplate jdbcTemplate; + private final EmailService emailService; + + @Override + public void write(Chunk chunk) { + + for(Invoice invoice : chunk) { + + if(invoice.getIsDeleted()) { + continue; + } + // 자동 이체 + if(invoice.getPaymentType().getId() == INVOICE_TYPE_AUTOMATIC_BILLING) { + + ConsentAccount consentAccount = invoice.getContract().getMember().getConsentAccount(); + boolean paymentAttempt = false; + + if (consentAccount != null) { + // 결제 시도( 아직 개발되지 않았음 ) + paymentAttempt = processAutoPayment(invoice); + } + + // 결제 성공 시 + if (paymentAttempt) { + + updatePaymentStatus(invoice.getId(), PAYMENT_STATUS_COMPLETED); + insertPaymentRecord(invoice, consentAccount); + emailService.sendPaymentSuccessMailCode(invoice.getContract().getMember().getEmail(), invoice, consentAccount); + + // 결제 실패 시 + } else { + + emailService.sendPaymentFailMailCode(invoice.getContract().getMember().getEmail(), invoice, consentAccount); + updateFailPaymentStatus(invoice.getId()); + } + + // 납부자 결제 + } else if(invoice.getPaymentType().getId() == INVOICE_TYPE_MANUAL_BILLING){ + + emailService.sendInvoiceMail(invoice.getContract().getMember().getEmail(), invoice); + updatePaymentStatus(invoice.getId(), PAYMENT_STATUS_PENDING); + } + } + } + + + + private void updateFailPaymentStatus(Long invoiceId) { + String sql = "update invoice set updated_at = now() where invoice_id = ?"; + jdbcTemplate.update(sql, invoiceId); + } + + + private void insertPaymentRecord(Invoice invoice, ConsentAccount consentAccount) { + String sql = "insert into payment (invoice_id, payment_method, pay_amount, created_at, updated_at, is_deleted) values (?, ?, ?, NOW(), NOW(), false)"; + jdbcTemplate.update(sql, invoice.getId(), "계좌이체", invoice.getChargeAmount()); + // 납부 계좌 테이블에 데이터 넣기 ( 결재 성공의 경우 ) + insertPaymentAccount(invoice.getId(), consentAccount); + } + + + private void insertPaymentAccount(Long invoiceId, ConsentAccount consentAccount) { + String sql = "insert into payment_account (invoice_id, number, bank, owner, created_at, updated_at, is_deleted) values (?, ?, ?, ?, now(), now(), false)"; + jdbcTemplate.update(sql, invoiceId, consentAccount.getNumber(), consentAccount.getBank(), consentAccount.getOwner()); + } + + + private void updatePaymentStatus(Long invoiceId, long statusId) { + String sql = "update invoice set payment_status_id = ?, updated_at = now() where invoice_id = ?"; + jdbcTemplate.update(sql, statusId, invoiceId); + } + + //결제 시도 + private boolean processAutoPayment(Invoice invoice) { + // 결제 시도 로직을 들어갈 곳 + + return true; + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/listner/JobCompletionCheckListener.java b/src/main/java/site/billingwise/batch/server_batch/batch/listner/JobCompletionCheckListener.java index 58d3691..479ec66 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/listner/JobCompletionCheckListener.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/listner/JobCompletionCheckListener.java @@ -1,38 +1,38 @@ package site.billingwise.batch.server_batch.batch.listner; +import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.listener.JobExecutionListenerSupport; import org.springframework.stereotype.Component; import java.time.Duration; import java.time.LocalDateTime; -import java.util.logging.Logger; @Component +@Slf4j public class JobCompletionCheckListener extends JobExecutionListenerSupport { - private static final Logger log = Logger.getLogger(JobCompletionCheckListener.class.getName()); private LocalDateTime startTime; private LocalDateTime endTime; @Override public void beforeJob(JobExecution jobExecution) { startTime = LocalDateTime.now(); - log.info("Job started at: " + startTime); + log.info("Job started at: {}", startTime); } @Override public void afterJob(JobExecution jobExecution) { endTime = LocalDateTime.now(); - log.info("Job ended at: " + endTime); + log.info("Job ended at: {}", endTime); Duration duration = Duration.between(startTime, endTime); long seconds = duration.getSeconds(); long minutes = seconds / 60; seconds = seconds % 60; - log.info("Job duration: " + minutes + " minutes and " + seconds + " seconds"); + log.info("Job duration: {} minutes and {} seconds", minutes, seconds); super.afterJob(jobExecution); } } \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/service/EmailService.java b/src/main/java/site/billingwise/batch/server_batch/batch/service/EmailService.java new file mode 100644 index 0000000..4c18530 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/service/EmailService.java @@ -0,0 +1,112 @@ +package site.billingwise.batch.server_batch.batch.service; + +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.member.ConsentAccount; + +@Service +@RequiredArgsConstructor +@Slf4j +public class EmailService { + + private final JavaMailSender mailSender; + + @Value("${spring.mail.username}") + private String fromMail; + + + // 결제 실패 + public void sendPaymentFailMailCode(String email, Invoice invoice, ConsentAccount consentAccount) { + try { + MimeMessage message = createFailMail(email, invoice, consentAccount); + mailSender.send(message); + log.info("결제 실패 이메일이 성공적으로 전송되었습니다. 수신자: {}", email); + } catch (MessagingException e) { + log.error("결제 실패 이메일 전송 중 오류가 발생했습니다. 수신자: {}", email, e); + } + } + + // 결제 성공 + public void sendPaymentSuccessMailCode(String email, Invoice invoice, ConsentAccount consentAccount) { + try { + MimeMessage message = createSuccessMail(email, invoice, consentAccount); + mailSender.send(message); + log.info("결제 성공 이메일이 성공적으로 전송되었습니다. 수신자: {}", email); + } catch (MessagingException e) { + log.error("결제 성공 이메일 전송 중 오류가 발생했습니다. 수신자: {}", email, e); + } + } + + // 청구서 발송 + public void sendInvoiceMail(String email, Invoice invoice) { + try { + MimeMessage message = createInvoiceMail(email, invoice); + mailSender.send(message); + log.info("청구서 이메일이 성공적으로 전송되었습니다. 수신자: {}", email); + } catch (MessagingException e) { + log.error("청구서 이메일 전송 중 오류가 발생했습니다. 수신자: {}", email, e); + } + } + + private MimeMessage createSuccessMail(String email, Invoice invoice, ConsentAccount consentAccount) throws MessagingException { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + + helper.setFrom(fromMail); + helper.setTo(email); + helper.setSubject("[빌링와이즈] 결제 성공"); + String body = "

안녕하세요, 빌링와이즈 입니다.

" + + "

결제가 성공적으로 처리되었습니다.

" + + "

청구 금액: " + invoice.getChargeAmount() + "

" + + "

은행: " + consentAccount.getBank() + "

" + + "

예금주: " + consentAccount.getOwner() + "

" + + "

계좌번호: " + consentAccount.getNumber() + "

" + + "

고객 문의 번호: " + "010-xxxx-xxxx " + "

"; + helper.setText(body, true); + + return message; + } + + private MimeMessage createFailMail(String email, Invoice invoice, ConsentAccount consentAccount) throws MessagingException { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + + helper.setFrom(fromMail); + helper.setTo(email); + helper.setSubject("[빌링와이즈] 결제 실패"); + String body = "

안녕하세요, 빌링와이즈 입니다.

" + + "

결제가 실패하였습니다.

" + + "

청구 금액: " + invoice.getChargeAmount() + "

" + + "

은행: " + consentAccount.getBank() + "

" + + "

예금주: " + consentAccount.getOwner() + "

" + + "

계좌번호: " + consentAccount.getNumber() + "

" + + "

고객 문의 번호: " + "010-xxxx-xxxx " + "

"; + helper.setText(body, true); + + return message; + } + + private MimeMessage createInvoiceMail(String email, Invoice invoice ) throws MessagingException { + MimeMessage message = mailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + + helper.setFrom(fromMail); + helper.setTo(email); + helper.setSubject("[빌링와이즈] 청구서 발송"); + String body = "

안녕하세요, 빌링와이즈 입니다.

" + + "

청구서가 발송되었습니다.

" + + "

청구 금액: " + invoice.getChargeAmount() + "

" + + "

고객 문의 번호: " + "010-xxxx-xxxx " + "

"; + // url 만들 예정 + helper.setText(body, true); + + return message; + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/util/StatusConstants.java b/src/main/java/site/billingwise/batch/server_batch/batch/util/StatusConstants.java new file mode 100644 index 0000000..0af8183 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/util/StatusConstants.java @@ -0,0 +1,23 @@ +package site.billingwise.batch.server_batch.batch.util; + +public class StatusConstants { + + // 계약 상태 + public static final long CONTRACT_STATUS_PENDING = 1L; // 대기 + public static final long CONTRACT_STATUS_IN_PROGRESS = 2L; // 진행 + public static final long CONTRACT_STATUS_TERMINATED = 3L; // 종료 + + // 납부 상태 + public static final long PAYMENT_STATUS_UNPAID = 1L; // 미납 + public static final long PAYMENT_STATUS_COMPLETED = 2L; // 완납 + public static final long PAYMENT_STATUS_PENDING = 3L; // 대기 + + // 청구 타입 + public static final long INVOICE_TYPE_AUTOMATIC_BILLING = 1L; // 자동청구 + public static final long INVOICE_TYPE_MANUAL_BILLING = 2L; // 수동청구 + + // 결제 수단 + public static final long PAYMENT_TYPE_PAYER_PAYMENT = 1L; // 납부자 결제 + public static final long PAYMENT_TYPE_AUTOMATIC_TRANSFER = 2L; // 자동 이체 + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index d1f8410..9b8419e 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -31,9 +31,10 @@ spring: properties: mail: smtp: + auth: true starttls: enable: true - auth: true + --- spring: batch: diff --git a/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriterTest.java b/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriterTest.java new file mode 100644 index 0000000..889c40e --- /dev/null +++ b/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriterTest.java @@ -0,0 +1,106 @@ +package site.billingwise.batch.server_batch.batch.invoiceprocessing.writer; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.jdbc.core.JdbcTemplate; +import site.billingwise.batch.server_batch.batch.service.EmailService; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.contract.PaymentType; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.member.ConsentAccount; +import site.billingwise.batch.server_batch.domain.member.Member; + +import java.lang.reflect.Method; + +import static org.mockito.Mockito.verify; +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.INVOICE_TYPE_AUTOMATIC_BILLING; +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.PAYMENT_STATUS_COMPLETED; + +@ExtendWith(MockitoExtension.class) +public class InvoiceSendingAndPaymentManageWriterTest { + + @Mock + private JdbcTemplate jdbcTemplate; + + @Mock + private EmailService emailService; + + @InjectMocks + private InvoiceSendingAndPaymentManageWriter writer; + + private Invoice invoice; + + @BeforeEach + public void setUp() { + Member member = Member.builder() + .email("test@naver.com") + .build(); + + Contract contract = Contract.builder() + .member(member) + .build(); + + PaymentType paymentType = PaymentType.builder() + .id(INVOICE_TYPE_AUTOMATIC_BILLING) + .build(); + + invoice = Invoice.builder() + .isDeleted(false) + .contract(contract) + .paymentType(paymentType) + .build(); + } + + @Test + @DisplayName("결제 실패 시 수정 시각 업데이트") + public void testUpdateFailPaymentStatus() throws Exception { + Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("updateFailPaymentStatus", Long.class); + method.setAccessible(true); + method.invoke(writer, 1L); + verify(jdbcTemplate).update("update invoice set updated_at = now() where invoice_id = ?", 1L); + } + + @Test + @DisplayName("결제 성공 시 납부 테이블 데이터 입력") + public void testInsertPaymentRecord() throws Exception { + ConsentAccount consentAccount = ConsentAccount.builder() + .number("12345") + .bank("bank") + .owner("변현진") + .build(); + + Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("insertPaymentRecord", Invoice.class, ConsentAccount.class); + method.setAccessible(true); + method.invoke(writer, invoice, consentAccount); + verify(jdbcTemplate).update("insert into payment (invoice_id, payment_method, pay_amount, created_at, updated_at, is_deleted) values (?, ?, ?, NOW(), NOW(), false)", invoice.getId(), "계좌이체", invoice.getChargeAmount()); + } + + @Test + @DisplayName("결제 성공 납부 계좌 테이블 데이터 입력") + public void testInsertPaymentAccount() throws Exception { + ConsentAccount consentAccount = ConsentAccount.builder() + .number("12345") + .bank("bank") + .owner("변현진") + .build(); + + Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("insertPaymentAccount", Long.class, ConsentAccount.class); + method.setAccessible(true); + method.invoke(writer, 1L, consentAccount); + verify(jdbcTemplate).update("insert into payment_account (invoice_id, number, bank, owner, created_at, updated_at, is_deleted) values (?, ?, ?, ?, now(), now(), false)", 1L, "12345", "bank", "변현진"); + } + + @Test + @DisplayName("납부 상태 변경") + public void testUpdatePaymentStatus() throws Exception { + Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("updatePaymentStatus", Long.class, long.class); + method.setAccessible(true); + method.invoke(writer, 1L, PAYMENT_STATUS_COMPLETED); + verify(jdbcTemplate).update("update invoice set payment_status_id = ?, updated_at = now() where invoice_id = ?", PAYMENT_STATUS_COMPLETED, 1L); + } +} \ No newline at end of file From d2beb9e502beafb8305c0f9265fc1faca3d2dbec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Mon, 15 Jul 2024 18:10:25 +0900 Subject: [PATCH 12/25] =?UTF-8?q?[K5P-54]=20[feat]=20=EA=B2=B0=EC=A0=9C?= =?UTF-8?q?=EA=B8=B0=ED=95=9C=20=EC=A7=80=EB=82=9C=20=EB=82=A9=EB=B6=80?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=B0=EC=B9=98?= =?UTF-8?q?=EB=A1=9C=EC=A7=81(=EB=82=A9=EB=B6=80=EC=9E=90=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/InvoiceProcessingJobConfig.java | 19 ++++++- .../CustomUpdateOverdueInvoicesTasklet.java | 31 ++++++++++ ...ustomUpdateOverdueInvoicesTaskletTest.java | 56 +++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/tasklet/CustomUpdateOverdueInvoicesTasklet.java create mode 100644 src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/tasklet/CustomUpdateOverdueInvoicesTaskletTest.java diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java index 410da03..b04204e 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java @@ -7,6 +7,7 @@ import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder; @@ -15,6 +16,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.PlatformTransactionManager; import site.billingwise.batch.server_batch.batch.invoiceprocessing.rowmapper.InvoiceRowMapper; +import site.billingwise.batch.server_batch.batch.invoiceprocessing.tasklet.CustomUpdateOverdueInvoicesTasklet; import site.billingwise.batch.server_batch.batch.invoiceprocessing.writer.InvoiceSendingAndPaymentManageWriter; import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener; import site.billingwise.batch.server_batch.batch.service.EmailService; @@ -35,11 +37,11 @@ public class InvoiceProcessingJobConfig { // 결제 기한 체크 로직 step 아직 개발 x @Bean - public Job invoiceProcessingJob(JobRepository jobRepository, Step invoiceSendingAndPaymentManageStep) { + public Job invoiceProcessingJob(JobRepository jobRepository, Step invoiceSendingAndPaymentManageStep, Step invoiceDueDateUpdateStep) { return new JobBuilder("InvoiceProcessingJob", jobRepository) .listener(jobCompletionCheckListener) .start(invoiceSendingAndPaymentManageStep) -// .next(invoiceDueDateUpdateStep) + .next(invoiceDueDateUpdateStep) .build(); } @@ -52,6 +54,19 @@ public Step invoiceSendingAndPaymentManageStep(JobRepository jobRepository, Plat .build(); } + @Bean + public Step invoiceDueDateUpdateStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) { + return new StepBuilder("invoiceDueDateUpdateStep", jobRepository) + .tasklet(updateOverdueInvoicesTasklet(), transactionManager) + .build(); + } + + @Bean + public Tasklet updateOverdueInvoicesTasklet() { + return new CustomUpdateOverdueInvoicesTasklet(jdbcTemplate); + } + + private ItemReader invoiceSendingAndPaymentManageReader() { return new JdbcCursorItemReaderBuilder() diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/tasklet/CustomUpdateOverdueInvoicesTasklet.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/tasklet/CustomUpdateOverdueInvoicesTasklet.java new file mode 100644 index 0000000..121e6e7 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/tasklet/CustomUpdateOverdueInvoicesTasklet.java @@ -0,0 +1,31 @@ +package site.billingwise.batch.server_batch.batch.invoiceprocessing.tasklet; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.StepContribution; +import org.springframework.batch.core.scope.context.ChunkContext; +import org.springframework.batch.core.step.tasklet.Tasklet; +import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.PAYMENT_STATUS_PENDING; +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.PAYMENT_STATUS_UNPAID; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CustomUpdateOverdueInvoicesTasklet implements Tasklet { + + private final JdbcTemplate jdbcTemplate; + + @Override + public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { + String sql = "update invoice set payment_status_id = ? where due_date < curdate() and payment_status_id = ? and is_deleted = false"; + int updateRows = jdbcTemplate.update(sql, PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PENDING); + + log.info("납부자 결제 대기에서 미납으로 전환된 청구 데이터 수: {}", updateRows); + + return RepeatStatus.FINISHED; + } +} diff --git a/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/tasklet/CustomUpdateOverdueInvoicesTaskletTest.java b/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/tasklet/CustomUpdateOverdueInvoicesTaskletTest.java new file mode 100644 index 0000000..4141ee7 --- /dev/null +++ b/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/tasklet/CustomUpdateOverdueInvoicesTaskletTest.java @@ -0,0 +1,56 @@ +package site.billingwise.batch.server_batch.batch.invoiceprocessing.tasklet; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.batch.core.StepContribution; +import org.springframework.batch.core.scope.context.ChunkContext; +import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.jdbc.core.JdbcTemplate; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class CustomUpdateOverdueInvoicesTaskletTest { + + @Mock + private JdbcTemplate jdbcTemplate; + + @InjectMocks + private CustomUpdateOverdueInvoicesTasklet tasklet; + + @Mock + private StepContribution stepContribution; + + @Mock + private ChunkContext chunkContext; + + private static final long PAYMENT_STATUS_UNPAID = 1L; + private static final long PAYMENT_STATUS_PENDING = 3L; + + @BeforeEach + public void setUp() { + tasklet = new CustomUpdateOverdueInvoicesTasklet(jdbcTemplate); + } + + @Test + @DisplayName("납부자 결제 미납 전환") + public void testExecute() throws Exception { + + when(jdbcTemplate.update("update invoice set payment_status_id = ? where due_date < curdate() and payment_status_id = ? and is_deleted = false", PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PENDING)) + .thenReturn(5); + + RepeatStatus status = tasklet.execute(stepContribution, chunkContext); + + verify(jdbcTemplate).update("update invoice set payment_status_id = ? where due_date < curdate() and payment_status_id = ? and is_deleted = false", PAYMENT_STATUS_UNPAID, PAYMENT_STATUS_PENDING); + + assertEquals(RepeatStatus.FINISHED, status); + + } +} \ No newline at end of file From 01626378bfd9c39cb424ca34e3eaa0fa248166ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:35:24 +0900 Subject: [PATCH 13/25] =?UTF-8?q?[K5P-49]=20[refactor,=20feat]=20=EC=B2=AD?= =?UTF-8?q?=EA=B5=AC=20=EC=83=9D=EC=84=B1=20=EC=A1=B0=EA=B1=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=A1=B0?= =?UTF-8?q?=EC=9D=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...teInvoiceScheduler.java => Scheduler.java} | 58 +++++++++---------- .../jdbc/JdbcGenerateInvoiceJobConfig.java | 7 +-- .../jdbc/JdbcGenerateInvoiceWriter.java | 36 +++++++----- .../rowmapper/JdbcContractRowMapper.java | 50 +++++++++++----- 4 files changed, 87 insertions(+), 64 deletions(-) rename src/main/java/site/billingwise/batch/server_batch/batch/common/{GenerateInvoiceScheduler.java => Scheduler.java} (85%) diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java similarity index 85% rename from src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java rename to src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java index c7803b9..0a5ffcb 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/common/GenerateInvoiceScheduler.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java @@ -18,7 +18,7 @@ @EnableScheduling @RequiredArgsConstructor @Slf4j -public class GenerateInvoiceScheduler { +public class Scheduler { private final JobLauncher jobLauncher; private final Job generateInvoiceJob; @@ -48,39 +48,16 @@ public class GenerateInvoiceScheduler { // } // } // -// // 15, 45초마다 실행 -// @Scheduled(cron = "15,45 * * * * ?") -// public void jdbcGenerateInvoice() { -// JobParameters jobParameters = new JobParametersBuilder() -// .addLong("jdbcInvoice", System.currentTimeMillis()) -// .toJobParameters(); -// try { -// log.info("JDBC 배치 프로그램 실행 시작"); -// jobLauncher.run(jdbcGenerateInvoiceJob, jobParameters); -// log.info("JDBC 배치 프로그램 실행 완료"); -// } catch (JobExecutionAlreadyRunningException e) { -// log.error("JobExecutionAlreadyRunningException 발생: ", e); -// } catch (JobRestartException e) { -// log.error("JobRestartException 발생: ", e); -// } catch (JobInstanceAlreadyCompleteException e) { -// log.error("JobInstanceAlreadyCompleteException 발생: ", e); -// } catch (JobParametersInvalidException e) { -// log.error("JobParametersInvalidException 발생: ", e); -// } catch (Exception e) { -// log.error("예기치 않은 오류 발생: ", e); -// } -// } - // 15, 45초마다 실행 @Scheduled(cron = "15,45 * * * * ?") - public void runInvoiceProcessingJob() { + public void jdbcGenerateInvoice() { JobParameters jobParameters = new JobParametersBuilder() - .addLong("InvoiceProcessingJob", System.currentTimeMillis()) + .addLong("jdbcInvoice", System.currentTimeMillis()) .toJobParameters(); try { - log.info("Invoice Processing Job 실행 시작"); - jobLauncher.run(invoiceProcessingJob, jobParameters); - log.info("Invoice Processing Job 실행 완료"); + log.info("JDBC 배치 프로그램 실행 시작"); + jobLauncher.run(jdbcGenerateInvoiceJob, jobParameters); + log.info("JDBC 배치 프로그램 실행 완료"); } catch (JobExecutionAlreadyRunningException e) { log.error("JobExecutionAlreadyRunningException 발생: ", e); } catch (JobRestartException e) { @@ -93,5 +70,28 @@ public void runInvoiceProcessingJob() { log.error("예기치 않은 오류 발생: ", e); } } + +// // 15, 45초마다 실행 +// @Scheduled(cron = "15,45 * * * * ?") +// public void runInvoiceProcessingJob() { +// JobParameters jobParameters = new JobParametersBuilder() +// .addLong("InvoiceProcessingJob", System.currentTimeMillis()) +// .toJobParameters(); +// try { +// log.info("Invoice Processing Job 실행 시작"); +// jobLauncher.run(invoiceProcessingJob, jobParameters); +// log.info("Invoice Processing Job 실행 완료"); +// } catch (JobExecutionAlreadyRunningException e) { +// log.error("JobExecutionAlreadyRunningException 발생: ", e); +// } catch (JobRestartException e) { +// log.error("JobRestartException 발생: ", e); +// } catch (JobInstanceAlreadyCompleteException e) { +// log.error("JobInstanceAlreadyCompleteException 발생: ", e); +// } catch (JobParametersInvalidException e) { +// log.error("JobParametersInvalidException 발생: ", e); +// } catch (Exception e) { +// log.error("예기치 않은 오류 발생: ", e); +// } +// } } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java index 9ab1731..bd303b0 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceJobConfig.java @@ -52,12 +52,9 @@ public ItemReader jdbcContractItemReader() { return new JdbcCursorItemReaderBuilder() .name("jdbcContractItemReader") .fetchSize(CHUNK_SIZE) - .sql("select c.*, pt.payment_type_id, pt.name as payment_type_name, it.invoice_type_id, it.name as invoice_type_name, c.is_deleted " + + .sql("select c.*, c.is_deleted " + "from contract c " + - "join contract_status cs ON c.contract_status_id = cs.contract_status_id " + - "join payment_type pt ON c.payment_type_id = pt.payment_type_id " + - "join invoice_type it ON c.invoice_type_id = it.invoice_type_id " + - "where cs.name = '진행'") + "where c.contract_status_id = 2") .rowMapper(new JdbcContractRowMapper()) .dataSource(dataSource) .build(); diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java index 834b693..3cf3ff6 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/jdbc/JdbcGenerateInvoiceWriter.java @@ -17,13 +17,16 @@ import java.util.ArrayList; import java.util.List; +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.INVOICE_TYPE_MANUAL_BILLING; +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.PAYMENT_TYPE_PAYER_PAYMENT; + @Component @RequiredArgsConstructor public class JdbcGenerateInvoiceWriter implements ItemWriter { private final JdbcTemplate jdbcTemplate; - private static final String PAYER_PAYMENT = "납부자 결제"; + @Override public void write(Chunk chunk) throws Exception { @@ -33,17 +36,23 @@ public void write(Chunk chunk) throws Exception { int nextMonthValue = nextMonth.getMonthValue(); int yearValue = nextMonth.getYear(); - PaymentStatus unpaidPaymentStatus = findUnpaidPaymentStatus(); + PaymentStatus pendingPaymentStatus = findPendingPaymentStatus(); List invoices = new ArrayList<>(); + for(Contract contract : chunk) { + // 수동 청구면 pass(애초에 계약이 수동 청구인 경우) + if(INVOICE_TYPE_MANUAL_BILLING == contract.getInvoiceType().getId()) { + continue; + } if(contract.getIsDeleted()){ continue; } - // 수동 청구가 이미 만들어지면, 청구 생성 X + + // 청구가 이미 만들어져 있으면, pass( 원래는 자동 청구인데, 단발성으로 청구를 생성한 경우 ) if(!invoiceExists(contract, nextMonthValue, yearValue)){ // 약정일 LocalDateTime setInvoiceDate = LocalDateTime.of(yearValue, nextMonthValue, contract.getContractCycle(), 0, 0); @@ -54,18 +63,17 @@ public void write(Chunk chunk) throws Exception { .contract(contract) .invoiceType(contract.getInvoiceType()) .paymentType(contract.getPaymentType()) - .paymentStatus(unpaidPaymentStatus) + .paymentStatus(pendingPaymentStatus) .chargeAmount(contract.getItemPrice() * contract.getItemAmount()) .contractDate(setInvoiceDate) .dueDate(payDueDate) - .isDeleted(false) // isDeleted 필드 값을 설정 - .createdAt(now) // createdAt 필드 값을 설정 - .updatedAt(now) // updatedAt 필드 값을 설정 + .isDeleted(false) + .createdAt(now) + .updatedAt(now) .build(); invoices.add(invoice); - } } @@ -99,25 +107,23 @@ public int getBatchSize() { }); } - private PaymentStatus findUnpaidPaymentStatus() { - String sql = "select payment_status_id, name from payment_status where name = '미납'"; + private PaymentStatus findPendingPaymentStatus() { + String sql = "select payment_status_id, name from payment_status where name = '대기'"; return jdbcTemplate.queryForObject(sql, (ResultSet rs, int rowNum) -> PaymentStatus.builder() .id(rs.getLong("payment_status_id")) - .name(rs.getString("name")) - .build() - ); + .build()); } private LocalDateTime calculateDueDate(Contract contract, LocalDateTime setInvoiceDate) { - if (PAYER_PAYMENT.equals(contract.getPaymentType().getName())) { + if (PAYMENT_TYPE_PAYER_PAYMENT == contract.getPaymentType().getId()) { return setInvoiceDate.plusDays(3); } return setInvoiceDate; } private boolean invoiceExists(Contract contract, int month, int year) { - LocalDateTime startDate = LocalDateTime.of(year, month, 1, 0, 0); + LocalDateTime startDate = LocalDateTime.of(year, month, 1, 0, 0,0); LocalDateTime endDate = startDate.plusMonths(1).minusSeconds(1); String sql = "select count(*) from invoice where contract_id = ? " + diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java index 5d903fd..af61cb1 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/generateinvoice/rowmapper/JdbcContractRowMapper.java @@ -2,8 +2,11 @@ import org.springframework.jdbc.core.RowMapper; import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.contract.ContractStatus; import site.billingwise.batch.server_batch.domain.contract.PaymentType; import site.billingwise.batch.server_batch.domain.invoice.InvoiceType; +import site.billingwise.batch.server_batch.domain.item.Item; +import site.billingwise.batch.server_batch.domain.member.Member; import java.sql.ResultSet; import java.sql.SQLException; @@ -11,27 +14,44 @@ public class JdbcContractRowMapper implements RowMapper { @Override public Contract mapRow(ResultSet rs, int rowNum) throws SQLException { + + Member member = Member.builder() + .id(rs.getLong("member_id")) + .build(); + + + Item item = Item.builder() + .id(rs.getLong("item_id")) + .build(); + + + InvoiceType invoiceType = InvoiceType.builder() + .id(rs.getLong("invoice_type_id")) + .build(); + + + PaymentType paymentType = PaymentType.builder() + .id(rs.getLong("payment_type_id")) + .build(); + + ContractStatus contractStatus = ContractStatus.builder() + .id(rs.getLong("contract_status_id")) + .build(); + + return Contract.builder() .id(rs.getLong("contract_id")) + .member(member) + .item(item) + .invoiceType(invoiceType) + .paymentType(paymentType) + .contractStatus(contractStatus) + .isSubscription(rs.getBoolean("is_subscription")) .itemPrice(rs.getLong("item_price")) .itemAmount(rs.getInt("item_amount")) .contractCycle(rs.getInt("contract_cycle")) .paymentDueCycle(rs.getInt("payment_due_cycle")) - .isDeleted(rs.getBoolean("is_deleted")) - .paymentType( - PaymentType.builder() - .id(rs.getLong("payment_type_id")) - .name(rs.getString("payment_type_name")) - .build() - ) - .invoiceType( - InvoiceType.builder() - .id(rs.getLong("invoice_type_id")) - .name(rs.getString("invoice_type_name")) - .build() - ) - .createdAt(rs.getTimestamp("created_at").toLocalDateTime()) - .updatedAt(rs.getTimestamp("updated_at").toLocalDateTime()) + .isEasyConsent(rs.getBoolean("is_easy_consent")) .isDeleted(rs.getBoolean("is_deleted")) .build(); } From 13cf362a43e4ac5456d3f97833ab5f5052815066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Tue, 16 Jul 2024 20:38:57 +0900 Subject: [PATCH 14/25] =?UTF-8?q?[K5P-53]=20[feat]=20=EB=8B=A8=EA=B1=B4=20?= =?UTF-8?q?=EA=B3=84=EC=95=BD(=EA=B5=AC=EB=8F=85=20=EC=83=81=ED=83=9C=20fa?= =?UTF-8?q?lse)=20=EA=B3=84=EC=95=BD=20=EC=83=81=ED=83=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server_batch/batch/common/Scheduler.java | 56 +++++++++---------- .../config/InvoiceProcessingJobConfig.java | 2 +- .../rowmapper/InvoiceRowMapper.java | 8 +++ .../InvoiceSendingAndPaymentManageWriter.java | 37 +++++++++--- 4 files changed, 66 insertions(+), 37 deletions(-) diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java index 0a5ffcb..d6b27b6 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java @@ -48,39 +48,16 @@ public class Scheduler { // } // } // - // 15, 45초마다 실행 - @Scheduled(cron = "15,45 * * * * ?") - public void jdbcGenerateInvoice() { - JobParameters jobParameters = new JobParametersBuilder() - .addLong("jdbcInvoice", System.currentTimeMillis()) - .toJobParameters(); - try { - log.info("JDBC 배치 프로그램 실행 시작"); - jobLauncher.run(jdbcGenerateInvoiceJob, jobParameters); - log.info("JDBC 배치 프로그램 실행 완료"); - } catch (JobExecutionAlreadyRunningException e) { - log.error("JobExecutionAlreadyRunningException 발생: ", e); - } catch (JobRestartException e) { - log.error("JobRestartException 발생: ", e); - } catch (JobInstanceAlreadyCompleteException e) { - log.error("JobInstanceAlreadyCompleteException 발생: ", e); - } catch (JobParametersInvalidException e) { - log.error("JobParametersInvalidException 발생: ", e); - } catch (Exception e) { - log.error("예기치 않은 오류 발생: ", e); - } - } - // // 15, 45초마다 실행 // @Scheduled(cron = "15,45 * * * * ?") -// public void runInvoiceProcessingJob() { +// public void jdbcGenerateInvoice() { // JobParameters jobParameters = new JobParametersBuilder() -// .addLong("InvoiceProcessingJob", System.currentTimeMillis()) +// .addLong("jdbcInvoice", System.currentTimeMillis()) // .toJobParameters(); // try { -// log.info("Invoice Processing Job 실행 시작"); -// jobLauncher.run(invoiceProcessingJob, jobParameters); -// log.info("Invoice Processing Job 실행 완료"); +// log.info("JDBC 배치 프로그램 실행 시작"); +// jobLauncher.run(jdbcGenerateInvoiceJob, jobParameters); +// log.info("JDBC 배치 프로그램 실행 완료"); // } catch (JobExecutionAlreadyRunningException e) { // log.error("JobExecutionAlreadyRunningException 발생: ", e); // } catch (JobRestartException e) { @@ -93,5 +70,28 @@ public void jdbcGenerateInvoice() { // log.error("예기치 않은 오류 발생: ", e); // } // } + + // 15, 45초마다 실행 + @Scheduled(cron = "15,45 * * * * ?") + public void runInvoiceProcessingJob() { + JobParameters jobParameters = new JobParametersBuilder() + .addLong("InvoiceProcessingJob", System.currentTimeMillis()) + .toJobParameters(); + try { + log.info("Invoice Processing Job 실행 시작"); + jobLauncher.run(invoiceProcessingJob, jobParameters); + log.info("Invoice Processing Job 실행 완료"); + } catch (JobExecutionAlreadyRunningException e) { + log.error("JobExecutionAlreadyRunningException 발생: ", e); + } catch (JobRestartException e) { + log.error("JobRestartException 발생: ", e); + } catch (JobInstanceAlreadyCompleteException e) { + log.error("JobInstanceAlreadyCompleteException 발생: ", e); + } catch (JobParametersInvalidException e) { + log.error("JobParametersInvalidException 발생: ", e); + } catch (Exception e) { + log.error("예기치 않은 오류 발생: ", e); + } + } } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java index b04204e..81b5b6f 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java @@ -72,7 +72,7 @@ private ItemReader invoiceSendingAndPaymentManageReader() { return new JdbcCursorItemReaderBuilder() .name("invoiceSendingAndPaymentManageReader") .fetchSize(CHUNK_SIZE) - .sql("select i.*, c.member_id, m.email, m.name, ca.number, ca.bank, ca.owner, i.is_deleted " + + .sql("select i.*, c.member_id, m.email, m.name, m.phone, ca.number, ca.bank, ca.owner, i.is_deleted, c.is_subscription " + "from invoice i " + "join contract c ON i.contract_id = c.contract_id " + "join member m ON c.member_id = m.member_id " + diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java index d42709a..698ca0d 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java @@ -4,6 +4,7 @@ import site.billingwise.batch.server_batch.domain.contract.Contract; import site.billingwise.batch.server_batch.domain.contract.PaymentType; import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.invoice.InvoiceType; import site.billingwise.batch.server_batch.domain.member.ConsentAccount; import site.billingwise.batch.server_batch.domain.member.Member; @@ -15,6 +16,9 @@ public class InvoiceRowMapper implements RowMapper { @Override public Invoice mapRow(ResultSet rs, int rowNum) throws SQLException { + InvoiceType invoiceType = InvoiceType.builder() + .id(rs.getLong("invoice_type_id")) + .build(); ConsentAccount consentAccount = ConsentAccount.builder() .number(rs.getString("number")) @@ -27,12 +31,16 @@ public Invoice mapRow(ResultSet rs, int rowNum) throws SQLException { .id(rs.getLong("member_id")) .email(rs.getString("email")) .name(rs.getString("name")) + .phone(rs.getString("phone")) .consentAccount(consentAccount) .build(); Contract contract = Contract.builder() + .id(rs.getLong("contract_id")) .member(member) + .invoiceType(invoiceType) + .isSubscription(rs.getBoolean("is_subscription")) .build(); PaymentType paymentType = PaymentType.builder() diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java index ac94726..2d1edca 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java @@ -7,6 +7,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import site.billingwise.batch.server_batch.batch.service.EmailService; +import site.billingwise.batch.server_batch.domain.contract.Contract; import site.billingwise.batch.server_batch.domain.invoice.Invoice; import site.billingwise.batch.server_batch.domain.member.ConsentAccount; @@ -28,8 +29,12 @@ public void write(Chunk chunk) { if(invoice.getIsDeleted()) { continue; } + + Contract contract = invoice.getContract(); + boolean checkSubscription = contract.getIsSubscription(); + long contract_id = contract.getId(); // 자동 이체 - if(invoice.getPaymentType().getId() == INVOICE_TYPE_AUTOMATIC_BILLING) { + if(invoice.getPaymentType().getId() == PAYMENT_TYPE_AUTOMATIC_TRANSFER) { ConsentAccount consentAccount = invoice.getContract().getMember().getConsentAccount(); boolean paymentAttempt = false; @@ -45,26 +50,42 @@ public void write(Chunk chunk) { updatePaymentStatus(invoice.getId(), PAYMENT_STATUS_COMPLETED); insertPaymentRecord(invoice, consentAccount); emailService.sendPaymentSuccessMailCode(invoice.getContract().getMember().getEmail(), invoice, consentAccount); + // 단건일 경우( 계약 종료로 변경 ) + if(!checkSubscription) { + updateNotSubscriptionContractStatus(contract_id, CONTRACT_STATUS_TERMINATED); + } - // 결제 실패 시 + // 결제 실패 시 } else { emailService.sendPaymentFailMailCode(invoice.getContract().getMember().getEmail(), invoice, consentAccount); updateFailPaymentStatus(invoice.getId()); + // 단건일 경우( 계약 종료로 변경 ) + if(!checkSubscription) { + updateNotSubscriptionContractStatus(contract_id, CONTRACT_STATUS_TERMINATED); + } } - // 납부자 결제 - } else if(invoice.getPaymentType().getId() == INVOICE_TYPE_MANUAL_BILLING){ + // 납부자 결제 + } else if(invoice.getPaymentType().getId() == PAYMENT_TYPE_PAYER_PAYMENT){ emailService.sendInvoiceMail(invoice.getContract().getMember().getEmail(), invoice); updatePaymentStatus(invoice.getId(), PAYMENT_STATUS_PENDING); + // 단건일 경우( 계약 종료로 변경 ) + if(!checkSubscription) { + updateNotSubscriptionContractStatus(contract_id, CONTRACT_STATUS_TERMINATED); + } } } } + private void updateNotSubscriptionContractStatus(long contract_id, long contractStatusTerminated){ + String sql = "update contract set contract_status_id = ? where contract_id = ?"; + jdbcTemplate.update(sql, contractStatusTerminated, contract_id); + } - private void updateFailPaymentStatus(Long invoiceId) { + private void updateFailPaymentStatus(long invoiceId) { String sql = "update invoice set updated_at = now() where invoice_id = ?"; jdbcTemplate.update(sql, invoiceId); } @@ -72,19 +93,19 @@ private void updateFailPaymentStatus(Long invoiceId) { private void insertPaymentRecord(Invoice invoice, ConsentAccount consentAccount) { String sql = "insert into payment (invoice_id, payment_method, pay_amount, created_at, updated_at, is_deleted) values (?, ?, ?, NOW(), NOW(), false)"; - jdbcTemplate.update(sql, invoice.getId(), "계좌이체", invoice.getChargeAmount()); + jdbcTemplate.update(sql, invoice.getId(), "ACCOUNT", invoice.getChargeAmount()); // 납부 계좌 테이블에 데이터 넣기 ( 결재 성공의 경우 ) insertPaymentAccount(invoice.getId(), consentAccount); } - private void insertPaymentAccount(Long invoiceId, ConsentAccount consentAccount) { + private void insertPaymentAccount(long invoiceId, ConsentAccount consentAccount) { String sql = "insert into payment_account (invoice_id, number, bank, owner, created_at, updated_at, is_deleted) values (?, ?, ?, ?, now(), now(), false)"; jdbcTemplate.update(sql, invoiceId, consentAccount.getNumber(), consentAccount.getBank(), consentAccount.getOwner()); } - private void updatePaymentStatus(Long invoiceId, long statusId) { + private void updatePaymentStatus(long invoiceId, long statusId) { String sql = "update invoice set payment_status_id = ?, updated_at = now() where invoice_id = ?"; jdbcTemplate.update(sql, statusId, invoiceId); } From c43a2db88228b90839d4af7255d1bb55de1dcf0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Wed, 17 Jul 2024 08:50:22 +0900 Subject: [PATCH 15/25] =?UTF-8?q?[K5P-53]=20[feat]=20=EB=AC=B8=EC=9E=90=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../config/InvoiceProcessingJobConfig.java | 4 +- .../InvoiceSendingAndPaymentManageWriter.java | 8 ++ .../batch/service/SmsService.java | 116 ++++++++++++++++++ src/main/resources/application-dev.yml | 9 +- 5 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/service/SmsService.java diff --git a/build.gradle b/build.gradle index 2a9ae4a..c19ecf1 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-mail' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'net.nurigo:sdk:4.3.0' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' runtimeOnly 'com.mysql:mysql-connector-j' diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java index 81b5b6f..8cda881 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java @@ -20,6 +20,7 @@ import site.billingwise.batch.server_batch.batch.invoiceprocessing.writer.InvoiceSendingAndPaymentManageWriter; import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener; import site.billingwise.batch.server_batch.batch.service.EmailService; +import site.billingwise.batch.server_batch.batch.service.SmsService; import site.billingwise.batch.server_batch.domain.invoice.Invoice; import javax.sql.DataSource; @@ -34,6 +35,7 @@ public class InvoiceProcessingJobConfig { private final JdbcTemplate jdbcTemplate; private final JobCompletionCheckListener jobCompletionCheckListener; private final EmailService emailService; + private final SmsService smsService; // 결제 기한 체크 로직 step 아직 개발 x @Bean @@ -84,6 +86,6 @@ private ItemReader invoiceSendingAndPaymentManageReader() { } private ItemWriter invoiceSendingAndPaymentManageWriter() { - return new InvoiceSendingAndPaymentManageWriter(jdbcTemplate, emailService); + return new InvoiceSendingAndPaymentManageWriter(jdbcTemplate, emailService, smsService); } } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java index 2d1edca..4e75652 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java @@ -7,9 +7,11 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import site.billingwise.batch.server_batch.batch.service.EmailService; +import site.billingwise.batch.server_batch.batch.service.SmsService; import site.billingwise.batch.server_batch.domain.contract.Contract; import site.billingwise.batch.server_batch.domain.invoice.Invoice; import site.billingwise.batch.server_batch.domain.member.ConsentAccount; +import site.billingwise.batch.server_batch.domain.member.Member; import static site.billingwise.batch.server_batch.batch.util.StatusConstants.*; @@ -20,6 +22,7 @@ public class InvoiceSendingAndPaymentManageWriter implements ItemWriter private final JdbcTemplate jdbcTemplate; private final EmailService emailService; + private final SmsService smsService; @Override public void write(Chunk chunk) { @@ -33,6 +36,7 @@ public void write(Chunk chunk) { Contract contract = invoice.getContract(); boolean checkSubscription = contract.getIsSubscription(); long contract_id = contract.getId(); + Member member = contract.getMember(); // 자동 이체 if(invoice.getPaymentType().getId() == PAYMENT_TYPE_AUTOMATIC_TRANSFER) { @@ -50,6 +54,7 @@ public void write(Chunk chunk) { updatePaymentStatus(invoice.getId(), PAYMENT_STATUS_COMPLETED); insertPaymentRecord(invoice, consentAccount); emailService.sendPaymentSuccessMailCode(invoice.getContract().getMember().getEmail(), invoice, consentAccount); + smsService.sendSuccessBilling(member.getPhone(), member.getConsentAccount().getOwner(), member.getConsentAccount().getBank(), invoice.getChargeAmount().intValue()); // 단건일 경우( 계약 종료로 변경 ) if(!checkSubscription) { updateNotSubscriptionContractStatus(contract_id, CONTRACT_STATUS_TERMINATED); @@ -60,6 +65,7 @@ public void write(Chunk chunk) { emailService.sendPaymentFailMailCode(invoice.getContract().getMember().getEmail(), invoice, consentAccount); updateFailPaymentStatus(invoice.getId()); + smsService.sendFailBilling(member.getPhone(), member.getConsentAccount().getOwner(), member.getConsentAccount().getBank(), invoice.getChargeAmount().intValue()); // 단건일 경우( 계약 종료로 변경 ) if(!checkSubscription) { updateNotSubscriptionContractStatus(contract_id, CONTRACT_STATUS_TERMINATED); @@ -71,6 +77,8 @@ public void write(Chunk chunk) { emailService.sendInvoiceMail(invoice.getContract().getMember().getEmail(), invoice); updatePaymentStatus(invoice.getId(), PAYMENT_STATUS_PENDING); + smsService.sendInvoice(member.getPhone(), member.getConsentAccount().getOwner(), member.getConsentAccount().getBank(), invoice.getChargeAmount().intValue()); + // 단건일 경우( 계약 종료로 변경 ) if(!checkSubscription) { updateNotSubscriptionContractStatus(contract_id, CONTRACT_STATUS_TERMINATED); diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/service/SmsService.java b/src/main/java/site/billingwise/batch/server_batch/batch/service/SmsService.java new file mode 100644 index 0000000..40a4dea --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/service/SmsService.java @@ -0,0 +1,116 @@ +package site.billingwise.batch.server_batch.batch.service; + +import jakarta.annotation.PostConstruct; +import net.nurigo.sdk.message.model.Message; +import lombok.RequiredArgsConstructor; +import net.nurigo.sdk.NurigoApp; +import net.nurigo.sdk.message.request.SingleMessageSendingRequest; +import net.nurigo.sdk.message.response.SingleMessageSentResponse; +import net.nurigo.sdk.message.service.DefaultMessageService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +public class SmsService { + + private DefaultMessageService messageService; + + @Value("${coolsms.api.key}") + private String apiKey; + + @Value("${coolsms.api.secret}") + private String apiSecretKey; + + @Value("${coolsms.api.url}") + private String url; + + @PostConstruct + private void init(){ + this.messageService = NurigoApp.INSTANCE.initialize(apiKey, apiSecretKey, url); + } + + public SingleMessageSentResponse sendSuccessBilling(String to, String owner, String bank, Integer itemPrice) { + LocalDateTime now = LocalDateTime.now(); + + int year = now.getYear(); + int month = now.getMonthValue(); + int day = now.getDayOfMonth(); + + Message message = new Message(); + + message.setFrom("010-6642-2113"); + message.setTo(to); + + StringBuilder text = new StringBuilder(); + text.append(" 안녕하세요. [빌링와이즈] 입니다.\n"); + text.append(" " + year +"년 " + month + "월 " + day + "일 " + "고객님의 정기 결제가 성공적으로 이뤄졌습니다.\n"); + text.append(" 은행 : " + bank + "\n" ); + text.append(" 예금주 : " + owner + "\n" ); + text.append(" 총 금액 : " + itemPrice ); + + message.setText(text.toString()); + + + SingleMessageSentResponse response = this.messageService.sendOne(new SingleMessageSendingRequest(message)); + return response; + } + + public SingleMessageSentResponse sendFailBilling(String to, String owner, String bank, Integer itemPrice) { + LocalDateTime now = LocalDateTime.now(); + + int year = now.getYear(); + int month = now.getMonthValue(); + int day = now.getDayOfMonth(); + + Message message = new Message(); + + message.setFrom("010-6642-2113"); + message.setTo(to); + + StringBuilder text = new StringBuilder(); + text.append(" 안녕하세요. [빌링와이즈] 입니다.\n"); + text.append(" " + year +"년 " + month + "월 " + day + "일 " + "고객님의 정기 결제가 이뤄지지 않았습니다.\n"); + text.append(" 은행 : " + bank + "\n" ); + text.append(" 예금주 : " + owner + "\n" ); + text.append(" 총 금액 : " + itemPrice ); + + message.setText(text.toString()); + + + SingleMessageSentResponse response = this.messageService.sendOne(new SingleMessageSendingRequest(message)); + return response; + } + + public SingleMessageSentResponse sendInvoice(String to, String owner, String bank, Integer itemPrice) { + LocalDateTime now = LocalDateTime.now(); + + int year = now.getYear(); + int month = now.getMonthValue(); + int day = now.getDayOfMonth(); + + Message message = new Message(); + + message.setFrom("010-6642-2113"); + message.setTo(to); + + StringBuilder text = new StringBuilder(); + text.append(" 안녕하세요. [빌링와이즈] 입니다.\n"); + text.append(" " + year +"년 " + month + "월 " + day + "일 " + "고객님의 청구서 보내드립니다.\n"); + text.append("\n" ); + text.append("납부 금액\n"); + text.append(" 금액 : " + itemPrice + "\n"); + text.append("납부 링크\n"); + text.append("(link)"); + + + message.setText(text.toString()); + + + SingleMessageSentResponse response = this.messageService.sendOne(new SingleMessageSendingRequest(message)); + return response; + } + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 9b8419e..3cac18e 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -39,4 +39,11 @@ spring: spring: batch: job: - enabled: false \ No newline at end of file + enabled: false + +--- +coolsms: + api: + key: "NCSLLWQSPGK4Z8KX" + secret: "O0F2MXPETVI0AIT0ZTPEG0Z1KWSLJV5P" + url: https://api.coolsms.co.kr \ No newline at end of file From b3db7d6d1de63f1eb2b90ff78f7de61af5636992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Thu, 18 Jul 2024 14:07:50 +0900 Subject: [PATCH 16/25] =?UTF-8?q?[K5P-61]=20[feat]=20=EC=A3=BC=EA=B0=84=20?= =?UTF-8?q?=EC=B2=AD=EA=B5=AC=20=EA=B8=88=EC=95=A1=20=EB=B0=8F=20=EC=88=98?= =?UTF-8?q?=EB=82=A9=20=EA=B8=88=EC=95=A1=20=EC=A7=91=EA=B3=84=20=EB=B0=B0?= =?UTF-8?q?=EC=B9=98=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/InvoiceProcessingJobConfig.java | 1 - .../rowmapper/InvoiceRowMapper.java | 2 +- .../config/WeeklyInvoiceStaticsJobConfig.java | 78 ++++++++++++++++ .../rowmapper/StaticsInvoiceRowMapper.java | 53 +++++++++++ .../writer/CustomWeeklyInvoiceWriter.java | 53 +++++++++++ .../listner/InvoiceStatisticsListener.java | 90 +++++++++++++++++++ .../{common => scheduler}/Scheduler.java | 40 +++++++-- .../batch/util/StatusConstants.java | 6 ++ .../domain/statics/InvoiceStatistics.java | 52 +++++++++++ .../domain/statics/InvoiceStatisticsType.java | 32 +++++++ src/main/resources/status.sql | 38 +++++++- 11 files changed, 436 insertions(+), 9 deletions(-) create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/WeeklyInvoiceStaticsJobConfig.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/rowmapper/StaticsInvoiceRowMapper.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriter.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/listner/InvoiceStatisticsListener.java rename src/main/java/site/billingwise/batch/server_batch/batch/{common => scheduler}/Scheduler.java (71%) create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/statics/InvoiceStatistics.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/domain/statics/InvoiceStatisticsType.java diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java index 81b5b6f..63c1f4c 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java @@ -35,7 +35,6 @@ public class InvoiceProcessingJobConfig { private final JobCompletionCheckListener jobCompletionCheckListener; private final EmailService emailService; - // 결제 기한 체크 로직 step 아직 개발 x @Bean public Job invoiceProcessingJob(JobRepository jobRepository, Step invoiceSendingAndPaymentManageStep, Step invoiceDueDateUpdateStep) { return new JobBuilder("InvoiceProcessingJob", jobRepository) diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java index 698ca0d..f5ee0fa 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/rowmapper/InvoiceRowMapper.java @@ -58,4 +58,4 @@ public Invoice mapRow(ResultSet rs, int rowNum) throws SQLException { .isDeleted(rs.getBoolean("is_deleted")) .build(); } -} +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/WeeklyInvoiceStaticsJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/WeeklyInvoiceStaticsJobConfig.java new file mode 100644 index 0000000..6360446 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/WeeklyInvoiceStaticsJobConfig.java @@ -0,0 +1,78 @@ +package site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.ArgumentPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.transaction.PlatformTransactionManager; +import site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.rowmapper.StaticsInvoiceRowMapper; +import site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.writer.CustomWeeklyInvoiceWriter; +import site.billingwise.batch.server_batch.batch.listner.InvoiceStatisticsListener; +import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; + +import javax.sql.DataSource; +import java.time.DayOfWeek; +import java.time.LocalDateTime; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class WeeklyInvoiceStaticsJobConfig { + + private final int CHUNK_SIZE = 100; + private final DataSource dataSource; + private final JobCompletionCheckListener jobCompletionCheckListener; + private final InvoiceStatisticsListener invoiceStatisticsListener; + + + @Bean + public Job weeklyInvoiceStatisticsJob(JobRepository jobRepository, Step weeklyInvoiceStatisticsStep) { + return new JobBuilder("weeklyInvoiceStatisticsJob", jobRepository) + .listener(jobCompletionCheckListener) + .start(weeklyInvoiceStatisticsStep) + .build(); + } + + @Bean + Step weeklyInvoiceStatisticsStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) { + + return new StepBuilder("weeklyInvoiceStatisticsStep", jobRepository) + .chunk(CHUNK_SIZE, transactionManager) + .reader(weeklyInvoiceReader()) + .writer(weeklyInvoiceWriter()) + .listener(invoiceStatisticsListener) + .build(); + + } + + + private ItemWriter weeklyInvoiceWriter() { + return new CustomWeeklyInvoiceWriter(invoiceStatisticsListener); + } + + private ItemReader weeklyInvoiceReader() { + LocalDateTime startDate = LocalDateTime.now().minusWeeks(1).with(DayOfWeek.MONDAY); + LocalDateTime endDate = startDate.plusDays(6); + return new JdbcCursorItemReaderBuilder() + .name("weeklyInvoiceReader") + .dataSource(dataSource) + .fetchSize(CHUNK_SIZE) + .sql("SELECT i.*, c.member_id, m.client_id FROM invoice i " + + "JOIN contract c ON i.contract_id = c.contract_id " + + "JOIN member m ON c.member_id = m.member_id " + + "WHERE i.due_date >= ? AND i.due_date <= ?") .preparedStatementSetter(new ArgumentPreparedStatementSetter(new Object[]{startDate, endDate})) + .rowMapper(new StaticsInvoiceRowMapper()) + .build(); + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/rowmapper/StaticsInvoiceRowMapper.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/rowmapper/StaticsInvoiceRowMapper.java new file mode 100644 index 0000000..381bb77 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/rowmapper/StaticsInvoiceRowMapper.java @@ -0,0 +1,53 @@ +package site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.rowmapper; + +import org.springframework.jdbc.core.RowMapper; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.contract.PaymentType; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus; +import site.billingwise.batch.server_batch.domain.member.Member; +import site.billingwise.batch.server_batch.domain.user.Client; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class StaticsInvoiceRowMapper implements RowMapper { + @Override + public Invoice mapRow(ResultSet rs, int rowNum) throws SQLException { + + Client client = Client.builder() + .id(rs.getLong("client_id")) + .build(); + + Member member = Member.builder() + .id(rs.getLong("member_id")) + .client(client) + .build(); + + Contract contract = Contract.builder() + .id(rs.getLong("contract_id")) + .member(member) + .build(); + + PaymentType paymentType = PaymentType.builder() + .id(rs.getLong("payment_type_id")) + .build(); + + PaymentStatus paymentStatus = PaymentStatus.builder() + .id(rs.getLong("payment_status_id")) + .build(); + + + return Invoice.builder() + .id(rs.getLong("invoice_id")) + .contract(contract) + .chargeAmount(rs.getLong("charge_amount")) + .contractDate(rs.getTimestamp("contract_date").toLocalDateTime()) + .dueDate(rs.getTimestamp("due_date").toLocalDateTime()) + .paymentType(paymentType) + .paymentStatus(paymentStatus) + .isDeleted(rs.getBoolean("is_deleted")) + .build(); + + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriter.java new file mode 100644 index 0000000..6e6d236 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriter.java @@ -0,0 +1,53 @@ +package site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.writer; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.item.Chunk; +import org.springframework.batch.item.ItemWriter; +import org.springframework.stereotype.Component; +import site.billingwise.batch.server_batch.batch.listner.InvoiceStatisticsListener; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; + +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.*; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CustomWeeklyInvoiceWriter implements ItemWriter { + + private final InvoiceStatisticsListener invoiceStatisticsListener; + + @Override + public void write(Chunk chunk) { + for (Invoice invoice : chunk) { + + + if (invoiceStatisticsListener.getClientId() == null) { + invoiceStatisticsListener.setClientId(invoice.getContract().getMember().getClient().getId()); + } + + + log.info("주간 통계 invoice 데이터 ID: {}", invoice.getId()); + + if (invoice.getIsDeleted()) { + log.info("삭제된 invoice 데이터, skipping: {}", invoice.getId()); + continue; + } + if (invoice.getPaymentStatus().getId() == PAYMENT_STATUS_PENDING) { + log.info("아직 결제 대기 중인 invoice 데이터, skipping: {}", invoice.getId()); + continue; + } + + invoiceStatisticsListener.addInvoice(invoice.getChargeAmount()); + log.info("총 청구액에 더할 invoice 데이터 금액: {}", invoice.getChargeAmount()); + + if (invoice.getPaymentStatus().getId() == PAYMENT_STATUS_COMPLETED) { + invoiceStatisticsListener.addCollected(invoice.getChargeAmount()); + log.info("총 수납금액에 더할 invoice 데이터 금액: {}", invoice.getChargeAmount()); + } else if (invoice.getPaymentStatus().getId() == PAYMENT_STATUS_UNPAID) { + invoiceStatisticsListener.addOutstanding(invoice.getChargeAmount()); + log.info("총 미납금액에 더할 invoice 데이터 금액: {}", invoice.getChargeAmount()); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/listner/InvoiceStatisticsListener.java b/src/main/java/site/billingwise/batch/server_batch/batch/listner/InvoiceStatisticsListener.java new file mode 100644 index 0000000..6c94a02 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/listner/InvoiceStatisticsListener.java @@ -0,0 +1,90 @@ +package site.billingwise.batch.server_batch.batch.listner; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.ExitStatus; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.StepExecutionListener; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import java.time.DayOfWeek; +import java.time.LocalDateTime; +import java.time.temporal.WeekFields; +import java.util.Locale; + +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.STATISTICS_TYPE_WEEKLY; + +@Component +@RequiredArgsConstructor +@Slf4j +public class InvoiceStatisticsListener implements StepExecutionListener { + + private final JdbcTemplate jdbcTemplate; + + private long totalInvoicedMoney ; + private long totalCollectedMoney; + private long totalOutstanding; + private Long clientId; + + @Override + public ExitStatus afterStep(StepExecution stepExecution) { + log.info("afterStep 호출"); + LocalDateTime today = LocalDateTime.now(); + LocalDateTime startOfLastWeek = today.minusWeeks(1).with(DayOfWeek.MONDAY); + int weekNumber = startOfLastWeek.get(WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear()); + updateinvoiceStatics(jdbcTemplate,totalInvoicedMoney, totalCollectedMoney, totalOutstanding, startOfLastWeek, weekNumber); + + return ExitStatus.COMPLETED; + } + + private void updateinvoiceStatics(JdbcTemplate jdbcTemplate , long totalInvoicedMoney, long totalCollectedMoney, long totalOutstanding, LocalDateTime startOfLastWeek, int weekNumber) { + // 데이터 베이스에 insert하는 로직 + // 참고날짜, 총 청구액, 총 수금액, 총 미납액, 타입상태(주간 or 월간), 년, 월, 주, + log.info("업데이트 invoice statistics 테이블: totalInvoicedMoney={}, totalCollectedMoney={}, totalOutstanding={}, startOfLastWeek={}, weekNumber={}", + totalInvoicedMoney, totalCollectedMoney, totalOutstanding, startOfLastWeek, weekNumber); + + + String sql = "insert into invoice_statistics (reference_date, total_invoiced, total_collected, outstanding, type_id, year, month, week, client_id, is_deleted, created_at, updated_at ) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, false, now(), now())"; + jdbcTemplate.update(sql, startOfLastWeek, + totalInvoicedMoney, + totalCollectedMoney, + totalOutstanding, + STATISTICS_TYPE_WEEKLY, + startOfLastWeek.getYear(), + startOfLastWeek.getMonthValue(), + weekNumber, + clientId + ); + + } + + @Override + public void beforeStep(StepExecution stepExecution) { + totalInvoicedMoney = 0; + totalCollectedMoney = 0; + totalOutstanding = 0; + clientId = null; + } + + public void addInvoice(long Invoice) { + totalInvoicedMoney += Invoice; + } + + public void addCollected(long Collected) { + totalCollectedMoney += Collected; + } + + public void addOutstanding(long outstanding) { + totalOutstanding += outstanding; + } + + public void setClientId(Long id) { + this.clientId = id; + } + + public Long getClientId() { + return clientId; + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java similarity index 71% rename from src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java rename to src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java index d6b27b6..4f4612e 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/common/Scheduler.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java @@ -1,4 +1,4 @@ -package site.billingwise.batch.server_batch.batch.common; +package site.billingwise.batch.server_batch.batch.scheduler; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -24,6 +24,8 @@ public class Scheduler { private final Job generateInvoiceJob; private final Job jdbcGenerateInvoiceJob; private final Job invoiceProcessingJob; + private final Job weeklyInvoiceStatisticsJob; + // // 0, 30초마다 실행 // @Scheduled(cron = "0,30 * * * * ?") @@ -72,15 +74,39 @@ public class Scheduler { // } // 15, 45초마다 실행 +// @Scheduled(cron = "15,45 * * * * ?") +// public void runInvoiceProcessingJob() { +// JobParameters jobParameters = new JobParametersBuilder() +// .addLong("InvoiceProcessingJob", System.currentTimeMillis()) +// .toJobParameters(); +// try { +// log.info("Invoice Processing Job 실행 시작"); +// jobLauncher.run(invoiceProcessingJob, jobParameters); +// log.info("Invoice Processing Job 실행 완료"); +// } catch (JobExecutionAlreadyRunningException e) { +// log.error("JobExecutionAlreadyRunningException 발생: ", e); +// } catch (JobRestartException e) { +// log.error("JobRestartException 발생: ", e); +// } catch (JobInstanceAlreadyCompleteException e) { +// log.error("JobInstanceAlreadyCompleteException 발생: ", e); +// } catch (JobParametersInvalidException e) { +// log.error("JobParametersInvalidException 발생: ", e); +// } catch (Exception e) { +// log.error("예기치 않은 오류 발생: ", e); +// } +// } + + + // @Scheduled(cron = "0 0 1 * * SUN") // 매주 일요일 새벽 1시에 실행 @Scheduled(cron = "15,45 * * * * ?") - public void runInvoiceProcessingJob() { + public void weeklyJob() { JobParameters jobParameters = new JobParametersBuilder() - .addLong("InvoiceProcessingJob", System.currentTimeMillis()) + .addLong("weeklyInvoiceStatisticsJob", System.currentTimeMillis()) .toJobParameters(); try { - log.info("Invoice Processing Job 실행 시작"); - jobLauncher.run(invoiceProcessingJob, jobParameters); - log.info("Invoice Processing Job 실행 완료"); + log.info("weeklyInvoiceStatisticsJob 실행 시작"); + jobLauncher.run(weeklyInvoiceStatisticsJob, jobParameters); + log.info("weeklyInvoiceStatisticsJob 실행 완료"); } catch (JobExecutionAlreadyRunningException e) { log.error("JobExecutionAlreadyRunningException 발생: ", e); } catch (JobRestartException e) { @@ -95,3 +121,5 @@ public void runInvoiceProcessingJob() { } } + + diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/util/StatusConstants.java b/src/main/java/site/billingwise/batch/server_batch/batch/util/StatusConstants.java index 0af8183..e502f14 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/util/StatusConstants.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/util/StatusConstants.java @@ -20,4 +20,10 @@ public class StatusConstants { public static final long PAYMENT_TYPE_PAYER_PAYMENT = 1L; // 납부자 결제 public static final long PAYMENT_TYPE_AUTOMATIC_TRANSFER = 2L; // 자동 이체 + + // 통계 타입 + public static final long STATISTICS_TYPE_WEEKLY = 1L; // 주간 통계 + public static final long STATISTICS_TYPE_MONTHLY = 2L; // 월간 통계 + + } diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/statics/InvoiceStatistics.java b/src/main/java/site/billingwise/batch/server_batch/domain/statics/InvoiceStatistics.java new file mode 100644 index 0000000..b8d2856 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/statics/InvoiceStatistics.java @@ -0,0 +1,52 @@ +package site.billingwise.batch.server_batch.domain.statics; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; + +import java.time.LocalDateTime; + +@Entity +@Getter +@SuperBuilder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Table(name="invoice_statistics") +public class InvoiceStatistics extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "reference_date", nullable = false) + private LocalDateTime date; + + @Column(name = "total_invoiced", nullable = false) + private Long totalInvoiced; + + @Column(name = "total_collected", nullable = false) + private Long totalCollected; + + @Column(name = "outstanding", nullable = false) + private Long outstanding; + + @Column(nullable = false) + private Integer year; + + @Column(nullable = true) + private Integer month; + + @Column(nullable = true) + private Integer week; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "type_id", nullable = false) + private InvoiceStatisticsType type; + + @Column(name = "client_id", nullable = false) + private Long clientId; +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/domain/statics/InvoiceStatisticsType.java b/src/main/java/site/billingwise/batch/server_batch/domain/statics/InvoiceStatisticsType.java new file mode 100644 index 0000000..4f43bbf --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/domain/statics/InvoiceStatisticsType.java @@ -0,0 +1,32 @@ +package site.billingwise.batch.server_batch.domain.statics; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; +import site.billingwise.batch.server_batch.domain.common.BaseEntity; +import site.billingwise.batch.server_batch.domain.contract.Contract; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@SuperBuilder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Table(name="invoice_statistics_type") +public class InvoiceStatisticsType extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "type_name", nullable = false) + private String typeName; + + @OneToMany(mappedBy = "type", cascade = CascadeType.ALL, orphanRemoval = true) + private List invoiceStatisticsList; +} \ No newline at end of file diff --git a/src/main/resources/status.sql b/src/main/resources/status.sql index b697314..1ceff49 100644 --- a/src/main/resources/status.sql +++ b/src/main/resources/status.sql @@ -27,4 +27,40 @@ INSERT INTO payment_status (payment_status_id, name, is_deleted, created_at, upd VALUES (1, '미납', false, NOW(), NOW()), (2, '완납', false, NOW(), NOW()), - (3, '대기', false, NOW(), NOW()); \ No newline at end of file + (3, '대기', false, NOW(), NOW()); + + + + +-- 통계 구분 값 +INSERT INTO invoice_statistics_type (type_name, is_deleted) +VALUES + ('주간', false), + ('월간', false); + + +-- 통계 테이블 생성 쿼리 +CREATE TABLE invoice_statistics_type ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + type_name VARCHAR(50) NOT NULL, + is_deleted BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +CREATE TABLE invoice_statistics ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + reference_date TIMESTAMP NOT NULL, + total_invoiced BIGINT NOT NULL, + total_collected BIGINT NOT NULL, + outstanding BIGINT NOT NULL, + type_id BIGINT NOT NULL, + year INT NOT NULL, + month INT DEFAULT NULL, + week INT DEFAULT NULL, + client_id BIGINT NOT NULL, + is_deleted BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (type_id) REFERENCES invoice_statistics_type(id) +); From b1dcc9dc42d1638f1c966d6bf42826a327bddf81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Thu, 18 Jul 2024 16:08:18 +0900 Subject: [PATCH 17/25] =?UTF-8?q?[K5P-61]=20[feat]=20=EC=9B=94=EA=B0=84=20?= =?UTF-8?q?=EC=B2=AD=EA=B5=AC=20=EA=B8=88=EC=95=A1=20=EB=B0=8F=20=EC=88=98?= =?UTF-8?q?=EB=82=A9=20=EA=B8=88=EC=95=A1=20=EC=A7=91=EA=B3=84=20=EB=B0=B0?= =?UTF-8?q?=EC=B9=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MonthlyInvoiceStatisticsJobConfig.java | 74 +++++++++++ .../config/WeeklyInvoiceStaticsJobConfig.java | 12 +- .../writer/CustomMonthlyInvoiceWriter.java | 51 +++++++ .../writer/CustomWeeklyInvoiceWriter.java | 4 +- .../InvoiceStatisticsListener.java | 42 +++--- .../MonthlyInvoiceStatisticsListener.java | 20 +++ .../WeeklyInvoiceStatisticsListener.java | 20 +++ .../batch/scheduler/Scheduler.java | 34 ++++- .../CustomMonthlyInvoiceWriterTest.java | 123 +++++++++++++++++ .../writer/CustomWeeklyInvoiceWriterTest.java | 125 ++++++++++++++++++ 10 files changed, 477 insertions(+), 28 deletions(-) create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/MonthlyInvoiceStatisticsJobConfig.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomMonthlyInvoiceWriter.java rename src/main/java/site/billingwise/batch/server_batch/batch/listner/{ => statistic}/InvoiceStatisticsListener.java (68%) create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/MonthlyInvoiceStatisticsListener.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/WeeklyInvoiceStatisticsListener.java create mode 100644 src/test/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomMonthlyInvoiceWriterTest.java create mode 100644 src/test/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriterTest.java diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/MonthlyInvoiceStatisticsJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/MonthlyInvoiceStatisticsJobConfig.java new file mode 100644 index 0000000..089ca2c --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/MonthlyInvoiceStatisticsJobConfig.java @@ -0,0 +1,74 @@ +package site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.core.step.builder.StepBuilder; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.ArgumentPreparedStatementSetter; +import org.springframework.transaction.PlatformTransactionManager; +import site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.rowmapper.StaticsInvoiceRowMapper; +import site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.writer.CustomMonthlyInvoiceWriter; +import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener; +import site.billingwise.batch.server_batch.batch.listner.statistic.MonthlyInvoiceStatisticsListener; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; + +import javax.sql.DataSource; +import java.time.LocalDateTime; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class MonthlyInvoiceStatisticsJobConfig { + + private final int CHUNK_SIZE = 100; + private final DataSource dataSource; + private final JobCompletionCheckListener jobCompletionCheckListener; + private final MonthlyInvoiceStatisticsListener monthlyInvoiceStatisticsListener; + + @Bean + public Job monthlyInvoiceStatisticsJob(JobRepository jobRepository, Step monthlyInvoiceStatisticsStep) { + return new JobBuilder("monthlyInvoiceStatisticsJob", jobRepository) + .listener(jobCompletionCheckListener) + .start(monthlyInvoiceStatisticsStep) + .build(); + } + + @Bean + Step monthlyInvoiceStatisticsStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) { + return new StepBuilder("monthlyInvoiceStatisticsStep", jobRepository) + .chunk(CHUNK_SIZE, transactionManager) + .reader(monthlyInvoiceReader()) + .writer(monthlyInvoiceWriter()) + .listener(monthlyInvoiceStatisticsListener) + .build(); + } + + private ItemWriter monthlyInvoiceWriter() { + return new CustomMonthlyInvoiceWriter(monthlyInvoiceStatisticsListener); + } + + private ItemReader monthlyInvoiceReader() { + LocalDateTime startDate = LocalDateTime.now().minusMonths(1).withDayOfMonth(1); + LocalDateTime endDate = startDate.plusMonths(1).minusDays(1); + return new JdbcCursorItemReaderBuilder() + .name("monthlyInvoiceReader") + .dataSource(dataSource) + .fetchSize(CHUNK_SIZE) + .sql("SELECT i.*, c.member_id, m.client_id FROM invoice i " + + "JOIN contract c ON i.contract_id = c.contract_id " + + "JOIN member m ON c.member_id = m.member_id " + + "WHERE i.due_date >= ? AND i.due_date <= ?") + .preparedStatementSetter(new ArgumentPreparedStatementSetter(new Object[]{startDate, endDate})) + .rowMapper(new StaticsInvoiceRowMapper()) + .build(); + } +} + diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/WeeklyInvoiceStaticsJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/WeeklyInvoiceStaticsJobConfig.java index 6360446..72cc38c 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/WeeklyInvoiceStaticsJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/config/WeeklyInvoiceStaticsJobConfig.java @@ -13,12 +13,11 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.ArgumentPreparedStatementSetter; -import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.PlatformTransactionManager; import site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.rowmapper.StaticsInvoiceRowMapper; import site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.writer.CustomWeeklyInvoiceWriter; -import site.billingwise.batch.server_batch.batch.listner.InvoiceStatisticsListener; import site.billingwise.batch.server_batch.batch.listner.JobCompletionCheckListener; +import site.billingwise.batch.server_batch.batch.listner.statistic.WeeklyInvoiceStatisticsListener; import site.billingwise.batch.server_batch.domain.invoice.Invoice; import javax.sql.DataSource; @@ -33,7 +32,7 @@ public class WeeklyInvoiceStaticsJobConfig { private final int CHUNK_SIZE = 100; private final DataSource dataSource; private final JobCompletionCheckListener jobCompletionCheckListener; - private final InvoiceStatisticsListener invoiceStatisticsListener; + private final WeeklyInvoiceStatisticsListener weeklyInvoiceStatisticsListener; @Bean @@ -51,14 +50,14 @@ Step weeklyInvoiceStatisticsStep(JobRepository jobRepository, PlatformTransactio .chunk(CHUNK_SIZE, transactionManager) .reader(weeklyInvoiceReader()) .writer(weeklyInvoiceWriter()) - .listener(invoiceStatisticsListener) + .listener(weeklyInvoiceStatisticsListener) .build(); } private ItemWriter weeklyInvoiceWriter() { - return new CustomWeeklyInvoiceWriter(invoiceStatisticsListener); + return new CustomWeeklyInvoiceWriter(weeklyInvoiceStatisticsListener); } private ItemReader weeklyInvoiceReader() { @@ -71,7 +70,8 @@ private ItemReader weeklyInvoiceReader() { .sql("SELECT i.*, c.member_id, m.client_id FROM invoice i " + "JOIN contract c ON i.contract_id = c.contract_id " + "JOIN member m ON c.member_id = m.member_id " + - "WHERE i.due_date >= ? AND i.due_date <= ?") .preparedStatementSetter(new ArgumentPreparedStatementSetter(new Object[]{startDate, endDate})) + "WHERE i.due_date >= ? AND i.due_date <= ?") + .preparedStatementSetter(new ArgumentPreparedStatementSetter(new Object[]{startDate, endDate})) .rowMapper(new StaticsInvoiceRowMapper()) .build(); } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomMonthlyInvoiceWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomMonthlyInvoiceWriter.java new file mode 100644 index 0000000..07cab62 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomMonthlyInvoiceWriter.java @@ -0,0 +1,51 @@ +package site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.writer; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.item.Chunk; +import org.springframework.batch.item.ItemWriter; +import org.springframework.stereotype.Component; +import site.billingwise.batch.server_batch.batch.listner.statistic.MonthlyInvoiceStatisticsListener; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; + +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.*; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CustomMonthlyInvoiceWriter implements ItemWriter { + + private final MonthlyInvoiceStatisticsListener invoiceStatisticsListener; + + @Override + public void write(Chunk chunk) { + for (Invoice invoice : chunk) { + + if (invoiceStatisticsListener.getClientId() == null) { + invoiceStatisticsListener.setClientId(invoice.getContract().getMember().getClient().getId()); + } + + log.info("월간 통계 invoice 데이터 ID: {}", invoice.getId()); + + if (invoice.getIsDeleted()) { + log.info("삭제된 invoice 데이터, skipping: {}", invoice.getId()); + continue; + } + if (invoice.getPaymentStatus().getId() == PAYMENT_STATUS_PENDING) { + log.info("아직 결제 대기 중인 invoice 데이터, skipping: {}", invoice.getId()); + continue; + } + + invoiceStatisticsListener.addInvoice(invoice.getChargeAmount()); + log.info("총 청구액에 더할 invoice 데이터 금액: {}", invoice.getChargeAmount()); + + if (invoice.getPaymentStatus().getId() == PAYMENT_STATUS_COMPLETED) { + invoiceStatisticsListener.addCollected(invoice.getChargeAmount()); + log.info("총 수납금액에 더할 invoice 데이터 금액: {}", invoice.getChargeAmount()); + } else if (invoice.getPaymentStatus().getId() == PAYMENT_STATUS_UNPAID) { + invoiceStatisticsListener.addOutstanding(invoice.getChargeAmount()); + log.info("총 미납금액에 더할 invoice 데이터 금액: {}", invoice.getChargeAmount()); + } + } + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriter.java index 6e6d236..9fece2d 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriter.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriter.java @@ -5,7 +5,7 @@ import org.springframework.batch.item.Chunk; import org.springframework.batch.item.ItemWriter; import org.springframework.stereotype.Component; -import site.billingwise.batch.server_batch.batch.listner.InvoiceStatisticsListener; +import site.billingwise.batch.server_batch.batch.listner.statistic.WeeklyInvoiceStatisticsListener; import site.billingwise.batch.server_batch.domain.invoice.Invoice; import static site.billingwise.batch.server_batch.batch.util.StatusConstants.*; @@ -15,7 +15,7 @@ @Slf4j public class CustomWeeklyInvoiceWriter implements ItemWriter { - private final InvoiceStatisticsListener invoiceStatisticsListener; + private final WeeklyInvoiceStatisticsListener invoiceStatisticsListener; @Override public void write(Chunk chunk) { diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/listner/InvoiceStatisticsListener.java b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java similarity index 68% rename from src/main/java/site/billingwise/batch/server_batch/batch/listner/InvoiceStatisticsListener.java rename to src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java index 6c94a02..ba69c74 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/listner/InvoiceStatisticsListener.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java @@ -1,4 +1,4 @@ -package site.billingwise.batch.server_batch.batch.listner; +package site.billingwise.batch.server_batch.batch.listner.statistic; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -6,19 +6,18 @@ import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.StepExecutionListener; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Component; +import site.billingwise.batch.server_batch.batch.util.StatusConstants; import java.time.DayOfWeek; import java.time.LocalDateTime; import java.time.temporal.WeekFields; import java.util.Locale; -import static site.billingwise.batch.server_batch.batch.util.StatusConstants.STATISTICS_TYPE_WEEKLY; - -@Component @RequiredArgsConstructor @Slf4j -public class InvoiceStatisticsListener implements StepExecutionListener { +public abstract class InvoiceStatisticsListener implements StepExecutionListener { + + protected abstract Long getStatisticsType(); private final JdbcTemplate jdbcTemplate; @@ -31,30 +30,40 @@ public class InvoiceStatisticsListener implements StepExecutionListener { public ExitStatus afterStep(StepExecution stepExecution) { log.info("afterStep 호출"); LocalDateTime today = LocalDateTime.now(); - LocalDateTime startOfLastWeek = today.minusWeeks(1).with(DayOfWeek.MONDAY); - int weekNumber = startOfLastWeek.get(WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear()); - updateinvoiceStatics(jdbcTemplate,totalInvoicedMoney, totalCollectedMoney, totalOutstanding, startOfLastWeek, weekNumber); + LocalDateTime startOfPeriod; + int periodNumber; + + // 통계 타입에 따라 시작 날짜와 기간 번호를 설정하기 + if (getStatisticsType() == StatusConstants.STATISTICS_TYPE_MONTHLY) { + startOfPeriod = today.minusMonths(1).withDayOfMonth(1); + periodNumber = startOfPeriod.getMonthValue(); + } else { + startOfPeriod = today.minusWeeks(1).with(DayOfWeek.MONDAY); + periodNumber = startOfPeriod.get(WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear()); + } + + updateinvoiceStatics(jdbcTemplate,totalInvoicedMoney, totalCollectedMoney, totalOutstanding, startOfPeriod, periodNumber); return ExitStatus.COMPLETED; } - private void updateinvoiceStatics(JdbcTemplate jdbcTemplate , long totalInvoicedMoney, long totalCollectedMoney, long totalOutstanding, LocalDateTime startOfLastWeek, int weekNumber) { + private void updateinvoiceStatics(JdbcTemplate jdbcTemplate , long totalInvoicedMoney, long totalCollectedMoney, long totalOutstanding, LocalDateTime startOfPeriod, int periodNumber) { // 데이터 베이스에 insert하는 로직 // 참고날짜, 총 청구액, 총 수금액, 총 미납액, 타입상태(주간 or 월간), 년, 월, 주, log.info("업데이트 invoice statistics 테이블: totalInvoicedMoney={}, totalCollectedMoney={}, totalOutstanding={}, startOfLastWeek={}, weekNumber={}", - totalInvoicedMoney, totalCollectedMoney, totalOutstanding, startOfLastWeek, weekNumber); + totalInvoicedMoney, totalCollectedMoney, totalOutstanding, startOfPeriod, periodNumber); String sql = "insert into invoice_statistics (reference_date, total_invoiced, total_collected, outstanding, type_id, year, month, week, client_id, is_deleted, created_at, updated_at ) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, false, now(), now())"; - jdbcTemplate.update(sql, startOfLastWeek, + jdbcTemplate.update(sql, startOfPeriod, totalInvoicedMoney, totalCollectedMoney, totalOutstanding, - STATISTICS_TYPE_WEEKLY, - startOfLastWeek.getYear(), - startOfLastWeek.getMonthValue(), - weekNumber, + getStatisticsType(), + startOfPeriod.getYear(), + startOfPeriod.getMonthValue(), + periodNumber, clientId ); @@ -87,4 +96,5 @@ public void setClientId(Long id) { public Long getClientId() { return clientId; } + } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/MonthlyInvoiceStatisticsListener.java b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/MonthlyInvoiceStatisticsListener.java new file mode 100644 index 0000000..36f11c2 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/MonthlyInvoiceStatisticsListener.java @@ -0,0 +1,20 @@ +package site.billingwise.batch.server_batch.batch.listner.statistic; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import site.billingwise.batch.server_batch.batch.util.StatusConstants; + +@Component +@Slf4j +public class MonthlyInvoiceStatisticsListener extends InvoiceStatisticsListener { + + public MonthlyInvoiceStatisticsListener(JdbcTemplate jdbcTemplate) { + super(jdbcTemplate); + } + + @Override + protected Long getStatisticsType() { + return StatusConstants.STATISTICS_TYPE_MONTHLY; + } +} diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/WeeklyInvoiceStatisticsListener.java b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/WeeklyInvoiceStatisticsListener.java new file mode 100644 index 0000000..46446e4 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/WeeklyInvoiceStatisticsListener.java @@ -0,0 +1,20 @@ +package site.billingwise.batch.server_batch.batch.listner.statistic; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; +import site.billingwise.batch.server_batch.batch.util.StatusConstants; + +@Component +@Slf4j +public class WeeklyInvoiceStatisticsListener extends InvoiceStatisticsListener { + + public WeeklyInvoiceStatisticsListener(JdbcTemplate jdbcTemplate) { + super(jdbcTemplate); + } + + @Override + protected Long getStatisticsType() { + return StatusConstants.STATISTICS_TYPE_WEEKLY; + } +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java index 4f4612e..f31d964 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java @@ -25,8 +25,9 @@ public class Scheduler { private final Job jdbcGenerateInvoiceJob; private final Job invoiceProcessingJob; private final Job weeklyInvoiceStatisticsJob; + private final Job monthlyInvoiceStatisticsJob; - +// // // 0, 30초마다 실행 // @Scheduled(cron = "0,30 * * * * ?") // public void generateInvoice() { @@ -72,8 +73,8 @@ public class Scheduler { // log.error("예기치 않은 오류 발생: ", e); // } // } - - // 15, 45초마다 실행 +// +//// 15, 45초마다 실행monthlyInvoiceStatisticsJob // @Scheduled(cron = "15,45 * * * * ?") // public void runInvoiceProcessingJob() { // JobParameters jobParameters = new JobParametersBuilder() @@ -119,7 +120,32 @@ public void weeklyJob() { log.error("예기치 않은 오류 발생: ", e); } } -} + +// @Scheduled(cron = "0 0 1 1 * ?") // 매월 1일 새벽 1시 + @Scheduled(cron = "0,30 * * * * ?") + public void monthlyJob() { + JobParameters jobParameters = new JobParametersBuilder() + .addLong("monthlyInvoiceStatisticsJob", System.currentTimeMillis()) + .toJobParameters(); + try { + log.info("monthlyInvoiceStatisticsJob 실행 시작"); + jobLauncher.run(monthlyInvoiceStatisticsJob, jobParameters); + log.info("monthlyInvoiceStatisticsJob 실행 완료"); + } catch (JobExecutionAlreadyRunningException e) { + log.error("JobExecutionAlreadyRunningException 발생: ", e); + } catch (JobRestartException e) { + log.error("JobRestartException 발생: ", e); + } catch (JobInstanceAlreadyCompleteException e) { + log.error("JobInstanceAlreadyCompleteException 발생: ", e); + } catch (JobParametersInvalidException e) { + log.error("JobParametersInvalidException 발생: ", e); + } catch (Exception e) { + log.error("예기치 않은 오류 발생: ", e); + } + + } + } + diff --git a/src/test/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomMonthlyInvoiceWriterTest.java b/src/test/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomMonthlyInvoiceWriterTest.java new file mode 100644 index 0000000..1631af8 --- /dev/null +++ b/src/test/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomMonthlyInvoiceWriterTest.java @@ -0,0 +1,123 @@ +package site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.writer; + +import static org.mockito.Mockito.*; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.batch.item.Chunk; + +import site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.writer.CustomMonthlyInvoiceWriter; +import site.billingwise.batch.server_batch.batch.listner.statistic.MonthlyInvoiceStatisticsListener; +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus; +import site.billingwise.batch.server_batch.domain.member.Member; +import site.billingwise.batch.server_batch.domain.user.Client; + +public class CustomMonthlyInvoiceWriterTest { + + @Mock + private MonthlyInvoiceStatisticsListener invoiceStatisticsListener; + + @InjectMocks + private CustomMonthlyInvoiceWriter writer; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + writer = new CustomMonthlyInvoiceWriter(invoiceStatisticsListener); + } + + @Test + public void testWrite() throws Exception { + + Client client = Client.builder().id(1L).build(); + Member member = Member.builder().client(client).build(); + Contract contract = Contract.builder().member(member).build(); + PaymentStatus paymentStatus = PaymentStatus.builder().id(2L).build(); + + Invoice invoice = Invoice.builder() + .id(1L) + .contract(contract) + .chargeAmount(1000L) + .paymentStatus(paymentStatus) + .isDeleted(false) + .build(); + + when(invoiceStatisticsListener.getClientId()).thenReturn(null); + + Chunk chunk = new Chunk<>(List.of(invoice)); + + + writer.write(chunk); + + verify(invoiceStatisticsListener, times(1)).setClientId(client.getId()); + verify(invoiceStatisticsListener, times(1)).addInvoice(invoice.getChargeAmount()); + verify(invoiceStatisticsListener, times(1)).addCollected(invoice.getChargeAmount()); + } + + @Test + public void testWriteWithPendingInvoice() throws Exception { + + Client client = Client.builder().id(1L).build(); + Member member = Member.builder().client(client).build(); + Contract contract = Contract.builder().member(member).build(); + PaymentStatus paymentStatus = PaymentStatus.builder().id(3L).build(); + + Invoice invoice = Invoice.builder() + .id(1L) + .contract(contract) + .chargeAmount(1000L) + .paymentStatus(paymentStatus) + .isDeleted(false) + .build(); + + when(invoiceStatisticsListener.getClientId()).thenReturn(null); + + Chunk chunk = new Chunk<>(List.of(invoice)); + + + writer.write(chunk); + + + verify(invoiceStatisticsListener, times(1)).setClientId(client.getId()); + verify(invoiceStatisticsListener, never()).addInvoice(anyLong()); + verify(invoiceStatisticsListener, never()).addCollected(anyLong()); + verify(invoiceStatisticsListener, never()).addOutstanding(anyLong()); + } + + @Test + public void testWriteWithDeletedInvoice() throws Exception { + // Given + Client client = Client.builder().id(1L).build(); + Member member = Member.builder().client(client).build(); + Contract contract = Contract.builder().member(member).build(); + PaymentStatus paymentStatus = PaymentStatus.builder().id(2L).build(); + + Invoice invoice = Invoice.builder() + .id(1L) + .contract(contract) + .chargeAmount(1000L) + .paymentStatus(paymentStatus) + .isDeleted(true) + .build(); + + when(invoiceStatisticsListener.getClientId()).thenReturn(null); + + Chunk chunk = new Chunk<>(List.of(invoice)); + + // When + writer.write(chunk); + + // Then + verify(invoiceStatisticsListener, times(1)).setClientId(client.getId()); + verify(invoiceStatisticsListener, never()).addInvoice(anyLong()); + verify(invoiceStatisticsListener, never()).addCollected(anyLong()); + verify(invoiceStatisticsListener, never()).addOutstanding(anyLong()); + } +} \ No newline at end of file diff --git a/src/test/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriterTest.java b/src/test/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriterTest.java new file mode 100644 index 0000000..59e8c32 --- /dev/null +++ b/src/test/java/site/billingwise/batch/server_batch/batch/invoicestaticsprocessing/writer/CustomWeeklyInvoiceWriterTest.java @@ -0,0 +1,125 @@ +package site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.writer; + +import static org.mockito.Mockito.*; + +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.batch.item.Chunk; + +import site.billingwise.batch.server_batch.batch.invoicestaticsprocessing.writer.CustomWeeklyInvoiceWriter; +import site.billingwise.batch.server_batch.batch.listner.statistic.WeeklyInvoiceStatisticsListener; + +import site.billingwise.batch.server_batch.domain.contract.Contract; +import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.domain.invoice.PaymentStatus; +import site.billingwise.batch.server_batch.domain.member.Member; +import site.billingwise.batch.server_batch.domain.user.Client; + +public class CustomWeeklyInvoiceWriterTest { + + @Mock + private WeeklyInvoiceStatisticsListener invoiceStatisticsListener; + + @InjectMocks + private CustomWeeklyInvoiceWriter writer; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + writer = new CustomWeeklyInvoiceWriter(invoiceStatisticsListener); + } + + @Test + public void testWrite() throws Exception { + + Client client = Client.builder().id(1L).build(); + Member member = Member.builder().client(client).build(); + Contract contract = Contract.builder().member(member).build(); + PaymentStatus paymentStatus = PaymentStatus.builder().id(2L).build(); + + Invoice invoice = Invoice.builder() + .id(1L) + .contract(contract) + .chargeAmount(1000L) + .paymentStatus(paymentStatus) + .isDeleted(false) + .build(); + + when(invoiceStatisticsListener.getClientId()).thenReturn(null); + + Chunk chunk = new Chunk<>(List.of(invoice)); + + + writer.write(chunk); + + + verify(invoiceStatisticsListener, times(1)).setClientId(client.getId()); + verify(invoiceStatisticsListener, times(1)).addInvoice(invoice.getChargeAmount()); + verify(invoiceStatisticsListener, times(1)).addCollected(invoice.getChargeAmount()); + } + + @Test + public void testWriteWithPendingInvoice() throws Exception { + // Given + Client client = Client.builder().id(1L).build(); + Member member = Member.builder().client(client).build(); + Contract contract = Contract.builder().member(member).build(); + PaymentStatus paymentStatus = PaymentStatus.builder().id(3L).build(); + + Invoice invoice = Invoice.builder() + .id(1L) + .contract(contract) + .chargeAmount(1000L) + .paymentStatus(paymentStatus) + .isDeleted(false) + .build(); + + when(invoiceStatisticsListener.getClientId()).thenReturn(null); + + Chunk chunk = new Chunk<>(List.of(invoice)); + + // When + writer.write(chunk); + + // Then + verify(invoiceStatisticsListener, times(1)).setClientId(client.getId()); + verify(invoiceStatisticsListener, never()).addInvoice(anyLong()); + verify(invoiceStatisticsListener, never()).addCollected(anyLong()); + verify(invoiceStatisticsListener, never()).addOutstanding(anyLong()); + } + + @Test + public void testWriteWithDeletedInvoice() throws Exception { + // Given + Client client = Client.builder().id(1L).build(); + Member member = Member.builder().client(client).build(); + Contract contract = Contract.builder().member(member).build(); + PaymentStatus paymentStatus = PaymentStatus.builder().id(2L).build(); + + Invoice invoice = Invoice.builder() + .id(1L) + .contract(contract) + .chargeAmount(1000L) + .paymentStatus(paymentStatus) + .isDeleted(true) + .build(); + + when(invoiceStatisticsListener.getClientId()).thenReturn(null); + + Chunk chunk = new Chunk<>(List.of(invoice)); + + // When + writer.write(chunk); + + // Then + verify(invoiceStatisticsListener, times(1)).setClientId(client.getId()); + verify(invoiceStatisticsListener, never()).addInvoice(anyLong()); + verify(invoiceStatisticsListener, never()).addCollected(anyLong()); + verify(invoiceStatisticsListener, never()).addOutstanding(anyLong()); + } +} \ No newline at end of file From c0314b67d9103072b67ec2785e7f9f842af68467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Thu, 18 Jul 2024 16:33:07 +0900 Subject: [PATCH 18/25] =?UTF-8?q?[K5P-61]=20[refactor]=20=20=EC=9B=94?= =?UTF-8?q?=EA=B0=84=20=EC=A7=91=EA=B3=84=20=EB=A0=88=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?insert=EC=97=90=20Week=20=EA=B0=92=200=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/listner/statistic/InvoiceStatisticsListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java index ba69c74..ea0879c 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java @@ -36,7 +36,7 @@ public ExitStatus afterStep(StepExecution stepExecution) { // 통계 타입에 따라 시작 날짜와 기간 번호를 설정하기 if (getStatisticsType() == StatusConstants.STATISTICS_TYPE_MONTHLY) { startOfPeriod = today.minusMonths(1).withDayOfMonth(1); - periodNumber = startOfPeriod.getMonthValue(); + periodNumber = 0; // 월간일 경우에는 주간 데이터 0 } else { startOfPeriod = today.minusWeeks(1).with(DayOfWeek.MONDAY); periodNumber = startOfPeriod.get(WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear()); From d8a6c1c617183d3de20ea97a02375b43655e5c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Thu, 18 Jul 2024 20:51:48 +0900 Subject: [PATCH 19/25] =?UTF-8?q?[K5P-53]=20[feat]=20=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B8=B0=EB=8A=A5=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 10 ++ .../server_batch/ServerBatchApplication.java | 2 + .../config/InvoiceProcessingJobConfig.java | 5 +- .../InvoiceSendingAndPaymentManageWriter.java | 36 ++++-- .../batch/scheduler/Scheduler.java | 104 +++++++++--------- .../batch/server_batch/feign/PayClient.java | 15 +++ .../server_batch/feign/PayClientResponse.java | 11 ++ src/main/resources/application-dev.yml | 7 +- 8 files changed, 127 insertions(+), 63 deletions(-) create mode 100644 src/main/java/site/billingwise/batch/server_batch/feign/PayClient.java create mode 100644 src/main/java/site/billingwise/batch/server_batch/feign/PayClientResponse.java diff --git a/build.gradle b/build.gradle index c19ecf1..2052ccd 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-mail' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' implementation 'net.nurigo:sdk:4.3.0' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' @@ -53,6 +54,15 @@ checkstyle { configProperties = ["suppressionFile": "config/checkstyle/naver-checkstyle-suppressions.xml"] } +ext { + set('springCloudVersion', "2023.0.2") +} + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } +} tasks.named('test') { diff --git a/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java b/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java index 9d0402b..cfa9dc3 100644 --- a/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java +++ b/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @EnableJpaAuditing +@EnableFeignClients public class ServerBatchApplication { public static void main(String[] args) { diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java index 95f0fd5..5376ae1 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/config/InvoiceProcessingJobConfig.java @@ -1,5 +1,6 @@ package site.billingwise.batch.server_batch.batch.invoiceprocessing.config; +import feign.Client; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.batch.core.Job; @@ -22,6 +23,7 @@ import site.billingwise.batch.server_batch.batch.service.EmailService; import site.billingwise.batch.server_batch.batch.service.SmsService; import site.billingwise.batch.server_batch.domain.invoice.Invoice; +import site.billingwise.batch.server_batch.feign.PayClient; import javax.sql.DataSource; @@ -36,6 +38,7 @@ public class InvoiceProcessingJobConfig { private final JobCompletionCheckListener jobCompletionCheckListener; private final EmailService emailService; private final SmsService smsService; + private final PayClient payClient; @Bean public Job invoiceProcessingJob(JobRepository jobRepository, Step invoiceSendingAndPaymentManageStep, Step invoiceDueDateUpdateStep) { @@ -85,6 +88,6 @@ private ItemReader invoiceSendingAndPaymentManageReader() { } private ItemWriter invoiceSendingAndPaymentManageWriter() { - return new InvoiceSendingAndPaymentManageWriter(jdbcTemplate, emailService, smsService); + return new InvoiceSendingAndPaymentManageWriter(jdbcTemplate, emailService, smsService, payClient); } } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java index 4e75652..4c21824 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriter.java @@ -12,6 +12,8 @@ import site.billingwise.batch.server_batch.domain.invoice.Invoice; import site.billingwise.batch.server_batch.domain.member.ConsentAccount; import site.billingwise.batch.server_batch.domain.member.Member; +import site.billingwise.batch.server_batch.feign.PayClient; +import site.billingwise.batch.server_batch.feign.PayClientResponse; import static site.billingwise.batch.server_batch.batch.util.StatusConstants.*; @@ -23,6 +25,7 @@ public class InvoiceSendingAndPaymentManageWriter implements ItemWriter private final JdbcTemplate jdbcTemplate; private final EmailService emailService; private final SmsService smsService; + private final PayClient payClient; @Override public void write(Chunk chunk) { @@ -30,6 +33,7 @@ public void write(Chunk chunk) { for(Invoice invoice : chunk) { if(invoice.getIsDeleted()) { + log.info("삭제된 invoice 데이터 ID: {}", invoice.getId()); continue; } @@ -37,6 +41,8 @@ public void write(Chunk chunk) { boolean checkSubscription = contract.getIsSubscription(); long contract_id = contract.getId(); Member member = contract.getMember(); + + log.info("Processing invoice ID: {}, Contract ID: {}, Member ID: {}", invoice.getId(), contract_id, member.getId()); // 자동 이체 if(invoice.getPaymentType().getId() == PAYMENT_TYPE_AUTOMATIC_TRANSFER) { @@ -44,13 +50,17 @@ public void write(Chunk chunk) { boolean paymentAttempt = false; if (consentAccount != null) { - // 결제 시도( 아직 개발되지 않았음 ) - paymentAttempt = processAutoPayment(invoice); + + log.info("자동 결제 시도 invoice ID: {}, Account Number: {}", invoice.getId(), consentAccount.getNumber()); + paymentAttempt = processAutoPayment(invoice,consentAccount); + + } else { + log.info("자동 결제 시도 안하는 invoice ID: {}", invoice.getId()); } - // 결제 성공 시 - if (paymentAttempt) { + if (paymentAttempt) { + log.info("결제 성공한 invoice ID: {}", invoice.getId()); updatePaymentStatus(invoice.getId(), PAYMENT_STATUS_COMPLETED); insertPaymentRecord(invoice, consentAccount); emailService.sendPaymentSuccessMailCode(invoice.getContract().getMember().getEmail(), invoice, consentAccount); @@ -62,7 +72,7 @@ public void write(Chunk chunk) { // 결제 실패 시 } else { - + log.info("결제 실패한 invoice ID: {}", invoice.getId()); emailService.sendPaymentFailMailCode(invoice.getContract().getMember().getEmail(), invoice, consentAccount); updateFailPaymentStatus(invoice.getId()); smsService.sendFailBilling(member.getPhone(), member.getConsentAccount().getOwner(), member.getConsentAccount().getBank(), invoice.getChargeAmount().intValue()); @@ -74,7 +84,7 @@ public void write(Chunk chunk) { // 납부자 결제 } else if(invoice.getPaymentType().getId() == PAYMENT_TYPE_PAYER_PAYMENT){ - + log.info("납부자 결제 invoice ID: {}", invoice.getId()); emailService.sendInvoiceMail(invoice.getContract().getMember().getEmail(), invoice); updatePaymentStatus(invoice.getId(), PAYMENT_STATUS_PENDING); smsService.sendInvoice(member.getPhone(), member.getConsentAccount().getOwner(), member.getConsentAccount().getBank(), invoice.getChargeAmount().intValue()); @@ -119,9 +129,17 @@ private void updatePaymentStatus(long invoiceId, long statusId) { } //결제 시도 - private boolean processAutoPayment(Invoice invoice) { - // 결제 시도 로직을 들어갈 곳 + private boolean processAutoPayment(Invoice invoice, ConsentAccount consentAccount) { + + String type = new String(); + if(invoice.getPaymentType().getId() == PAYMENT_TYPE_AUTOMATIC_TRANSFER) { + type = "account"; + } + String number = consentAccount.getNumber(); + PayClientResponse response = payClient.pay(type, number); + + log.info("결제 시도 세부 내역 invoice ID: {}, Response status: {}", invoice.getId(), response.getStatusCode()); - return true; + return response.getStatusCode() == 200; } } diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java index f31d964..5bd7eaf 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java @@ -74,40 +74,16 @@ public class Scheduler { // } // } // -//// 15, 45초마다 실행monthlyInvoiceStatisticsJob -// @Scheduled(cron = "15,45 * * * * ?") -// public void runInvoiceProcessingJob() { -// JobParameters jobParameters = new JobParametersBuilder() -// .addLong("InvoiceProcessingJob", System.currentTimeMillis()) -// .toJobParameters(); -// try { -// log.info("Invoice Processing Job 실행 시작"); -// jobLauncher.run(invoiceProcessingJob, jobParameters); -// log.info("Invoice Processing Job 실행 완료"); -// } catch (JobExecutionAlreadyRunningException e) { -// log.error("JobExecutionAlreadyRunningException 발생: ", e); -// } catch (JobRestartException e) { -// log.error("JobRestartException 발생: ", e); -// } catch (JobInstanceAlreadyCompleteException e) { -// log.error("JobInstanceAlreadyCompleteException 발생: ", e); -// } catch (JobParametersInvalidException e) { -// log.error("JobParametersInvalidException 발생: ", e); -// } catch (Exception e) { -// log.error("예기치 않은 오류 발생: ", e); -// } -// } - - - // @Scheduled(cron = "0 0 1 * * SUN") // 매주 일요일 새벽 1시에 실행 +// 15, 45초마다 실행monthlyInvoiceStatisticsJob @Scheduled(cron = "15,45 * * * * ?") - public void weeklyJob() { + public void runInvoiceProcessingJob() { JobParameters jobParameters = new JobParametersBuilder() - .addLong("weeklyInvoiceStatisticsJob", System.currentTimeMillis()) + .addLong("InvoiceProcessingJob", System.currentTimeMillis()) .toJobParameters(); try { - log.info("weeklyInvoiceStatisticsJob 실행 시작"); - jobLauncher.run(weeklyInvoiceStatisticsJob, jobParameters); - log.info("weeklyInvoiceStatisticsJob 실행 완료"); + log.info("Invoice Processing Job 실행 시작"); + jobLauncher.run(invoiceProcessingJob, jobParameters); + log.info("Invoice Processing Job 실행 완료"); } catch (JobExecutionAlreadyRunningException e) { log.error("JobExecutionAlreadyRunningException 발생: ", e); } catch (JobRestartException e) { @@ -121,29 +97,53 @@ public void weeklyJob() { } } -// @Scheduled(cron = "0 0 1 1 * ?") // 매월 1일 새벽 1시 - @Scheduled(cron = "0,30 * * * * ?") - public void monthlyJob() { - JobParameters jobParameters = new JobParametersBuilder() - .addLong("monthlyInvoiceStatisticsJob", System.currentTimeMillis()) - .toJobParameters(); - try { - log.info("monthlyInvoiceStatisticsJob 실행 시작"); - jobLauncher.run(monthlyInvoiceStatisticsJob, jobParameters); - log.info("monthlyInvoiceStatisticsJob 실행 완료"); - } catch (JobExecutionAlreadyRunningException e) { - log.error("JobExecutionAlreadyRunningException 발생: ", e); - } catch (JobRestartException e) { - log.error("JobRestartException 발생: ", e); - } catch (JobInstanceAlreadyCompleteException e) { - log.error("JobInstanceAlreadyCompleteException 발생: ", e); - } catch (JobParametersInvalidException e) { - log.error("JobParametersInvalidException 발생: ", e); - } catch (Exception e) { - log.error("예기치 않은 오류 발생: ", e); - } +// +// // @Scheduled(cron = "0 0 1 * * SUN") // 매주 일요일 새벽 1시에 실행 +// @Scheduled(cron = "15,45 * * * * ?") +// public void weeklyJob() { +// JobParameters jobParameters = new JobParametersBuilder() +// .addLong("weeklyInvoiceStatisticsJob", System.currentTimeMillis()) +// .toJobParameters(); +// try { +// log.info("weeklyInvoiceStatisticsJob 실행 시작"); +// jobLauncher.run(weeklyInvoiceStatisticsJob, jobParameters); +// log.info("weeklyInvoiceStatisticsJob 실행 완료"); +// } catch (JobExecutionAlreadyRunningException e) { +// log.error("JobExecutionAlreadyRunningException 발생: ", e); +// } catch (JobRestartException e) { +// log.error("JobRestartException 발생: ", e); +// } catch (JobInstanceAlreadyCompleteException e) { +// log.error("JobInstanceAlreadyCompleteException 발생: ", e); +// } catch (JobParametersInvalidException e) { +// log.error("JobParametersInvalidException 발생: ", e); +// } catch (Exception e) { +// log.error("예기치 않은 오류 발생: ", e); +// } +// } +// +//// @Scheduled(cron = "0 0 1 1 * ?") // 매월 1일 새벽 1시 +// @Scheduled(cron = "0,30 * * * * ?") +// public void monthlyJob() { +// JobParameters jobParameters = new JobParametersBuilder() +// .addLong("monthlyInvoiceStatisticsJob", System.currentTimeMillis()) +// .toJobParameters(); +// try { +// log.info("monthlyInvoiceStatisticsJob 실행 시작"); +// jobLauncher.run(monthlyInvoiceStatisticsJob, jobParameters); +// log.info("monthlyInvoiceStatisticsJob 실행 완료"); +// } catch (JobExecutionAlreadyRunningException e) { +// log.error("JobExecutionAlreadyRunningException 발생: ", e); +// } catch (JobRestartException e) { +// log.error("JobRestartException 발생: ", e); +// } catch (JobInstanceAlreadyCompleteException e) { +// log.error("JobInstanceAlreadyCompleteException 발생: ", e); +// } catch (JobParametersInvalidException e) { +// log.error("JobParametersInvalidException 발생: ", e); +// } catch (Exception e) { +// log.error("예기치 않은 오류 발생: ", e); +// } - } +// } } diff --git a/src/main/java/site/billingwise/batch/server_batch/feign/PayClient.java b/src/main/java/site/billingwise/batch/server_batch/feign/PayClient.java new file mode 100644 index 0000000..b4855b7 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/feign/PayClient.java @@ -0,0 +1,15 @@ +package site.billingwise.batch.server_batch.feign; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + + +@FeignClient(name = "PayClient", url = "${pay.api.url}") +public interface PayClient { + + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + PayClientResponse pay(@RequestParam String type, @RequestParam String number); + +} \ No newline at end of file diff --git a/src/main/java/site/billingwise/batch/server_batch/feign/PayClientResponse.java b/src/main/java/site/billingwise/batch/server_batch/feign/PayClientResponse.java new file mode 100644 index 0000000..7bc26b0 --- /dev/null +++ b/src/main/java/site/billingwise/batch/server_batch/feign/PayClientResponse.java @@ -0,0 +1,11 @@ +package site.billingwise.batch.server_batch.feign; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class PayClientResponse { + private int statusCode; + private String message; +} \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 3cac18e..858651e 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -46,4 +46,9 @@ coolsms: api: key: "NCSLLWQSPGK4Z8KX" secret: "O0F2MXPETVI0AIT0ZTPEG0Z1KWSLJV5P" - url: https://api.coolsms.co.kr \ No newline at end of file + url: https://api.coolsms.co.kr + +--- +pay: + api: + url: https://pay.billingwise.site/t5-pay \ No newline at end of file From a9fc9db649075f10bef0a3fd60da6086a14bce18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Fri, 19 Jul 2024 11:31:13 +0900 Subject: [PATCH 20/25] =?UTF-8?q?[chore]=20=EC=8A=A4=EC=BC=80=EC=A4=84?= =?UTF-8?q?=EB=9F=AC=20=EC=8B=9C=EA=B0=84=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistic/InvoiceStatisticsListener.java | 4 + .../batch/scheduler/Scheduler.java | 160 ++++++++++-------- 2 files changed, 89 insertions(+), 75 deletions(-) diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java index ea0879c..aed4c24 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/listner/statistic/InvoiceStatisticsListener.java @@ -53,6 +53,10 @@ private void updateinvoiceStatics(JdbcTemplate jdbcTemplate , long totalInvoiced log.info("업데이트 invoice statistics 테이블: totalInvoicedMoney={}, totalCollectedMoney={}, totalOutstanding={}, startOfLastWeek={}, weekNumber={}", totalInvoicedMoney, totalCollectedMoney, totalOutstanding, startOfPeriod, periodNumber); + // 데이터 없을 시 에러 방지 ( 제약 조건 ) + if (clientId == null) { + clientId = -1L; + } String sql = "insert into invoice_statistics (reference_date, total_invoiced, total_collected, outstanding, type_id, year, month, week, client_id, is_deleted, created_at, updated_at ) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, false, now(), now())"; diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java index 5bd7eaf..09b125a 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java @@ -27,8 +27,8 @@ public class Scheduler { private final Job weeklyInvoiceStatisticsJob; private final Job monthlyInvoiceStatisticsJob; -// -// // 0, 30초마다 실행 +// 0, 30초마다 실행 +// 청구 생성 (jpa) // @Scheduled(cron = "0,30 * * * * ?") // public void generateInvoice() { // JobParameters jobParameters = new JobParametersBuilder() @@ -50,32 +50,38 @@ public class Scheduler { // log.error("예기치 않은 오류 발생: ", e); // } // } -// -// // 15, 45초마다 실행 -// @Scheduled(cron = "15,45 * * * * ?") -// public void jdbcGenerateInvoice() { -// JobParameters jobParameters = new JobParametersBuilder() -// .addLong("jdbcInvoice", System.currentTimeMillis()) -// .toJobParameters(); -// try { -// log.info("JDBC 배치 프로그램 실행 시작"); -// jobLauncher.run(jdbcGenerateInvoiceJob, jobParameters); -// log.info("JDBC 배치 프로그램 실행 완료"); -// } catch (JobExecutionAlreadyRunningException e) { -// log.error("JobExecutionAlreadyRunningException 발생: ", e); -// } catch (JobRestartException e) { -// log.error("JobRestartException 발생: ", e); -// } catch (JobInstanceAlreadyCompleteException e) { -// log.error("JobInstanceAlreadyCompleteException 발생: ", e); -// } catch (JobParametersInvalidException e) { -// log.error("JobParametersInvalidException 발생: ", e); -// } catch (Exception e) { -// log.error("예기치 않은 오류 발생: ", e); -// } -// } -// -// 15, 45초마다 실행monthlyInvoiceStatisticsJob - @Scheduled(cron = "15,45 * * * * ?") + + // 청구 생성 잡 +// 15, 45초마다 실행( 테스트 용 ) +// @Scheduled(cron = "15,45 * * * * ?") + // 매달 말일 새벽 3시에 작동 + @Scheduled(cron = "0 0 3 L * ?") + public void jdbcGenerateInvoice() { + JobParameters jobParameters = new JobParametersBuilder() + .addLong("jdbcInvoice", System.currentTimeMillis()) + .toJobParameters(); + try { + log.info("JDBC 배치 프로그램 실행 시작"); + jobLauncher.run(jdbcGenerateInvoiceJob, jobParameters); + log.info("JDBC 배치 프로그램 실행 완료"); + } catch (JobExecutionAlreadyRunningException e) { + log.error("JobExecutionAlreadyRunningException 발생: ", e); + } catch (JobRestartException e) { + log.error("JobRestartException 발생: ", e); + } catch (JobInstanceAlreadyCompleteException e) { + log.error("JobInstanceAlreadyCompleteException 발생: ", e); + } catch (JobParametersInvalidException e) { + log.error("JobParametersInvalidException 발생: ", e); + } catch (Exception e) { + log.error("예기치 않은 오류 발생: ", e); + } + } + + // 결제 처리 및 결제 기한(Due_date) 지난 납부자 결제 처리 잡 +// 15, 45초마다 실행( 테스트용 ) +// @Scheduled(cron = "15,45 * * * * ?") + // 매일 새벽 1시에 작동 + @Scheduled(cron = "0 0 1 * * ?") public void runInvoiceProcessingJob() { JobParameters jobParameters = new JobParametersBuilder() .addLong("InvoiceProcessingJob", System.currentTimeMillis()) @@ -97,55 +103,59 @@ public void runInvoiceProcessingJob() { } } -// -// // @Scheduled(cron = "0 0 1 * * SUN") // 매주 일요일 새벽 1시에 실행 -// @Scheduled(cron = "15,45 * * * * ?") -// public void weeklyJob() { -// JobParameters jobParameters = new JobParametersBuilder() -// .addLong("weeklyInvoiceStatisticsJob", System.currentTimeMillis()) -// .toJobParameters(); -// try { -// log.info("weeklyInvoiceStatisticsJob 실행 시작"); -// jobLauncher.run(weeklyInvoiceStatisticsJob, jobParameters); -// log.info("weeklyInvoiceStatisticsJob 실행 완료"); -// } catch (JobExecutionAlreadyRunningException e) { -// log.error("JobExecutionAlreadyRunningException 발생: ", e); -// } catch (JobRestartException e) { -// log.error("JobRestartException 발생: ", e); -// } catch (JobInstanceAlreadyCompleteException e) { -// log.error("JobInstanceAlreadyCompleteException 발생: ", e); -// } catch (JobParametersInvalidException e) { -// log.error("JobParametersInvalidException 발생: ", e); -// } catch (Exception e) { -// log.error("예기치 않은 오류 발생: ", e); -// } -// } -// -//// @Scheduled(cron = "0 0 1 1 * ?") // 매월 1일 새벽 1시 -// @Scheduled(cron = "0,30 * * * * ?") -// public void monthlyJob() { -// JobParameters jobParameters = new JobParametersBuilder() -// .addLong("monthlyInvoiceStatisticsJob", System.currentTimeMillis()) -// .toJobParameters(); -// try { -// log.info("monthlyInvoiceStatisticsJob 실행 시작"); -// jobLauncher.run(monthlyInvoiceStatisticsJob, jobParameters); -// log.info("monthlyInvoiceStatisticsJob 실행 완료"); -// } catch (JobExecutionAlreadyRunningException e) { -// log.error("JobExecutionAlreadyRunningException 발생: ", e); -// } catch (JobRestartException e) { -// log.error("JobRestartException 발생: ", e); -// } catch (JobInstanceAlreadyCompleteException e) { -// log.error("JobInstanceAlreadyCompleteException 발생: ", e); -// } catch (JobParametersInvalidException e) { -// log.error("JobParametersInvalidException 발생: ", e); -// } catch (Exception e) { -// log.error("예기치 않은 오류 발생: ", e); -// } - -// } + // 주간 청구액 및 수납액 집계 통계 처리 잡 +// 15, 45초마다 실행( 테스트용 ) +// @Scheduled(cron = "15,45 * * * * ?") + // 매주 월요일 새벽 5시에 작동 + @Scheduled(cron = "0 0 5 ? * MON") + public void weeklyJob() { + JobParameters jobParameters = new JobParametersBuilder() + .addLong("weeklyInvoiceStatisticsJob", System.currentTimeMillis()) + .toJobParameters(); + try { + log.info("weeklyInvoiceStatisticsJob 실행 시작"); + jobLauncher.run(weeklyInvoiceStatisticsJob, jobParameters); + log.info("weeklyInvoiceStatisticsJob 실행 완료"); + } catch (JobExecutionAlreadyRunningException e) { + log.error("JobExecutionAlreadyRunningException 발생: ", e); + } catch (JobRestartException e) { + log.error("JobRestartException 발생: ", e); + } catch (JobInstanceAlreadyCompleteException e) { + log.error("JobInstanceAlreadyCompleteException 발생: ", e); + } catch (JobParametersInvalidException e) { + log.error("JobParametersInvalidException 발생: ", e); + } catch (Exception e) { + log.error("예기치 않은 오류 발생: ", e); + } } + // 월간 청구액 및 수납액 집계 통계 처리 잡 +// 15, 45초마다 실행( 테스트용 ) +// @Scheduled(cron = "0,30 * * * * ?") + // 매월 1일 새벽 1시에 작동 + @Scheduled(cron = "0 0 6 1 * ?") + public void monthlyJob() { + JobParameters jobParameters = new JobParametersBuilder() + .addLong("monthlyInvoiceStatisticsJob", System.currentTimeMillis()) + .toJobParameters(); + try { + log.info("monthlyInvoiceStatisticsJob 실행 시작"); + jobLauncher.run(monthlyInvoiceStatisticsJob, jobParameters); + log.info("monthlyInvoiceStatisticsJob 실행 완료"); + } catch (JobExecutionAlreadyRunningException e) { + log.error("JobExecutionAlreadyRunningException 발생: ", e); + } catch (JobRestartException e) { + log.error("JobRestartException 발생: ", e); + } catch (JobInstanceAlreadyCompleteException e) { + log.error("JobInstanceAlreadyCompleteException 발생: ", e); + } catch (JobParametersInvalidException e) { + log.error("JobParametersInvalidException 발생: ", e); + } catch (Exception e) { + log.error("예기치 않은 오류 발생: ", e); + } + + } +} From f0b911f325e9b20362a5b915cea5a0747b7db95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9A=B0=EC=84=B1?= Date: Mon, 22 Jul 2024 10:57:35 +0900 Subject: [PATCH 21/25] =?UTF-8?q?[K5P-63]=20[env]=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=95=84=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .../server_batch/ServerBatchApplication.java | 2 + src/main/resources/application-dev.yml | 46 +----------------- src/main/resources/application-prod.yml | 10 ++++ src/main/resources/application.yml | 48 +++++++++++++++++++ 5 files changed, 63 insertions(+), 45 deletions(-) create mode 100644 src/main/resources/application-prod.yml diff --git a/.gitignore b/.gitignore index c2065bc..44d542b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ out/ ### VS Code ### .vscode/ + +secure.properties diff --git a/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java b/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java index cfa9dc3..cbfccbe 100644 --- a/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java +++ b/src/main/java/site/billingwise/batch/server_batch/ServerBatchApplication.java @@ -3,11 +3,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.PropertySource; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @EnableJpaAuditing @EnableFeignClients +@PropertySource("classpath:secure.properties") public class ServerBatchApplication { public static void main(String[] args) { diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 858651e..434e217 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,54 +1,10 @@ spring: datasource: hikari: - jdbc-url: jdbc:mysql://localhost:3308/batch_data?useUnicode=true&characterEncoding=utf8 + jdbc-url: jdbc:mysql://localhost:3307/billingwise?useSSL=false&characterEncoding=UTF-8&serverTimeZone=Asia/Seoul&allowPublicKeyRetrieval=true username: root password: 1004 driver-class-name: com.mysql.cj.jdbc.Driver batch: jdbc: initialize-schema: always ---- -spring: - jpa: - hibernate: - ddl-auto: validate - show-sql: false - properties: - hibernate: - dialect: org.hibernate.dialect.MySQL8Dialect - format_sql: true - defer-datasource-initialization: false - datasource: - initialization-mode: never ---- -spring: - mail: - host: smtp.gmail.com - port: 587 - username: bhjin42@gmail.com - password: uvrt oabq gqdy isbg - properties: - mail: - smtp: - auth: true - starttls: - enable: true - ---- -spring: - batch: - job: - enabled: false - ---- -coolsms: - api: - key: "NCSLLWQSPGK4Z8KX" - secret: "O0F2MXPETVI0AIT0ZTPEG0Z1KWSLJV5P" - url: https://api.coolsms.co.kr - ---- -pay: - api: - url: https://pay.billingwise.site/t5-pay \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000..a21d821 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,10 @@ +spring: + datasource: + hikari: + jdbc-url: ${DB_URL} + username: ${DB_USERNAME} + password: ${DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + batch: + jdbc: + initialize-schema: always \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f368052..9abb43d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,10 +1,58 @@ server: port: 9090 +--- spring: profiles: active: dev +--- +spring: + jpa: + hibernate: + ddl-auto: validate + show-sql: false + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL8Dialect + format_sql: true + defer-datasource-initialization: false + datasource: + initialization-mode: never + +--- +spring: + mail: + host: smtp.gmail.com + port: 587 + username: ${MAIL_USERNAME} + password: ${MAIL_PASSWORD} + properties: + mail: + smtp: + auth: true + starttls: + enable: true + +--- +spring: + batch: + job: + enabled: false + +--- +coolsms: + api: + key: ${COOLSMS_API_KEY} + secret: ${COOLSMS_SECRET_KEY} + url: https://api.coolsms.co.kr + +--- +pay: + api: + url: ${PAY_API_URL} + +--- logging: level: org: From b3f071ecb580c1adba3ad06c94d507f009df0b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B3=80=ED=98=84=EC=A7=84?= <78016592+shinbm44@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:19:52 +0900 Subject: [PATCH 22/25] =?UTF-8?q?[K5P-53]=20[refactor,test]=20InvoiceSendi?= =?UTF-8?q?ngAndPaymentManageWriterTest=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/scheduler/Scheduler.java | 2 +- ...oiceSendingAndPaymentManageWriterTest.java | 45 +++++++++++++++---- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java index 09b125a..31ba337 100644 --- a/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java +++ b/src/main/java/site/billingwise/batch/server_batch/batch/scheduler/Scheduler.java @@ -132,7 +132,7 @@ public void weeklyJob() { // 월간 청구액 및 수납액 집계 통계 처리 잡 // 15, 45초마다 실행( 테스트용 ) // @Scheduled(cron = "0,30 * * * * ?") - // 매월 1일 새벽 1시에 작동 + // 매월 1일 새벽 6시에 작동 @Scheduled(cron = "0 0 6 1 * ?") public void monthlyJob() { JobParameters jobParameters = new JobParametersBuilder() diff --git a/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriterTest.java b/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriterTest.java index 889c40e..5dcb05f 100644 --- a/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriterTest.java +++ b/src/test/java/site/billingwise/batch/server_batch/batch/invoiceprocessing/writer/InvoiceSendingAndPaymentManageWriterTest.java @@ -9,17 +9,20 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.jdbc.core.JdbcTemplate; import site.billingwise.batch.server_batch.batch.service.EmailService; +import site.billingwise.batch.server_batch.batch.service.SmsService; import site.billingwise.batch.server_batch.domain.contract.Contract; import site.billingwise.batch.server_batch.domain.contract.PaymentType; import site.billingwise.batch.server_batch.domain.invoice.Invoice; import site.billingwise.batch.server_batch.domain.member.ConsentAccount; import site.billingwise.batch.server_batch.domain.member.Member; +import site.billingwise.batch.server_batch.feign.PayClient; +import site.billingwise.batch.server_batch.feign.PayClientResponse; import java.lang.reflect.Method; import static org.mockito.Mockito.verify; -import static site.billingwise.batch.server_batch.batch.util.StatusConstants.INVOICE_TYPE_AUTOMATIC_BILLING; -import static site.billingwise.batch.server_batch.batch.util.StatusConstants.PAYMENT_STATUS_COMPLETED; +import static org.mockito.Mockito.when; +import static site.billingwise.batch.server_batch.batch.util.StatusConstants.*; @ExtendWith(MockitoExtension.class) public class InvoiceSendingAndPaymentManageWriterTest { @@ -30,6 +33,12 @@ public class InvoiceSendingAndPaymentManageWriterTest { @Mock private EmailService emailService; + @Mock + private SmsService smsService; + + @Mock + private PayClient payClient; + @InjectMocks private InvoiceSendingAndPaymentManageWriter writer; @@ -38,18 +47,21 @@ public class InvoiceSendingAndPaymentManageWriterTest { @BeforeEach public void setUp() { Member member = Member.builder() + .id(1L) .email("test@naver.com") .build(); Contract contract = Contract.builder() + .id(1L) .member(member) .build(); PaymentType paymentType = PaymentType.builder() - .id(INVOICE_TYPE_AUTOMATIC_BILLING) + .id(PAYMENT_TYPE_AUTOMATIC_TRANSFER) .build(); invoice = Invoice.builder() + .id(1L) .isDeleted(false) .contract(contract) .paymentType(paymentType) @@ -59,7 +71,7 @@ public void setUp() { @Test @DisplayName("결제 실패 시 수정 시각 업데이트") public void testUpdateFailPaymentStatus() throws Exception { - Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("updateFailPaymentStatus", Long.class); + Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("updateFailPaymentStatus", long.class); method.setAccessible(true); method.invoke(writer, 1L); verify(jdbcTemplate).update("update invoice set updated_at = now() where invoice_id = ?", 1L); @@ -77,7 +89,7 @@ public void testInsertPaymentRecord() throws Exception { Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("insertPaymentRecord", Invoice.class, ConsentAccount.class); method.setAccessible(true); method.invoke(writer, invoice, consentAccount); - verify(jdbcTemplate).update("insert into payment (invoice_id, payment_method, pay_amount, created_at, updated_at, is_deleted) values (?, ?, ?, NOW(), NOW(), false)", invoice.getId(), "계좌이체", invoice.getChargeAmount()); + verify(jdbcTemplate).update("insert into payment (invoice_id, payment_method, pay_amount, created_at, updated_at, is_deleted) values (?, ?, ?, NOW(), NOW(), false)", invoice.getId(), "ACCOUNT", invoice.getChargeAmount()); } @Test @@ -89,7 +101,7 @@ public void testInsertPaymentAccount() throws Exception { .owner("변현진") .build(); - Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("insertPaymentAccount", Long.class, ConsentAccount.class); + Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("insertPaymentAccount", long.class, ConsentAccount.class); // long 타입으로 변경 method.setAccessible(true); method.invoke(writer, 1L, consentAccount); verify(jdbcTemplate).update("insert into payment_account (invoice_id, number, bank, owner, created_at, updated_at, is_deleted) values (?, ?, ?, ?, now(), now(), false)", 1L, "12345", "bank", "변현진"); @@ -98,9 +110,26 @@ public void testInsertPaymentAccount() throws Exception { @Test @DisplayName("납부 상태 변경") public void testUpdatePaymentStatus() throws Exception { - Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("updatePaymentStatus", Long.class, long.class); + Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("updatePaymentStatus", long.class, long.class); method.setAccessible(true); method.invoke(writer, 1L, PAYMENT_STATUS_COMPLETED); verify(jdbcTemplate).update("update invoice set payment_status_id = ?, updated_at = now() where invoice_id = ?", PAYMENT_STATUS_COMPLETED, 1L); } -} \ No newline at end of file + + @Test + @DisplayName("자동 결제 성공 테스트") + public void testProcessAutoPayment() throws Exception { + ConsentAccount consentAccount = ConsentAccount.builder() + .number("12345") + .build(); + + when(payClient.pay("account", "12345")).thenReturn(PayClientResponse.builder().statusCode(200).build()); + + Method method = InvoiceSendingAndPaymentManageWriter.class.getDeclaredMethod("processAutoPayment", Invoice.class, ConsentAccount.class); + method.setAccessible(true); + boolean result = (boolean) method.invoke(writer, invoice, consentAccount); + + assert result; + verify(payClient).pay("account", "12345"); + } +} From 3230774a3d6905904dc754342659f493c83417a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9A=B0=EC=84=B1?= Date: Mon, 22 Jul 2024 11:21:57 +0900 Subject: [PATCH 23/25] =?UTF-8?q?[K5P-63]=20[ci]=20ci=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A6=BD=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PULL_REQUEST_TEMPLATE.md | 11 ++++++ .github/workflows/ci.yml | 45 ++++++++++++++++++++++++ build.gradle | 47 +++++++++++++++++++------- src/main/resources/application-dev.yml | 2 +- 4 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/ci.yml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..ac5489b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +## 관련 이슈 + +- [지라 이슈 코드를 적어주세요.] + +## 구현 기능 + +- 구현 기능을 적어주세요. + +## 참고 사항 + +- 참고 사항을 적어주세요. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..cbd504f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI +on: + push: + branches: + - prod + - dev + pull_request: + +jobs: + sonarcloud: + name: SonarCloud + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: create secure file + run: | + cd src/main/resources + touch secure.properties + echo "${{ secrets.SECURE }}" >> secure.properties + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: 'gradle' + + - name: Cache SonarCloud packages + uses: actions/cache@v3 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Grant execute permission to Gradle Wrapper + run: chmod +x ./gradlew + + - name: Build and analyze + run: ./gradlew build jacocoTestReport sonar --info + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 2052ccd..4196934 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,9 @@ plugins { id 'java' id 'org.springframework.boot' version '3.2.7' id 'io.spring.dependency-management' version '1.1.5' - id 'checkstyle' + id 'com.google.cloud.tools.jib' version '3.4.3' + id 'org.sonarqube' version '4.4.1.3373' + id 'jacoco' } group = 'site.billingwise.batch' @@ -42,18 +44,6 @@ dependencies { testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } -// Checkstyle 설정을 추가합니다. -tasks.withType(Checkstyle) { - reports { - xml.required = true - } -} - -checkstyle { - configFile = file("config/checkstyle/naver-checkstyle-rules.xml") - configProperties = ["suppressionFile": "config/checkstyle/naver-checkstyle-suppressions.xml"] -} - ext { set('springCloudVersion', "2023.0.2") } @@ -68,3 +58,34 @@ dependencyManagement { tasks.named('test') { useJUnitPlatform() } + +jacocoTestReport { + reports { + xml.required.set(true) + } +} + +jib { + from { + image = "eclipse-temurin:17-jdk" + } + to { + image = "891376922202.dkr.ecr.ap-northeast-2.amazonaws.com/t5-batch-ecr" + tags = ["${project.version}".toString()] + credHelper = 'ecr-login' + } + container { + creationTime = "USE_CURRENT_TIMESTAMP" + jvmFlags = ['-Dspring.profiles.active=prod', '-XX:+UseContainerSupport', '-Dserver.port=9090', '-Dfile.encoding=UTF-8', '-Duser.timezone=Asia/Seoul'] + ports = ['9090'] + } +} + +sonar { + properties { + property "sonar.projectKey", "Billing-Wise_sever-batch" + property "sonar.organization", "billing-wise" + property "sonar.host.url", "https://sonarcloud.io" + property 'sonar.coverage.jacoco.xmlReportPaths', 'build/reports/jacoco/test/jacocoTestReport.xml' + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 434e217..13583f5 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,7 +1,7 @@ spring: datasource: hikari: - jdbc-url: jdbc:mysql://localhost:3307/billingwise?useSSL=false&characterEncoding=UTF-8&serverTimeZone=Asia/Seoul&allowPublicKeyRetrieval=true + jdbc-url: jdbc:mysql://localhost:3308/batch_data?useUnicode=true&characterEncoding=utf8 username: root password: 1004 driver-class-name: com.mysql.cj.jdbc.Driver From 44c7608179a544d87e7f7ec9b154828366014af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9A=B0=EC=84=B1?= Date: Mon, 22 Jul 2024 11:27:42 +0900 Subject: [PATCH 24/25] =?UTF-8?q?[K5P-63]=20[fix]=20contextLoads=20?= =?UTF-8?q?=EB=AF=B8=EC=8B=A4=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/server_batch/ServerBatchApplicationTests.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/site/billingwise/batch/server_batch/ServerBatchApplicationTests.java b/src/test/java/site/billingwise/batch/server_batch/ServerBatchApplicationTests.java index 2b0b369..058bb32 100644 --- a/src/test/java/site/billingwise/batch/server_batch/ServerBatchApplicationTests.java +++ b/src/test/java/site/billingwise/batch/server_batch/ServerBatchApplicationTests.java @@ -6,8 +6,8 @@ @SpringBootTest class ServerBatchApplicationTests { - @Test - void contextLoads() { - } +// @Test +// void contextLoads() { +// } } From e04fbe8dd81779fcf9d06960571f70e1ac986aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=9A=B0=EC=84=B1?= Date: Mon, 22 Jul 2024 11:51:14 +0900 Subject: [PATCH 25/25] =?UTF-8?q?[K5P-63]=20[cd]=20cd=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A6=BD=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 48 +++++++++++++++++++++++++++++++++++++ task-definition.json | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 .github/workflows/cd.yml create mode 100644 task-definition.json diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..2f8909c --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,48 @@ +name: CD +on: + push: + branches: + - prod +jobs: + deploy: + name: Deploy to AWS + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: create secure file + run: | + cd src/main/resources + touch secure.properties + echo "${{ secrets.SECURE }}" >> secure.properties + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ap-northeast-2 + + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@v1 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission to Gradle Wrapper + run: chmod +x ./gradlew + + - name: Build and push image to Amazon ECR + run: ./gradlew clean jib -x test + + - name: Deploy to AWS ECS + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + with: + task-definition: task-definition.json + cluster: t5-ecs-fargate-cluster + service: t5-batch-service + wait-for-service-stability: true \ No newline at end of file diff --git a/task-definition.json b/task-definition.json new file mode 100644 index 0000000..2126654 --- /dev/null +++ b/task-definition.json @@ -0,0 +1,51 @@ +{ + "containerDefinitions": [ + { + "name": "t5-batch-container", + "image": "891376922202.dkr.ecr.ap-northeast-2.amazonaws.com/t5-batch-ecr", + "cpu": 0, + "portMappings": [ + { + "name": "t5-batch-port", + "containerPort": 9090, + "hostPort": 9090, + "protocol": "tcp", + "appProtocol": "http" + } + ], + "essential": true, + "environment": [], + "environmentFiles": [], + "mountPoints": [], + "volumesFrom": [], + "ulimits": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "/ecs/t5-batch-task-definition", + "awslogs-create-group": "true", + "awslogs-region": "ap-northeast-2", + "awslogs-stream-prefix": "ecs" + }, + "secretOptions": [] + }, + "systemControls": [] + } + ], + "family": "t5-batch-task-definition", + "taskRoleArn": "arn:aws:iam::891376922202:role/ecs-task-execution-role", + "executionRoleArn": "arn:aws:iam::891376922202:role/ecsTaskExecutionRole", + "networkMode": "awsvpc", + "volumes": [], + "placementConstraints": [], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "1024", + "memory": "3072", + "runtimePlatform": { + "cpuArchitecture": "X86_64", + "operatingSystemFamily": "LINUX" + }, + "tags": [] +} \ No newline at end of file