# Business Logic — JBSbatKKAdChgTekkyoKjFinUpd.timeStampCheckSvcExecHaita() [24 LOC]

| Field | Value |
|-------|-------|
| Fully Qualified Name | `eo.business.service.JBSbatKKAdChgTekkyoKjFinUpd` |
| Layer | Service (Batch) |
| Module | `service` (Package: `eo.business.service`) |

## 1. Role

### JBSbatKKAdChgTekkyoKjFinUpd.timeStampCheckSvcExecHaita()

This method implements a **timestamp-based optimistic concurrency control** check for service contracts (サービス契約) in the context of the "Residential Change Mid-Process Removal Work Completion Registration" batch — a K-Opticom process that registers the completion of work removals during residential address changes. For each service contract number provided in the input list, the method queries the latest `LAST_UPD_DTM` (last update datetime) from the `KK_T_SVKEI_EXC_CTRL` table and compares it against the previously captured value. If the timestamps match (indicating no concurrent modification by another batch thread), the method proceeds to acquire a pessimistic row-level lock via `selectByPrimaryKeysForUpdateWait` on that record. If any timestamp mismatch is detected — meaning another process has modified the record since the initial read — the method logs a business error (error code `EKKB0360KE`) and immediately returns `false`, halting the calling batch flow. This method serves as a **shared concurrency gate** used by `execute()` to protect parallel service contract records from lost updates during batch completion registration.

## 2. Processing Pattern (Detailed Business Logic)

```mermaid
flowchart TD
    START(["timeStampCheckSvcExecHaita(svcKeiNoList, lastUpdateList)"])
    LOG_START["printDebugLog('タイムスタンプチェック_START')"]
    LOOP_START["for i = 0 to svcKeiNoList.size() - 1"]
    GET_SVC["svcKeiNo = svcKeiNoList.get(i)
lastUpdateBf = lastUpdateList.get(i)"]
    SEARCH["searchSvkeiExcCtrl(svcKeiNo)"]
    GET_CUR_DTM["outMap.getString(JBSbatKK_T_SVKEI_EXC_CTRL.LAST_UPD_DTM)
JBSbatStringUtil.Rtrim()"]
    TS_CHECK["isTimeStampCheck(svcKeiNo, lastUpdateBf, currentLastUpdDtm)"]
    CHECK_NULL{isTimeStampCheck
== null?}
    ERROR_LOG["printBusinessErrorLog('EKKB0360KE',
['サービス契約排他制御TBL', svcKeiNo])"]
    DEBUG_ERR["printDebugLog('住所変更中撤去工事完了登録処理で排他エラーが発生しました。')"]
    RETURN_FALSE["return false"]
    NEXT_ITER["i++
continue"]
    LOG_END["printDebugLog('タイムスタンプチェック_END')"]
    RETURN_TRUE["return true"]
    EXIT(["Exit: true"])

    START --> LOG_START
    LOG_START --> LOOP_START
    LOOP_START --> GET_SVC
    GET_SVC --> SEARCH
    SEARCH --> GET_CUR_DTM
    GET_CUR_DTM --> TS_CHECK
    TS_CHECK --> CHECK_NULL
    CHECK_NULL -- yes (null returned) --> ERROR_LOG
    ERROR_LOG --> DEBUG_ERR
    DEBUG_ERR --> RETURN_FALSE
    RETURN_FALSE --> EXIT
    CHECK_NULL -- no (valid dbmap) --> NEXT_ITER
    NEXT_ITER --> LOOP_START
    LOOP_START -- loop complete --> LOG_END
    LOG_END --> RETURN_TRUE
    RETURN_TRUE --> EXIT
```

**Processing overview:**

