# FUW03901SFLogic

## Purpose

`FUW03901SFLogic` is the backend business logic controller for the ISP authentication ID and password change flow in the NTT East online services web platform. It orchestrates a multi-step user journey: initially displaying a page that shows any applicable initial fees, allowing the user to confirm their password change request, and then submitting that request to the backend service. It sits between the view layer (JSP pages) and the remote business services.

## Design

This class follows the **page controller / frontend logic** pattern commonly used in the JFF-Web framework (JFUStrConst, JFUWebCommon, X31SDataBeanAccess, etc.). It extends `JCCWebBusinessLogic`, which provides the base infrastructure for calling remote business services via `invokeService()`, accessing common and service form data beans, and formatting business exceptions.

Its architectural role is a **facade + orchestrator**: it reads data from a deeply nested bean hierarchy (commonInfoBean -> webChgInfoBean -> ssoInfoBean / genCustKeiInfoBean -> svcKeiInfoBean -> svcKeiUcwkInfoBean), performs local validation (password match, re-entry consistency, identity verification), delegates to remote services via one of three use-case-specific DB mappers, and writes navigation state back into the common info bean.

```mermaid
classDiagram
  class FUW03901SFLogic {
    -static final String USECASE_ID_FUSV0075 = "FUSV0075"
    -static final String USECASE_ID_FUSV0192 = "FUSV0192"
    -static final String USECASE_ID_FUSV0259 = "FUSV0259"
    -static final String FUSV019201CC
    -static final String EKK0721A010CBSMSG1LIST
    +boolean init()
    +boolean mskm()
    +boolean chkPayInitialCost(HashMap outputMap)
    -boolean tankanrenCheck(bean, commonInfoBean)
    -boolean getMansionDiv(bean, commonInfoBean)
  }

  class JCCWebBusinessLogic {
    +getCommonInfoBean()
    +getServiceFormBean()
    +invokeService()
  }

  class X31SDataBeanAccess {
    +getDataBeanArray()
    +sendMessageString()
    +sendMessageBoolean()
  }

  FUW03901SFLogic --|> JCCWebBusinessLogic : extends
  FUW03901SFLogic --> X31SDataBeanAccess : uses
```

## Key Methods

### `init()` — Initial Page Load (lines 97–252)

This is the primary entry point when the user first navigates to the password-change screen. It performs the following sequence:

1. **Cross-site request / Orosi check** — calls `JFUWebCommon.checkOrosi(this)` for CSRF protection.

