Add JavaSrc: cobol-tna-system COBOL->Java conversion (ZAN01-06/KIN01-09/KYU01-09 + 变换规则 doc)
This commit is contained in:
@@ -0,0 +1,467 @@
|
||||
import dto.Kin07Rec;
|
||||
import dto.Kin08Rec;
|
||||
import dto.ZanendacPar;
|
||||
import dto.ZanmsgacPar;
|
||||
import util.ConvUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
* 【COBOL→Java】KIN08DBU 勤怠DB更新処理
|
||||
* ============================================================
|
||||
* PROGRAM-ID. KIN08DBU → 类 Kin08DbuMain
|
||||
* PGMパターン:DB更新 + SYSIN読込(P28)
|
||||
* 機能概要:DAILY-RECORD(R01/KIN07REC)を読込み DAILY_RECORDS へ INSERT。
|
||||
* 社員別月次集計(AGGREGATION-TABLE)後、MONTHLY_ABSENCE へ UPSERT。
|
||||
* 集計結果を ABSENCE-SUMMARY(W01/KIN08REC) に出力。
|
||||
* SYSIN制御カード(T/P/M)で対象社員・対象年月・モードを指定。
|
||||
*/
|
||||
public class Kin08DbuMain {
|
||||
|
||||
private static final String CNS_PRGIDX = "KIN08DBU";
|
||||
private static final int CNS_MSGSTR = 1;
|
||||
private static final int CNS_MSGFIN = 2;
|
||||
private static final int CNS_MSGIINKES = 6;
|
||||
private static final int CNS_MSGOUTKES = 7;
|
||||
private static final int CNS_MSGKEYINF = 33;
|
||||
private static final String CNS_KN0002 = "2";
|
||||
private static final int CNS_ABD999 = 999;
|
||||
|
||||
/** SELECT R01INNFIL ASSIGN TO "KIN08R01". */
|
||||
private static final String R01_FILE = "data/KIN08R01.DAT";
|
||||
/** SELECT SYSINFILE ASSIGN TO "KIN08S01". */
|
||||
private static final String SYSIN_FILE = "data/KIN08S01.DAT";
|
||||
/** SELECT W01OUTFIL ASSIGN TO "KIN08W01". */
|
||||
private static final String W01_FILE = "data/KIN08W01.DAT";
|
||||
|
||||
// ---- カウンタ ----
|
||||
private long cunR01Inn;
|
||||
private long cunDbxIns;
|
||||
private long cunDbxUpd;
|
||||
private long cunDbxDel;
|
||||
private long cunW01Out;
|
||||
|
||||
// ---- SYSIN 解析結果 ----
|
||||
/** Tカード対象社員(指定なし=全社員)。 */
|
||||
private final List<String> targetList = new ArrayList<>();
|
||||
private boolean allEmp = true;
|
||||
/** Pカード対象年月。 */
|
||||
private String yearMonth = "";
|
||||
private boolean periodFound = false;
|
||||
/** Mカード RESET モード。 */
|
||||
private boolean resetMode = false;
|
||||
|
||||
// ---- 集計テーブル(AGGREGATION-TABLE 相当、tenth 単位で保持)----
|
||||
private static class AggRow {
|
||||
String empId;
|
||||
String yearMonth;
|
||||
int annualH;
|
||||
int personalH;
|
||||
int officialH;
|
||||
int sickH;
|
||||
int absentH;
|
||||
/** WS-TOTAL-HOURS(設計書: COMPUTE ROUNDED + ON SIZE ERROR、出力には非含). */
|
||||
int totalH;
|
||||
}
|
||||
|
||||
private final List<AggRow> aggTable = new ArrayList<>();
|
||||
|
||||
private final ZanmsgacPar msgPar = new ZanmsgacPar();
|
||||
private final ZanendacPar endPar = new ZanendacPar();
|
||||
|
||||
private BufferedWriter w01OutFil;
|
||||
private Connection conn;
|
||||
|
||||
public static void main(final String[] args) {
|
||||
new Kin08DbuMain().run();
|
||||
}
|
||||
|
||||
private void run() {
|
||||
initProc();
|
||||
majProc();
|
||||
stpProc();
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// 初期処理 1000ITTSOR
|
||||
// *****************************************************************
|
||||
private void initProc() {
|
||||
msgOutPara(CNS_MSGSTR, "", "", "");
|
||||
msgOutPara(CNS_MSGKEYINF, "COMPILED", "", "");
|
||||
|
||||
// ワーク初期化
|
||||
allEmp = true;
|
||||
resetMode = false;
|
||||
|
||||
// DB接続
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
conn = DriverManager.getConnection("jdbc:sqlite:data/kin.db");
|
||||
conn.setAutoCommit(false);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("DB接続失敗: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
// SYSIN読込
|
||||
readSysin();
|
||||
|
||||
// 必須パラメータ(対象年月)未設定チェック
|
||||
if (!periodFound) {
|
||||
abend();
|
||||
}
|
||||
|
||||
// 入出力ファイルOPEN
|
||||
try {
|
||||
w01OutFil = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(W01_FILE), StandardCharsets.UTF_8));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("出力ファイルOPEN失敗: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// SYSIN 読込・解析(1199-SYSIN-LOOP / 1110/1120/1130)
|
||||
// *****************************************************************
|
||||
private void readSysin() {
|
||||
try (BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(new FileInputStream(SYSIN_FILE), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
char card = line.charAt(0);
|
||||
String body = line.length() > 2 ? line.substring(2) : "";
|
||||
if (card == 'T') {
|
||||
// Tカード: 対象社員一覧(カンマ区切り)
|
||||
targetList.clear();
|
||||
String[] emps = body.split(",");
|
||||
for (String e : emps) {
|
||||
String t = e.trim();
|
||||
if (!t.isEmpty()) {
|
||||
targetList.add(t);
|
||||
}
|
||||
}
|
||||
allEmp = false;
|
||||
} else if (card == 'P') {
|
||||
// Pカード: YEARMONTH=YYYYMM
|
||||
String[] kv = body.split("=");
|
||||
String val = kv.length > 1 ? kv[1].trim() : "";
|
||||
if (val.length() >= 6) {
|
||||
yearMonth = val.substring(0, 6);
|
||||
periodFound = true;
|
||||
}
|
||||
} else if (card == 'M') {
|
||||
// Mカード: MODE=RESET|NORMAL
|
||||
String mode = body.replace("MODE=", "").trim();
|
||||
resetMode = "RESET".equals(mode);
|
||||
} else if (card == '*') {
|
||||
// コメント行は無視
|
||||
} else {
|
||||
System.out.println("WARNING: Unknown card type [" + card + "]");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("SYSIN読込失敗: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// 主処理 2000MAJSOR
|
||||
// *****************************************************************
|
||||
private void majProc() {
|
||||
// 設計書(使用DBテーブル): RESET = DELETE + INSERT
|
||||
// → DAILY_RECORDS 挿入前に既存レコードを削除(stpProc 後だと PK 違反)
|
||||
if (resetMode) {
|
||||
resetDelete();
|
||||
}
|
||||
try (BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(new FileInputStream(R01_FILE), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.trim().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
cunR01Inn++;
|
||||
Kin07Rec rec = new Kin07Rec();
|
||||
rec.fromRecordLine(line);
|
||||
|
||||
// 社員番号有効性チェック(空ならスキップ)
|
||||
if (rec.getEmpId() == null || rec.getEmpId().trim().isEmpty()) {
|
||||
System.out.println("WARNING: Empty EMP-ID record " + cunR01Inn);
|
||||
continue;
|
||||
}
|
||||
|
||||
String empId = rec.getEmpId();
|
||||
String targetDate = String.format("%08d", rec.getDate());
|
||||
String timeIn = ConvUtil.padLeftZero(rec.getTimeIn(), 4);
|
||||
String timeOut = ConvUtil.padLeftZero(rec.getTimeOut(), 4);
|
||||
double annualH = rec.getAnnualH() / 10.0;
|
||||
double personalH = rec.getPersonalH() / 10.0;
|
||||
double officialH = rec.getOfficialH() / 10.0;
|
||||
double sickH = rec.getSickH() / 10.0;
|
||||
double absentH = rec.getAbsentH() / 10.0;
|
||||
|
||||
// DAILY_RECORDS INSERT
|
||||
String insSql = "INSERT INTO DAILY_RECORDS "
|
||||
+ "(EMP_ID, TARGET_DATE, TIME_IN, TIME_OUT, ANNUAL_LEAVE_H, "
|
||||
+ "PERSONAL_LEAVE_H, OFFICIAL_LEAVE_H, SICK_LEAVE_H, "
|
||||
+ "UNAPPROVED_ABSENT_H, UPDATED_AT) VALUES "
|
||||
+ "(?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP)";
|
||||
try (PreparedStatement ps = conn.prepareStatement(insSql)) {
|
||||
ps.setString(1, empId);
|
||||
ps.setString(2, targetDate);
|
||||
ps.setString(3, timeIn);
|
||||
ps.setString(4, timeOut);
|
||||
ps.setDouble(5, annualH);
|
||||
ps.setDouble(6, personalH);
|
||||
ps.setDouble(7, officialH);
|
||||
ps.setDouble(8, sickH);
|
||||
ps.setDouble(9, absentH);
|
||||
ps.executeUpdate();
|
||||
cunDbxIns++;
|
||||
} catch (Exception e) {
|
||||
dbError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// 対象社員フィルタリング(Tカード指定時) → 集計のみスキップ
|
||||
if (!allEmp && !targetList.contains(empId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 年月抽出(YYYYMMDD→YYYYMM)
|
||||
String ym = targetDate.substring(0, 6);
|
||||
|
||||
// 集計テーブル加算
|
||||
AggRow row = findAgg(empId, ym);
|
||||
if (row == null) {
|
||||
if (aggTable.size() < 100) {
|
||||
row = new AggRow();
|
||||
row.empId = empId;
|
||||
row.yearMonth = ym;
|
||||
row.annualH = rec.getAnnualH();
|
||||
row.personalH = rec.getPersonalH();
|
||||
row.officialH = rec.getOfficialH();
|
||||
row.sickH = rec.getSickH();
|
||||
row.absentH = rec.getAbsentH();
|
||||
aggTable.add(row);
|
||||
} else {
|
||||
System.out.println("WARNING: Agg table full " + empId);
|
||||
}
|
||||
} else {
|
||||
// ANNUAL は COMPUTE ROUNDED(入力は0.1h単位のため ADD と同一結果)、
|
||||
// 他は ADD(V9(1) 切捨だが値は既に0.1h単位)→ 全て単純加算
|
||||
row.annualH += rec.getAnnualH();
|
||||
row.personalH += rec.getPersonalH();
|
||||
row.officialH += rec.getOfficialH();
|
||||
row.sickH += rec.getSickH();
|
||||
row.absentH += rec.getAbsentH();
|
||||
}
|
||||
|
||||
// 設計書: WS-TOTAL-HOURS ROUNDED = ANNUAL+PERSONAL+OFFICIAL+SICK+ABSENT
|
||||
// ON SIZE ERROR → 0 クリア + 警告
|
||||
if (row != null) {
|
||||
row.totalH = computeTotalHours(empId, ym, row.annualH, row.personalH,
|
||||
row.officialH, row.sickH, row.absentH);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("R01読込失敗: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private AggRow findAgg(final String empId, final String ym) {
|
||||
for (AggRow r : aggTable) {
|
||||
if (r.empId.equals(empId) && r.yearMonth.equals(ym)) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** RESET 時の既存レコード削除(DAILY_RECORDS + MONTHLY_ABSENCE、対象年月のみ). */
|
||||
private void resetDelete() {
|
||||
try (PreparedStatement ps = conn.prepareStatement(
|
||||
"DELETE FROM DAILY_RECORDS WHERE TARGET_DATE LIKE ?")) {
|
||||
ps.setString(1, yearMonth + "%");
|
||||
int n = ps.executeUpdate();
|
||||
cunDbxDel += n;
|
||||
} catch (Exception e) {
|
||||
dbError(e);
|
||||
return;
|
||||
}
|
||||
try (PreparedStatement ps = conn.prepareStatement(
|
||||
"DELETE FROM MONTHLY_ABSENCE WHERE YEAR_MONTH = ?")) {
|
||||
ps.setString(1, yearMonth);
|
||||
int n = ps.executeUpdate();
|
||||
cunDbxDel += n;
|
||||
} catch (Exception e) {
|
||||
dbError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 設計書 2-3: WS-TOTAL-HOURS ROUNDED = ANNUAL+PERSONAL+OFFICIAL+SICK+ABSENT。
|
||||
* 9(4)V9(1) → tenth 単位上限 99999。溢れ(ON SIZE ERROR)時は 0 クリア+警告。
|
||||
*/
|
||||
private int computeTotalHours(final String empId, final String ym,
|
||||
final int annualH, final int personalH, final int officialH,
|
||||
final int sickH, final int absentH) {
|
||||
long sum = (long) annualH + personalH + officialH + sickH + absentH;
|
||||
if (sum > 99999 || sum < -9999) {
|
||||
System.out.println("WARNING: Total hours overflow emp=" + empId + " ym=" + ym);
|
||||
return 0;
|
||||
}
|
||||
return (int) sum;
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// 終了処理 3000STPSOR
|
||||
// *****************************************************************
|
||||
private void stpProc() {
|
||||
try {
|
||||
// 集計結果 → MONTHLY_ABSENCE UPSERT
|
||||
for (AggRow row : aggTable) {
|
||||
String cntSql = "SELECT COUNT(*) FROM MONTHLY_ABSENCE "
|
||||
+ "WHERE EMP_ID = ? AND YEAR_MONTH = ?";
|
||||
int cnt;
|
||||
try (PreparedStatement ps = conn.prepareStatement(cntSql)) {
|
||||
ps.setString(1, row.empId);
|
||||
ps.setString(2, row.yearMonth);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
cnt = rs.next() ? rs.getInt(1) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt > 0) {
|
||||
String updSql = "UPDATE MONTHLY_ABSENCE SET "
|
||||
+ "ANNUAL_LEAVE_H=?, PERSONAL_LEAVE_H=?, OFFICIAL_LEAVE_H=?, "
|
||||
+ "SICK_LEAVE_H=?, UNAPPROVED_ABSENT_H=?, UPDATED_AT=CURRENT_TIMESTAMP "
|
||||
+ "WHERE EMP_ID=? AND YEAR_MONTH=?";
|
||||
try (PreparedStatement ps = conn.prepareStatement(updSql)) {
|
||||
ps.setDouble(1, row.annualH / 10.0);
|
||||
ps.setDouble(2, row.personalH / 10.0);
|
||||
ps.setDouble(3, row.officialH / 10.0);
|
||||
ps.setDouble(4, row.sickH / 10.0);
|
||||
ps.setDouble(5, row.absentH / 10.0);
|
||||
ps.setString(6, row.empId);
|
||||
ps.setString(7, row.yearMonth);
|
||||
ps.executeUpdate();
|
||||
cunDbxUpd++;
|
||||
}
|
||||
} else {
|
||||
String insSql = "INSERT INTO MONTHLY_ABSENCE "
|
||||
+ "(EMP_ID, YEAR_MONTH, ANNUAL_LEAVE_H, PERSONAL_LEAVE_H, "
|
||||
+ "OFFICIAL_LEAVE_H, SICK_LEAVE_H, UNAPPROVED_ABSENT_H, UPDATED_AT) "
|
||||
+ "VALUES (?,?,?,?,?,?,?,CURRENT_TIMESTAMP)";
|
||||
try (PreparedStatement ps = conn.prepareStatement(insSql)) {
|
||||
ps.setString(1, row.empId);
|
||||
ps.setString(2, row.yearMonth);
|
||||
ps.setDouble(3, row.annualH / 10.0);
|
||||
ps.setDouble(4, row.personalH / 10.0);
|
||||
ps.setDouble(5, row.officialH / 10.0);
|
||||
ps.setDouble(6, row.sickH / 10.0);
|
||||
ps.setDouble(7, row.absentH / 10.0);
|
||||
ps.executeUpdate();
|
||||
cunDbxIns++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ABSENCE_SUMMARY 出力
|
||||
for (AggRow row : aggTable) {
|
||||
Kin08Rec out = new Kin08Rec();
|
||||
out.setEmpId(row.empId);
|
||||
out.setYearMonth(row.yearMonth);
|
||||
out.setAnnualH(row.annualH);
|
||||
out.setPersonalH(row.personalH);
|
||||
out.setOfficialH(row.officialH);
|
||||
out.setSickH(row.sickH);
|
||||
out.setAbsentH(row.absentH);
|
||||
out.setFiller("");
|
||||
w01OutFil.write(out.toRecordLine());
|
||||
w01OutFil.newLine();
|
||||
cunW01Out++;
|
||||
}
|
||||
|
||||
// COMMIT
|
||||
conn.commit();
|
||||
} catch (Exception e) {
|
||||
dbError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// ファイルCLOSE
|
||||
try {
|
||||
if (w01OutFil != null) {
|
||||
w01OutFil.close();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
// 無視
|
||||
}
|
||||
try {
|
||||
if (conn != null) {
|
||||
conn.close();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
// 無視
|
||||
}
|
||||
|
||||
// 件数メッセージ
|
||||
msgOutPara(CNS_MSGIINKES, "KIN08R01", ConvUtil.padLeftZero(cunR01Inn, 9), "");
|
||||
msgOutPara(CNS_MSGIINKES, "INS", ConvUtil.padLeftZero(cunDbxIns, 9), "");
|
||||
msgOutPara(CNS_MSGIINKES, "UPD", ConvUtil.padLeftZero(cunDbxUpd, 9), "");
|
||||
if (cunDbxDel > 0) {
|
||||
msgOutPara(CNS_MSGOUTKES, "DEL", ConvUtil.padLeftZero(cunDbxDel, 9), "");
|
||||
}
|
||||
msgOutPara(CNS_MSGOUTKES, "KIN08W01", ConvUtil.padLeftZero(cunW01Out, 9), "");
|
||||
msgOutPara(CNS_MSGFIN, "", "", "");
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// DBエラー処理 9100DBERRSOR(ROLLBACK + ABEND)
|
||||
// *****************************************************************
|
||||
private void dbError(final Exception e) {
|
||||
System.out.println("SQL ERROR: " + e.getMessage() + " PGM=KIN08DBU");
|
||||
try {
|
||||
if (conn != null) {
|
||||
conn.rollback();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
// 無視
|
||||
}
|
||||
abend();
|
||||
}
|
||||
|
||||
/** メッセージ編集出力 4000MSGOUTSOR(SUB02MSG 呼出). */
|
||||
private void msgOutPara(final int msgCod, final String p1, final String p2, final String p3) {
|
||||
msgPar.reset();
|
||||
msgPar.setM00MsgCod(msgCod);
|
||||
msgPar.setM00Umkdats2203(CNS_KN0002);
|
||||
msgPar.setM00Umkdats2204(CNS_KN0002);
|
||||
msgPar.setM00Umkdats2205(CNS_PRGIDX);
|
||||
msgPar.setM00Umkdats2201(p1);
|
||||
msgPar.setM00Umkdats2202(p2);
|
||||
Sub02MsgSub.execute(msgPar);
|
||||
}
|
||||
|
||||
/** ABEND処理(9999ABDSOR 相当)。 */
|
||||
private void abend() {
|
||||
endPar.setE01AbdCod(CNS_ABD999);
|
||||
Sub03EndSub.execute(endPar);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user