1. The method enters and logs a debug start marker.
2. It iterates over every index of the `svcKeiNoList`, retrieving the corresponding `svcKeiNo` and `lastUpdateBf` (previous last-update datetime) from both parallel lists.
3. For each service contract number, it queries `KK_T_SVKEI_EXC_CTRL` via `searchSvkeiExcCtrl()` to fetch the current row, then extracts the latest `LAST_UPD_DTM` value (with whitespace trimmed).
4. It delegates to `isTimeStampCheck()` with three arguments: the service contract number, the previous timestamp, and the freshly read current timestamp.
5. Inside `isTimeStampCheck()`, if the two timestamps are **equal** (`equals` returns `true`), it performs `selectByPrimaryKeysForUpdateWait()` — acquiring a pessimistic lock with wait — and returns the locked row. If the timestamps **differ**, it returns `null` (conflict signal).
6. Back in `timeStampCheckSvcExecHaita()`, if `isTimeStampCheck()` returns `null` (conflict detected), it logs a business error with code `EKKB0360KE`, logs a Japanese debug message explaining the exclusion error, and returns `false` — terminating the entire batch operation.
7. If no conflicts occur across all iterations, it logs the end marker and returns `true`.

## 3. Parameter Analysis

| No | Parameter Name | Type | Business Description |
|----|---------------|------|---------------------|
| 1 | `svcKeiNoList` | `List<String>` | List of service contract numbers (サービス契約番号) to be checked for concurrent modification. Each entry identifies a unique service contract line item (e.g., FTTH, Mail, TV) that the batch is completing removal work for. The list is 1:1 aligned with `lastUpdateList` by index. |
| 2 | `lastUpdateList` | `List<String>` | List of previously captured "last update datetime" values (最終更新年月日時分秒). These are the baseline timestamps from when the service contract records were initially read earlier in the batch flow. Used as the "before" value in the optimistic concurrency comparison. |

**Instance fields / external state read:**

| Field | Type | Business Description |
|-------|------|---------------------|
| `super.logPrint` | Debug/Business logger | Inherited logging utility used for debug and business error log output |

## 4. CRUD Operations / Called Services

| CRUD | SC / CBS | SC Code | Entity / DB | Operation Description |
|------|----------|---------|-------------|----------------------|
| - | `JBSbatStringUtil.Rtrim` | JBSbatStringUtil | - | Trims trailing whitespace from the last update datetime string extracted from the DB result map |
| - | `JBSbatKKAdChgTekkyoKjFinUpd.searchSvkeiExcCtrl(String)` | JBSbatKKAdChgTekkyoKjFinUpd | - | Queries the service contract exclusion control table by primary key (service contract number) to retrieve the current record |
| R | `JBSbatKKAdChgTekkyoKjFinUpd.isTimeStampCheck` | JBSbatKKAdChgTekkyoKjFinUpd | KK_T_SVKEI_EXC_CTRL | Compares the previous timestamp against the current one; if equal, acquires a pessimistic lock via `selectByPrimaryKeysForUpdateWait` and returns the locked record |
| - | `JBSbatKKAdChgTekkyoKjFinUpd.printDebugLog` | JBSbatKKAdChgTekkyoKjFinUpd | - | Writes debug-level log messages for start/end markers and error conditions |
| - | `JBSbatKKAdChgTekkyoKjFinUpd.printBusinessErrorLog` | JBSbatKKAdChgTekkyoKjFinUpd | - | Writes a business-level error log (code `EKKB0360KE`) when a concurrency conflict is detected, identifying the table and the offending service contract number |

## 5. Dependency Trace

| # | Caller (Screen/Batch) | Call Chain (Full Path to this Method) | Terminal (SC / CRUD / Entity) |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | Batch: JBSbatKKAdChgTekkyoKjFinUpd | `JBSbatKKAdChgTekkyoKjFinUpd.execute()` -> `timeStampCheckSvcExecHaita(svcKeiNoList, svKeiExcLastUpdateList)` | `searchSvkeiExcCtrl [R] KK_T_SVKEI_EXC_CTRL` |
| 2 | Batch: JBSbatKKAdChgTekkyoKjFinUpd | `execute()` -> `timeStampCheckSvcExecHaita()` -> `isTimeStampCheck()` -> `selectByPrimaryKeysForUpdateWait [R] KK_T_SVKEI_EXC_CTRL` | `isTimeStampCheck [R] KK_T_SVKEI_EXC_CTRL` |

**Caller details:**