2. **Retrieve beans** — fetches the `commonInfoBean` (shared session context) and the `serviceFormBean` (this page's form data).

3. **Handle SSO redirect backflow** — If the screen ID matches `FUW08801` (the "various password change screen"), it means the user came from the SSO password change page. In that case:
   - Reads the SSO `sysId` and `ninshoId` from the common info bean.
   - Validates both are present; throws `ERROR_CODE_0002` (system error) if missing.
   - Calls `JFUWebCommon.refreshGenCustKei(this)` to refresh the current customer contract info.
   - Runs a common relation check via `JFUWebCommon.checkCommonRelation(this, "FUSV0075")`.
   - Re-fetches the common info bean after the refresh.

4. **Traverse the bean hierarchy** — navigates from `commonInfoBean -> webChgInfoBean -> ssoInfoBean` to obtain the current SSO authentication ID (`ssoInfoNinshoId`), then writes it into the service form bean under the key `NOW_NINSHO_ID`.

5. **Match authentication ID to service contract** — iterates through the service contract subsidiary info array (`svcKeiUcwkInfoArray`) to find the entry whose `ISP_NINSHO_ID_24` matches the current SSO authentication ID. If no match is found, throws `ERROR_CODE_0102` (contract status error).

6. **Contract status validation** — checks that the service contract status is neither `210` nor `220` (inactive/suspended states). If so, throws `ERROR_CODE_0102`.

7. **Call the initial-display remote service** — builds a parameter map with `USECASE_ID_FUSV0192` (initial display), creates a `FUSV0192_FUSV0192OPDBMapper`, runs the "upper mapping" to populate the data map with initial fee information (`setFUSV019201CC`) and current contract subsidiary info (`setFUSV019201SC`), then calls `invokeService()`. If `JCCWebServiceException` is caught, re-throws as `ERROR_CODE_0002`.

8. **Lower mapping and price info** — processes the output via `getFUSV019201SC`, then calls `JFUWebCommon.setPrcInfoArea()` to populate the price display area on the page.

9. **Set derived flags** — calls `chkPayInitialCost()` to determine if there are initial fees (sets `PAY_FLG`), and calls `getMansionDiv()` to determine if this is a mansion (whole-building) contract with payment (sets `MANSION_DIV`).

10. **Navigate** — sets `NEXT_SCREEN_ID` and `NEXT_SCREEN_NAME` to `FUW03901` (the confirmation page).

Returns `true` unconditionally. This method handles the GET / initial display path.

### `mskm()` — Confirmation Submit (lines 260–422)

This method handles the user pressing the "confirm" (確定) button on the password-change screen. The term "mskm" is a Japanese abbreviation for *kakunin mensetsu botton* (確認画面ボタン — confirmation screen button).

The flow is:

1. **Common relation check** — calls `JFUWebCommon.checkCommonRelation(this, "FUSV0075")` again to ensure the session context is still valid.

2. **Retrieve beans** — fetches the same bean hierarchy as `init()`: `commonInfoBean`, `webChgInfoBean`, `ssoInfoBean`, `genCustKeiInfoBean`.

3. **Validate current authentication ID** — reads `NOW_NINSHO_ID` from the service form bean. If null or empty, throws `ERROR_CODE_0102` (contract status error).

4. **Run subsidiary consistency check** — calls `tankanrenCheck(bean, commonInfoBean)`. If this returns `false` (indicating a validation error that was already displayed to the user via `setMessageInfo`), the method returns `true` early, leaving the user on the same screen to correct their input.

5. **Determine service type** — reads the `PRC_GRP_CD_23` (price group code) from the service contract info:
   - **If NOT ADSL** (code != `CD00133_06`): uses the `FUSV0075` use case. Invokes a chain of 11 DB mapper methods:
     - `setFUSV007501CC` — authentication ID/password existence check
     - `setFUSV007501SC` — authentication ID list matching
     - `setFUSV007502SC` — authentication ID/password auth list 1
     - `setFUSV007503SC` — authentication ID/password auth list 2
     - `setFUSV007504SC` — submission content acceptance registration
     - `setFUSV007505SC` — ISP authentication ID password change
     - `setFUSV007506SC` — progress registration
     - `setFUSV007507SC` — submission details check / follow-up task
     - `setFUSV007502CC` / `03CC` / `04CC` — service IF result data transfer (1, 2, 3)
     - `setFUSV007505CC` — service audit issuance check
   - **If ADSL** (code == `CD00133_06`): uses the `FUSV0259` use case. Invokes only `setFUSV025901CC`.

6. **Call the remote service** — invokes `invokeService()` with the populated maps. If successful and a `MessageResult` is returned, sets the message info and returns.

7. **Exception handling** — if `JCCWebServiceException` is thrown, extracts detailed error information from the `MessageMoreInfo` list and dispatches:
   - If it's a relation check error with template `EKK0171C012` and item `svc_kei_ucwk_no`: throws `ERROR_CODE_0204` (update impossible error).
   - If it's template `EKK0171B002` with item `key_isp_ninsho_id_pwd`: displays the "invalid password" message pointing to `now_pwd`, returns early.
   - If it's template `EKK0171A010` with item `key_svc_kei_ucwk_no`: throws `ERROR_CODE_0002` (system error, subsidiary contract does not exist).
   - Otherwise: throws `ERROR_CODE_0002` as a catch-all.

8. **Navigate** — sets `NEXT_SCREEN_ID` to `FUW03902` (the submission completion/acceptance screen).

Returns `true` unconditionally. This method handles the POST / submit path.

### `tankanrenCheck()` — Password Consistency Validation (lines 432–500)

A private helper called from `mskm()` that performs client-side-style password validation on the server. It validates three conditions:

1. **Current password matches the stored value** — looks up the service contract subsidiary entry whose `ISP_NINSHO_ID_24` matches the current SSO authentication ID (same matching logic as in `init()`), retrieves the stored password (`ISP_NINSHO_ID_PWD_24`), and compares it against the `NOW_NINSHO_ID_PWD` entered by the user. If they don't match, sets an error message (`EKF0400_TW` with `REPLACE_CHAR_PWD_ARRAY`) pointing to `now_pwd` and returns `false`.

2. **New password re-entry matches** — compares `CHG_NINSHO_ID_PWD` (new password) against `RE_INPUT_CHG_NINSHO_ID_PWD` (re-typed new password). If they don't match, sets an error (`EKF0400_TW` with `RE_INPUT_CHG_PWD_KANREN_MSG`) pointing to `re_input_chg_pwd` and returns `false`.

3. **New password is different from current** — if `NOW_NINSHO_ID_PWD` equals `CHG_NINSHO_ID_PWD`, sets an error (`EKF0270_NW` with `CHG_PWD_KANREN_MSG`) pointing to `chg_pwd` and returns `false`. This prevents the user from "changing" to the same password.

Returns `true` if all three validations pass.

### `getMansionDiv()` — Mansion Division Flag (lines 510–532)

Determines whether the user has a "mansion (whole-building) with payment" scenario, which controls whether certain paid content should be displayed. Returns `true` only when all three conditions are met:

- `PRC_GRP_CD_23` equals `CD00133_04` (mansion/whole-building price group)
- `KANYU_KEI_PAY_HOSHIKI_CD_23` equals `CD01216_003` (subscription payment method)
- `PAY_FLG` (initial fee flag) is `true`

Otherwise returns `false`.

### `chkPayInitialCost()` — Initial Fee Detection (lines 540–568)

Extracts whether there are any initial fees from the remote service output map. Navigates:

1. Gets the `FUSV019201CC` entry from `outputMap` (upper mapping result).
2. Gets the `EKK0721A010CBSMsg1List` entry from that map (child cost list).
3. If the list is non-null and non-empty, returns `true` (initial fees exist); otherwise `false`.

The method uses raw `HashMap` and `ArrayList` types (with `@SuppressWarnings("unchecked")`) since the output map carries arbitrary business data from the remote service.

## Relationships

### Inbound (who depends on this class)

```mermaid
flowchart TD
    A["WEBGAMEN_FUW039010PJP"] -->|"calls init / mskm"| B["FUW03901SFLogic"]
    C["WEBGAMEN_FUW039020PJP"] -->|"calls init / mskm"| B
    D["x31business_logic_FUW03901SF"] -->|"references"| B
```

- **WEBGAMEN_FUW039010PJP.xml** — The page controller for the password-change initial screen (FUW03901). Calls `init()` on page load and `mskm()` on form submit.
- **WEBGAMEN_FUW039020PJP.xml** — The page controller for the post-submission confirmation screen (FUW03902). Receives navigation from `mskm()` and likely calls `init()` for display.
- **x31business_logic_FUW03901SF.xml** — The business logic routing configuration, which wires this logic class to the appropriate screens and use cases.

### Outbound (dependencies)

- **`JCCWebBusinessLogic`** — The parent class providing `getCommonInfoBean()`, `getServiceFormBean()`, `invokeService()`, and exception handling utilities. This is the core extension point for all web-facing business logic classes in the framework.

- **`FUSV0192_FUSV0192OPDBMapper`** — Used in `init()` for the initial display use case (FUSV0192). Handles mapping of initial fee info and current contract subsidiary data.

- **`FUSV0075_FUSV0075OPDBMapper`** — Used in `mskm()` for the non-ADSL password change submission flow (FUSV0075). A large chain of 11 mapper methods covering existence checks, auth, submission registration, data transfer, and audit.

- **`FUSV0259_FUSV0259OPDBMapper`** — Used in `mskm()` for the ADSL variant of the password change flow (FUSV0259).

## Usage Example

A typical request lifecycle:

1. User navigates to the password change screen. The web framework instantiates `FUW03901SFLogic` and calls `init()`.
2. `init()` performs the SSO identity lookup, validates the contract is active, calls the `FUSV0192` service to fetch initial fee data, populates the form bean with fee info and derived flags, and sets the next screen to `FUW03901`.
3. The user sees the page with the current authentication ID, password change fields (current password, new password, re-enter new password), and any initial fee information.
4. User enters their details and clicks the confirmation button. The framework calls `mskm()`.
5. `mskm()` validates the current password and new password via `tankanrenCheck()`. If validation fails, error messages are displayed and the user stays on the screen.
6. If validation passes, `mskm()` dispatches to either the FUSV0075 (standard) or FUSV0259 (ADSL) service, executes a chain of business operations (existence check, auth, submission, progress registration, data transfer, audit), and sets the next screen to `FUW03902`.
7. The user is redirected to the submission completion screen (FUW03902).

## Notes for Developers

- **Thread safety** — This class has no instance fields (all state is local to method parameters or the base class's bean access). Each request gets a new instance, so it is inherently thread-safe.

- **Error handling is exception-heavy** — Most error conditions throw `JCCBusinessException` with specific error codes (`0002` for system errors, `0102` for contract status errors, `0204` for update-impossible). The `mskm()` method also handles `JCCWebServiceException` by extracting structured error details from the remote service and dispatching to specific UI messages. Be careful not to swallow exceptions in overrides.

- **Japanese abbreviation naming** — The method name `mskm` is not self-documenting. It stands for "kakunin mensetsu botton" (確認画面ボタン). The method `tankanrenCheck` stands for "tanka kanren check" (単関連チェック — subsidiary consistency check). These names are common in this codebase and should be treated as fixed identifiers.

- **Service type branching** — In `mskm()`, the code splits between FUSV0075 and FUSV0259 based on `PRC_GRP_CD_23 == "06"` (ADSL). This is a product-line branch — standard internet contracts use one service chain, ADSL contracts use another. Any new service type additions would need a new branch here.

- **Password validation happens at the logic layer, not the view layer** — `tankanrenCheck()` performs all three password checks (current password correctness, new-password re-entry consistency, no-same-password) on the server side. Even if client-side JavaScript exists, the server-side check is authoritative.

- **Navigation is imperative** — The class writes directly to `commonInfoBean` with `NEXT_SCREEN_ID` and `NEXT_SCREEN_NAME`. This is how the framework decides where to redirect. Modifying the navigation target requires updating both `NEXT_SCREEN_ID` and `NEXT_SCREEN_NAME` consistently.

- **The `chkPayInitialCost()` method uses unchecked casts** — It accesses arbitrary keys from a `HashMap` without type safety. The keys (`FUSV019201CC`, `EKK0721A010CBSMsg1List`) are string constants, but the values are raw `HashMap` and `ArrayList`. This is a known risk if the remote service contract changes without corresponding code updates.

- **Static constants for error message configuration** — The class embeds Japanese error message text arrays (e.g., `RE_INPUT_CHG_PWD_KANREN_MSG`, `REPLACE_CHAR_PWD_ARRAY`) that are used with `JFUWebCommon.setMessageInfo()` along with error templates and item IDs. These are tightly coupled to the i18n/message configuration in the broader framework and should not be modified without verifying the message infrastructure.