The method is called exclusively from `JBSbatKKAdChgTekkyoKjFinUpd.execute()` — the batch's main processing method. The call chain within `execute()`:
1. `execute()` reads input and validates service contract numbers.
2. It queries related tables (`KK_T_MSKM_DTL`, `KK_T_IDO_RSV`) to find associated service contracts.
3. For each batch of service contracts, it first calls `searchSvkeiExcCtrl(List, List)` to populate `svKeiExcLastUpdateList` with the current last-update timestamps.
4. It then calls `timeStampCheckSvcExecHaita(svcKeiNoList, svKeiExcLastUpdateList)` to validate concurrency and acquire locks.
5. If `timeStampCheckSvcExecHaita()` returns `false`, `execute()` returns `null` (batch abort).
6. If `true`, processing continues with downstream work.

**Terminal operations reached from this method:**
- `searchSvkeiExcCtrl` — R on `KK_T_SVKEI_EXC_CTRL` (select by primary key)
- `isTimeStampCheck` — R on `KK_T_SVKEI_EXC_CTRL` (select by primary key with FOR UPDATE WAIT / pessimistic lock)
- `printBusinessErrorLog` — Error log with code `EKKB0360KE`
- `printDebugLog` — Debug trace entries

## 6. Per-Branch Detail Blocks

**Block 1** — [LOG] (L664)

> Log the start of the timestamp check phase.

| # | Type | Code |
|---|------|------|
| 1 | EXEC | `super.logPrint.printDebugLog("タイムスタンプチェック_START")` |

**Block 2** — [FOR LOOP] `(i=0; i < svcKeiNoList.size(); i++)` (L666)

> Iterate over each service contract number and its corresponding previous last-update timestamp.

| # | Type | Code |
|---|------|------|
| 1 | SET | `svcKeiNo = svcKeiNoList.get(i)` |
| 2 | SET | `lastUpdateBf = lastUpdateList.get(i)` |
| 3 | CALL | `searchSvkeiExcCtrl(svcKeiNo)` — Query KK_T_SVKEI_EXC_CTRL table by PK to get current record [R KK_T_SVKEI_EXC_CTRL] |
| 4 | SET | `outMap = searchSvkeiExcCtrl(svcKeiNo)` |
| 5 | EXEC | `outMap.getString(JBSbatKK_T_SVKEI_EXC_CTRL.LAST_UPD_DTM)` — Extract the current last update datetime from the DB result map |
| 6 | EXEC | `JBSbatStringUtil.Rtrim(...)` — Trim whitespace from the datetime string |
| 7 | CALL | `isTimeStampCheck(svcKeiNo, lastUpdateBf, trimmedLastUpdDtm)` — Delegate concurrency check [Block 2.1] |

**Block 2.1** — [IF] `(null == isTimeStampCheck(...))` (L675)

> Concurrency conflict detected. The previously captured timestamp no longer matches the current database value, indicating a concurrent modification by another batch process. This block logs the error and aborts the batch.

| # | Type | Code |
|---|------|------|
| 1 | EXEC | `super.logPrint.printBusinessErrorLog("EKKB0360KE", new String[]{"サービス契約排他制御TBL", svcKeiNo})` — Log business error with error code and context |
| 2 | EXEC | `super.logPrint.printDebugLog("住所変更中撤去工事完了登録処理で排他エラーが発生しました。{SVC_KEI_NO:" + svcKeiNo + "}")` // Log Japanese debug message: "An exclusion error occurred in the residential change mid-process removal work completion registration processing." |
| 3 | RETURN | `return false` — Abort the batch operation |

**Block 3** — [FOR LOOP EXIT] (L682)

> All service contract numbers have been checked without any concurrency conflicts.

| # | Type | Code |
|---|------|------|
| 1 | EXEC | `super.logPrint.printDebugLog("タイムスタンプチェック_END")` |
| 2 | RETURN | `return true` — All timestamp checks passed; caller may proceed |

### Sub-block: `isTimeStampCheck(svcKeiNo, lastUpdDtmStrBf, lastUpdDtmStrAf)`

This is a private helper method called from Block 2.

**Block 3.1** — [IF] `(super.logPrint.chkLogLevel(JBSbatLogUtil.MODE_DEBUG))` (L716)

> If debug logging is enabled, log the initial-search timestamp and the current timestamp for diagnostic purposes.

| # | Type | Code |
|---|------|------|
| 1 | EXEC | `super.logPrint.printDebugLog("----更新年月日時分秒(初期検索)：" + lastUpdDtmStrBf)` // "Last update datetime (initial search)" |
| 2 | EXEC | `super.logPrint.printDebugLog("----更新年月日時分秒(直前値)：" + lastUpdDtmStrAf)` // "Last update datetime (current value)" |

**Block 3.2** — [IF] `(lastUpdDtmStrAf.equals(lastUpdDtmStrBf))` (L726)

> **Timestamps match** — No concurrent modification detected. Acquire a pessimistic lock on the service contract exclusion control record using `selectByPrimaryKeysForUpdateWait`, which waits if the row is locked by another transaction. This provides two-layer protection: optimistic check first, then pessimistic lock if the check passes.

| # | Type | Code |
|---|------|------|
| 1 | SET | `svkeiExcCtrlMap = new JBSbatCommonDBInterface()` |
| 2 | SET | `svkeiExcCtrlMap.setValue(JBSbatKK_T_SVKEI_EXC_CTRL.SVC_KEI_NO, svcKeiNo)` |
| 3 | CALL | `db_KK_T_SVKEI_EXC_CTRL.selectByPrimaryKeysForUpdateWait(svkeiExcCtrlMap)` — Acquire pessimistic lock with wait [R KK_T_SVKEI_EXC_CTRL, FOR UPDATE] |
| 4 | IF (debug) | `super.logPrint.chkLogLevel(JBSbatLogUtil.MODE_DEBUG)` |
| 5 | EXEC | `super.logPrint.printDebugLog("タイムスタンプチェック結果_OK")` // "Timestamp check result: OK" |
| 6 | RETURN | `dbmap` — Return the locked database map |

**Block 3.3** — [ELSE — timestamps differ] (L738)

> **Timestamps differ** — Concurrent modification detected. The record was modified by another process since the initial read. Return `null` to signal failure to the caller.

| # | Type | Code |
|---|------|------|
| 1 | IF (debug) | `super.logPrint.chkLogLevel(JBSbatLogUtil.MODE_DEBUG)` |
| 2 | EXEC | `super.logPrint.printDebugLog("タイムスタンプチェック結果_NG")` // "Timestamp check result: NG" |
| 3 | RETURN | `null` — Signal concurrency conflict to caller |

## 7. Glossary

| Term | Type | Business Meaning |
|------|------|------------------|
| `svcKeiNo` / `SVC_KEI_NO` | Field | Service contract number — unique identifier for a service contract line item (e.g., FTTH subscription, Mail plan, TV package) |
| `LAST_UPD_DTM` / `lastUpdDtm` | Field | Last update datetime (年月日時分秒 = year/month/day/hour/minute/second) — tracks when a service contract record was last modified; used for concurrency control |
| サービス契約排他制御 (Svkei Exc Ctrl) | Domain term | Service contract exclusion control — a concurrency management mechanism that prevents two batch processes from simultaneously modifying the same service contract record |
| タイムスタンプチェック (Timestamp Check) | Domain term | Timestamp-based optimistic concurrency check — compares a previously read timestamp against the current database value to detect concurrent modifications |
| 最終更新 (Saishuu Koushin) | Japanese term | Final update / last update — the most recent modification timestamp of a record |
| 住所変更中撤去工事完了登録 (Jusho Henkou Chu Tetsu Koi Shuu Kanno Toroku) | Japanese term | Residential Change Mid-Process Removal Work Completion Registration — the batch job's business purpose: registering the completion of equipment removal during a customer's address change |
| KK_T_SVKEI_EXC_CTRL | DB Table | Service Contract Exclusion Control table — stores service contract numbers and their last update datetimes; serves as the concurrency control data store |
| `selectByPrimaryKeysForUpdateWait` | Technical method | Database-level pessimistic locking — selects a row by primary key and acquires an exclusive lock, waiting if the row is currently locked by another transaction |
| `EKKB0360KE` | Error Code | Business error code for service contract exclusion control table errors (conflict detection) |
| JBSbatCommonDBInterface | Technical type | Generic database result map interface — provides `getString()` to extract column values from a DB query result |
| JBSbatSQLAccess | Technical type | Database access wrapper — provides `selectByPrimaryKeys`, `selectByPrimaryKeysForUpdateWait`, and `updateByPrimaryKeys` methods for DB operations |
| Rtrim | Technical method | Right-trim utility — removes trailing whitespace characters from a string value |
