diff --git a/Jenkinsfile b/Jenkinsfile index 72311d6db..478054b9e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ def serviceModules = [ - 'gateway', 'auth', 'iam', 'firstleg', 'order', 'warehouse', 'file', 'print', + 'gateway', 'auth', 'iam', 'firstleg', 'finance', 'order', 'warehouse', 'file', 'print', 'integration-service', 'event-bridge-service', 'audit-service' ] @@ -8,6 +8,7 @@ def serviceCheckboxes = [ 'auth': 'SERVICE_AUTH', 'iam': 'SERVICE_IAM', 'firstleg': 'SERVICE_FIRSTLEG', + 'finance': 'SERVICE_FINANCE', 'order': 'SERVICE_ORDER', 'warehouse': 'SERVICE_WAREHOUSE', 'file': 'SERVICE_FILE', @@ -41,6 +42,7 @@ pipeline { booleanParam(name: 'SERVICE_AUTH', defaultValue: false, description: '统一认证服务(auth)') booleanParam(name: 'SERVICE_IAM', defaultValue: false, description: '租户与权限服务(iam)') booleanParam(name: 'SERVICE_FIRSTLEG', defaultValue: false, description: '头程履约服务(firstleg)') + booleanParam(name: 'SERVICE_FINANCE', defaultValue: false, description: '财务服务(finance)') booleanParam(name: 'SERVICE_ORDER', defaultValue: false, description: '订单服务(order)') booleanParam(name: 'SERVICE_WAREHOUSE', defaultValue: false, description: '仓库与往来单位服务(warehouse)') booleanParam(name: 'SERVICE_FILE', defaultValue: false, description: '文件中心(file)') diff --git a/Jenkinsfile.service b/Jenkinsfile.service index c7e78b586..d3d472d7c 100644 --- a/Jenkinsfile.service +++ b/Jenkinsfile.service @@ -1,5 +1,5 @@ def serviceModules = [ - 'gateway', 'auth', 'iam', 'firstleg', 'order', 'warehouse', 'file', 'print', + 'gateway', 'auth', 'iam', 'firstleg', 'finance', 'order', 'warehouse', 'file', 'print', 'integration-service', 'event-bridge-service', 'audit-service' ] @@ -8,6 +8,7 @@ def serviceJars = [ 'auth': 'auth/target/auth-service-1.0.0-SNAPSHOT.jar', 'iam': 'iam/target/iam-service-1.0.0-SNAPSHOT.jar', 'firstleg': 'firstleg/target/firstleg-service-1.0.0-SNAPSHOT.jar', + 'finance': 'finance/target/finance-service-1.0.0-SNAPSHOT.jar', 'order': 'order/target/order-service-1.0.0-SNAPSHOT.jar', 'warehouse': 'warehouse/target/warehouse-service-1.0.0-SNAPSHOT.jar', 'file': 'file/target/file-service-1.0.0-SNAPSHOT.jar', diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 6fd9ea08f..e8f8ef29f 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -86,6 +86,19 @@ services: FIRSTLEG_DB_USERNAME: ${FIRSTLEG_DB_USERNAME} FIRSTLEG_DB_PASSWORD: ${FIRSTLEG_DB_PASSWORD} + finance: + <<: *common + image: ${IMAGE_REPOSITORY}/finance:${IMAGE_TAG} + container_name: firstleg-${DEPLOY_ENV:-test}-finance + ports: ["${FINANCE_PORT:-9203}:9203"] + volumes: ["${LOG_ROOT:-/data/logs/baiyue-firstleg}/finance:/app/logs"] + environment: + <<: *common-environment + SERVER_PORT: 9203 + FINANCE_DB_URL: ${FINANCE_DB_URL} + FINANCE_DB_USERNAME: ${FINANCE_DB_USERNAME} + FINANCE_DB_PASSWORD: ${FINANCE_DB_PASSWORD} + order: <<: *common image: ${IMAGE_REPOSITORY}/order:${IMAGE_TAG} diff --git a/deploy/scripts/deploy-services.sh b/deploy/scripts/deploy-services.sh index 9ab70dcf2..d29243ad8 100644 --- a/deploy/scripts/deploy-services.sh +++ b/deploy/scripts/deploy-services.sh @@ -27,6 +27,17 @@ env_file_value() { sed -n "s/^$1=//p" "$ENV_FILE" | tail -n 1 | tr -d '\r' } +case " $SERVICES " in + *" finance "*) + for key in FINANCE_DB_URL FINANCE_DB_USERNAME FINANCE_DB_PASSWORD; do + if ! env_file_has_key "$key" || [ -z "$(env_file_value "$key")" ]; then + echo "部署 finance 服务时,环境文件必须包含非空 $key" >&2 + exit 2 + fi + done + ;; +esac + case " $SERVICES " in *" file "*) if ! env_file_has_key ROCKETMQ_NAME_SERVER || [ -z "$(env_file_value ROCKETMQ_NAME_SERVER)" ]; then diff --git a/deploy/service-map.yml b/deploy/service-map.yml index bfd0c98cb..7b8008371 100644 --- a/deploy/service-map.yml +++ b/deploy/service-map.yml @@ -43,7 +43,7 @@ services: artifactId: audit-service jar: audit-service/target/audit-service-1.0.0-SNAPSHOT.jar port: 9700 - finance-service: + finance: artifactId: finance-service jar: finance/target/finance-service-1.0.0-SNAPSHOT.jar port: 9203 diff --git a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformLoadingPdaController.java b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformLoadingPdaController.java index 9a7f5ffbd..f21a58484 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformLoadingPdaController.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformLoadingPdaController.java @@ -11,6 +11,7 @@ import com.baiyue.firstleg.dto.LoadingPickDtos.ReviewRequest; import com.baiyue.firstleg.dto.LoadingPickDtos.Operation; import com.baiyue.firstleg.dto.LoadingPickDtos.BatchRequest; import com.baiyue.firstleg.dto.LoadingPickDtos.InventorySnapshot; +import com.baiyue.firstleg.dto.LoadingPickDtos.PendingTask; import java.util.List; import com.baiyue.firstleg.dto.LoadingPickDtos.Task; import com.baiyue.firstleg.service.FirstlegScopeGuard; @@ -49,6 +50,15 @@ public class PlatformLoadingPdaController { return ApiResponse.success(service.task(containerNo, warehouseId)); } + @GetMapping("/tasks/pending") + @PreAuthorize("hasAuthority('platform:firstleg:pda:view')") + public ApiResponse> pendingTasks(@AuthenticationPrincipal AuthenticatedUser user, + @RequestParam(required = false) + @Size(max = 64) String keyword) { + FirstlegScopeGuard.platform(user); + return ApiResponse.success(service.pendingTasks(user.userId(), keyword)); + } + @GetMapping("/tasks/{taskId}/inventory-snapshot") @PreAuthorize("hasAuthority('platform:firstleg:pda:view')") public ApiResponse inventorySnapshot(@AuthenticationPrincipal AuthenticatedUser user, diff --git a/firstleg/src/main/java/com/baiyue/firstleg/dto/LoadingPickDtos.java b/firstleg/src/main/java/com/baiyue/firstleg/dto/LoadingPickDtos.java index 536fd8f9a..4ec527551 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/dto/LoadingPickDtos.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/dto/LoadingPickDtos.java @@ -99,6 +99,7 @@ public final class LoadingPickDtos { } public record PickItem(Long id, Long loadingPlanItemId, Long inboundId, Long inboundDetailId, + String orderNo, String inboundCode, String cargoName, List boxCodes, Integer plannedPackageCount, Integer pickedPackageCount, String itemStatus, Integer version) { } @@ -120,6 +121,13 @@ public final class LoadingPickDtos { Review review, List items) { } + /** PDA 工作台只展示当前作业员可领取或已领取的进行中任务。 */ + public record PendingTask(Long taskId, Long loadingPlanId, String containerNo, + Long warehouseId, String warehouseName, String taskStatus, + String workflowStatus, Integer version, Integer assignmentVersion, + Integer itemCount, Integer pendingItemCount) { + } + public record InventoryResource(Long reservationId, Long occupancyId, Long palletId, Long locationId, String locationCode, Boolean sharedOverflow, Integer availablePackageCount) { diff --git a/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingPickItemMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingPickItemMapper.java index a2de1a488..cada43494 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingPickItemMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingPickItemMapper.java @@ -7,8 +7,32 @@ import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; +import java.util.List; + @Mapper public interface LoadingPickItemMapper extends BaseMapper { + @Select(""" + SELECT pick_item.id AS pickItemId, plan_item.order_no AS orderNo, + plan_item.inbound_code AS inboundCode, label.cargo_name AS cargoName, + label.box_code AS boxCode + FROM fl_loading_pick_item pick_item + JOIN fl_loading_plan_item plan_item ON plan_item.id = pick_item.loading_plan_item_id + JOIN fl_inbound_carton_label label ON label.tenant_id = plan_item.account_id + AND label.inbound_id = plan_item.inbound_id + AND label.inbound_detail_id = plan_item.inbound_detail_id + WHERE pick_item.pick_task_id = #{taskId} AND plan_item.deleted = 0 + ORDER BY pick_item.id, label.carton_index + """) + List selectTaskCartonLabels(@Param("taskId") long taskId); + + interface CartonLabelRow { + Long getPickItemId(); + String getOrderNo(); + String getInboundCode(); + String getCargoName(); + String getBoxCode(); + } + @Update("UPDATE fl_loading_plan_item SET package_count=#{pickedCount},updated_by=#{operatorId}," + "updated_by_name=#{operatorName},version=version+1 WHERE id=#{planItemId} AND deleted=0 " + "AND package_count=#{plannedCount}") diff --git a/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingPickTaskMapper.java b/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingPickTaskMapper.java index b64df8b23..596f07889 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingPickTaskMapper.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingPickTaskMapper.java @@ -7,6 +7,8 @@ import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; +import java.util.List; + @Mapper public interface LoadingPickTaskMapper extends BaseMapper { @Select("SELECT * FROM fl_loading_pick_task WHERE id=#{taskId} FOR UPDATE") @@ -28,4 +30,48 @@ public interface LoadingPickTaskMapper extends BaseMapper """) LoadingPickTaskEntity selectLoadingByContainerAndAccount(@Param("containerNo") String containerNo, @Param("warehouseId") long warehouseId); + + @Select(""" + + """) + List selectPendingForOperator(@Param("operatorId") long operatorId, + @Param("keyword") String keyword); + + interface PendingTaskRow { + Long getTaskId(); + Long getLoadingPlanId(); + String getContainerNo(); + Long getWarehouseId(); + String getWarehouseName(); + String getTaskStatus(); + String getWorkflowStatus(); + Integer getVersion(); + Integer getAssignmentVersion(); + Integer getItemCount(); + Integer getPendingItemCount(); + } } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/repository/LoadingPickRepository.java b/firstleg/src/main/java/com/baiyue/firstleg/repository/LoadingPickRepository.java index 34b314a46..aa9d472ac 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/repository/LoadingPickRepository.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/repository/LoadingPickRepository.java @@ -7,7 +7,9 @@ import com.baiyue.firstleg.entity.LoadingPickTaskEntity; import com.baiyue.firstleg.mapper.LoadingPickHistoryMapper; import com.baiyue.firstleg.entity.LoadingPlanItemEntity; import com.baiyue.firstleg.mapper.LoadingPickItemMapper; +import com.baiyue.firstleg.mapper.LoadingPickItemMapper.CartonLabelRow; import com.baiyue.firstleg.mapper.LoadingPickTaskMapper; +import com.baiyue.firstleg.mapper.LoadingPickTaskMapper.PendingTaskRow; import com.baiyue.firstleg.mapper.LoadingPickAssignmentMapper; import com.baiyue.firstleg.mapper.LoadingPickOperationMapper; import com.baiyue.firstleg.mapper.LoadingPickReviewMapper; @@ -66,6 +68,10 @@ public class LoadingPickRepository { return tasks.selectLoadingByContainerAndAccount(containerNo, warehouseId); } + public List pendingTasks(long operatorId, String keyword) { + return tasks.selectPendingForOperator(operatorId, keyword); + } + public LoadingPickItemEntity item(long taskId, long itemId) { return items.selectOne(new LambdaQueryWrapper() .eq(LoadingPickItemEntity::getPickTaskId, taskId) @@ -78,6 +84,10 @@ public class LoadingPickRepository { .orderByAsc(LoadingPickItemEntity::getId)); } + public List cartonLabels(long taskId) { + return items.selectTaskCartonLabels(taskId); + } + public List items(long taskId, long warehouseId) { return items.selectTaskItemsWithAccount(taskId, warehouseId); } diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java index 0919e9f0c..1bfc0d408 100644 --- a/firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/LoadingPickService.java @@ -20,6 +20,7 @@ import com.baiyue.firstleg.dto.LoadingPickDtos.BatchItemRequest; import com.baiyue.firstleg.dto.LoadingPickDtos.InventoryResource; import com.baiyue.firstleg.dto.LoadingPickDtos.InboundInventory; import com.baiyue.firstleg.dto.LoadingPickDtos.InventorySnapshot; +import com.baiyue.firstleg.dto.LoadingPickDtos.PendingTask; import com.baiyue.firstleg.entity.LoadingPickAssignmentEntity; import com.baiyue.firstleg.entity.LoadingPickHistoryEntity; import com.baiyue.firstleg.entity.LoadingPickItemEntity; @@ -28,6 +29,7 @@ import com.baiyue.firstleg.entity.LoadingPickReviewEntity; import com.baiyue.firstleg.entity.LoadingPickTaskEntity; import com.baiyue.firstleg.entity.LoadingPlanItemEntity; import com.baiyue.firstleg.entity.LoadingPlanHistoryEntity; +import com.baiyue.firstleg.mapper.LoadingPickItemMapper.CartonLabelRow; import com.baiyue.firstleg.repository.LoadingPickRepository; import com.baiyue.firstleg.repository.SmartLoadingRepository; import jakarta.annotation.Resource; @@ -119,6 +121,15 @@ public class LoadingPickService { return view(task, warehouseId); } + public List pendingTasks(long operatorId, String keyword) { + String normalizedKeyword = StringUtils.hasText(keyword) ? keyword.trim() : null; + return repository.pendingTasks(operatorId, normalizedKeyword).stream() + .map(row -> new PendingTask(row.getTaskId(), row.getLoadingPlanId(), row.getContainerNo(), + row.getWarehouseId(), row.getWarehouseName(), row.getTaskStatus(), row.getWorkflowStatus(), + row.getVersion(), row.getAssignmentVersion(), row.getItemCount(), row.getPendingItemCount())) + .toList(); + } + public InventorySnapshot inventorySnapshot(long taskId, long warehouseId) { LoadingPickTaskEntity task = requiredTask(taskId); List items = warehouseScopedItems(task, warehouseId); @@ -475,10 +486,17 @@ public class LoadingPickService { private Task view(LoadingPickTaskEntity task, Long warehouseId) { List source = warehouseId == null ? repository.items(task.getId()) : repository.items(task.getId(), warehouseId); - List items = source.stream() - .map(item -> new PickItem(item.getId(), item.getLoadingPlanItemId(), item.getInboundId(), - item.getInboundDetailId(), item.getPlannedPackageCount(), item.getPickedPackageCount(), - item.getItemStatus(), item.getVersion())).toList(); + Map> cartonsByItem = repository.cartonLabels(task.getId()).stream() + .collect(Collectors.groupingBy(CartonLabelRow::getPickItemId)); + List items = source.stream().map(item -> { + List cartons = cartonsByItem.getOrDefault(item.getId(), List.of()); + CartonLabelRow first = cartons.isEmpty() ? null : cartons.get(0); + return new PickItem(item.getId(), item.getLoadingPlanItemId(), item.getInboundId(), + item.getInboundDetailId(), first == null ? null : first.getOrderNo(), + first == null ? null : first.getInboundCode(), first == null ? null : first.getCargoName(), + cartons.stream().map(CartonLabelRow::getBoxCode).toList(), item.getPlannedPackageCount(), + item.getPickedPackageCount(), item.getItemStatus(), item.getVersion()); + }).toList(); LoadingPickAssignmentEntity assigned = repository.assignment(task.getId()); LoadingPickReviewEntity reviewed = repository.review(task.getId()); Assignment assignment = assigned == null ? null : new Assignment(assigned.getAssigneeId(), diff --git a/firstleg/src/main/resources/openapi/phase3-pda-convergence-v1.yaml b/firstleg/src/main/resources/openapi/phase3-pda-convergence-v1.yaml index 433f2f6a1..6cc0fa0c2 100644 --- a/firstleg/src/main/resources/openapi/phase3-pda-convergence-v1.yaml +++ b/firstleg/src/main/resources/openapi/phase3-pda-convergence-v1.yaml @@ -11,6 +11,19 @@ paths: - {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}} responses: '200': {description: Task with assignment, review and item-level versions} + /api/platform/v1/first-leg/pda-loading/tasks/pending: + get: + summary: List active tasks available to the authenticated PDA operator + parameters: + - {name: keyword, in: query, required: false, schema: {type: string, maxLength: 64}} + responses: + '200': + description: Unassigned tasks and tasks assigned to the current operator; no customer selector required + content: + application/json: + schema: + type: array + items: {$ref: '#/components/schemas/PendingTask'} /api/platform/v1/first-leg/pda-loading/tasks/{taskId}/claim: post: summary: Atomically claim an unassigned task for the authenticated operator @@ -85,6 +98,21 @@ components: TaskId: {name: taskId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}} ItemId: {name: itemId, in: path, required: true, schema: {type: integer, format: int64, minimum: 1}} schemas: + PendingTask: + type: object + required: [taskId, loadingPlanId, containerNo, warehouseId, taskStatus, workflowStatus, version, assignmentVersion, itemCount, pendingItemCount] + properties: + taskId: {type: integer, format: int64} + loadingPlanId: {type: integer, format: int64} + containerNo: {type: string} + warehouseId: {type: integer, format: int64} + warehouseName: {type: string, nullable: true} + taskStatus: {type: string, enum: [LOADING]} + workflowStatus: {type: string, enum: [UNASSIGNED, IN_PROGRESS]} + version: {type: integer, minimum: 0} + assignmentVersion: {type: integer, minimum: 0} + itemCount: {type: integer, minimum: 1} + pendingItemCount: {type: integer, minimum: 0} ClaimRequest: type: object required: [expectedAssignmentVersion, operationId] diff --git a/firstleg/src/test/java/com/baiyue/firstleg/controller/PlatformLoadingPdaControllerContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/controller/PlatformLoadingPdaControllerContractTest.java index 7d0cb03f8..fea0a5ce9 100644 --- a/firstleg/src/test/java/com/baiyue/firstleg/controller/PlatformLoadingPdaControllerContractTest.java +++ b/firstleg/src/test/java/com/baiyue/firstleg/controller/PlatformLoadingPdaControllerContractTest.java @@ -13,6 +13,7 @@ class PlatformLoadingPdaControllerContractTest { @Test void exposesAssignmentWorkAndReviewWithDedicatedAuthorities() throws Exception { authority("task", "platform:firstleg:pda:view", AuthenticatedUser.class, String.class, long.class); + authority("pendingTasks", "platform:firstleg:pda:view", AuthenticatedUser.class, String.class); authority("inventorySnapshot", "platform:firstleg:pda:view", AuthenticatedUser.class, long.class, long.class); authority("claim", "platform:firstleg:pda:work", AuthenticatedUser.class, long.class, ClaimRequest.class); authority("assign", "platform:firstleg:pda:assign", AuthenticatedUser.class, long.class, AssignRequest.class); diff --git a/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingPickPdaTaskSqlContractTest.java b/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingPickPdaTaskSqlContractTest.java new file mode 100644 index 000000000..1ecb572ad --- /dev/null +++ b/firstleg/src/test/java/com/baiyue/firstleg/repository/LoadingPickPdaTaskSqlContractTest.java @@ -0,0 +1,37 @@ +package com.baiyue.firstleg.repository; + +import com.baiyue.firstleg.mapper.LoadingPickItemMapper; +import com.baiyue.firstleg.mapper.LoadingPickTaskMapper; +import org.apache.ibatis.annotations.Select; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class LoadingPickPdaTaskSqlContractTest { + @Test + void pendingTasksAreLimitedToUnassignedOrCurrentOperatorWithoutCustomerInput() throws Exception { + String sql = LoadingPickTaskMapper.class + .getDeclaredMethod("selectPendingForOperator", long.class, String.class) + .getAnnotation(Select.class).value()[0]; + + assertThat(sql) + .contains("task.task_status = 'LOADING'") + .contains("assignment.pick_task_id IS NULL OR assignment.assignee_id = #{operatorId}") + .contains("booking.warehouse_id AS warehouseId") + .doesNotContain("tenantId"); + } + + @Test + void taskDetailsExposeStableCartonCodesBoundToThePlannedInboundDetail() throws Exception { + String sql = LoadingPickItemMapper.class + .getDeclaredMethod("selectTaskCartonLabels", long.class) + .getAnnotation(Select.class).value()[0]; + + assertThat(sql) + .contains("fl_inbound_carton_label") + .contains("label.tenant_id = plan_item.account_id") + .contains("label.inbound_id = plan_item.inbound_id") + .contains("label.inbound_detail_id = plan_item.inbound_detail_id") + .contains("label.box_code AS boxCode"); + } +} diff --git a/scripts/finance-deployment-contract.test.mjs b/scripts/finance-deployment-contract.test.mjs new file mode 100644 index 000000000..7e20e5b37 --- /dev/null +++ b/scripts/finance-deployment-contract.test.mjs @@ -0,0 +1,31 @@ +import assert from 'node:assert/strict' +import { readFile } from 'node:fs/promises' +import test from 'node:test' + +const read = async (path) => (await readFile(new URL(`../${path}`, import.meta.url), 'utf8')) + .replaceAll('\r\n', '\n') + +test('Jenkins pipelines build finance as an independent service', async () => { + const controller = await read('Jenkinsfile') + const service = await read('Jenkinsfile.service') + + assert.match(controller, /'gateway', 'auth', 'iam', 'firstleg', 'finance'/) + assert.match(controller, /'finance': 'SERVICE_FINANCE'/) + assert.match(controller, /booleanParam\(name: 'SERVICE_FINANCE'/) + assert.match(service, /'gateway', 'auth', 'iam', 'firstleg', 'finance'/) + assert.match(service, /'finance': 'finance\/target\/finance-service-1\.0\.0-SNAPSHOT\.jar'/) +}) + +test('Compose and deploy script expose finance with fail-closed database config', async () => { + const compose = await read('deploy/docker-compose.yml') + const serviceMap = await read('deploy/service-map.yml') + const deployScript = await read('deploy/scripts/deploy-services.sh') + + assert.match(compose, /^ finance:\s*$/m) + assert.match(compose, /image: \$\{IMAGE_REPOSITORY\}\/finance:\$\{IMAGE_TAG\}/) + assert.match(compose, /SERVER_PORT: 9203/) + assert.match(compose, /FINANCE_DB_URL: \$\{FINANCE_DB_URL\}/) + assert.match(serviceMap, /^ finance:\s*$/m) + assert.match(serviceMap, /jar: finance\/target\/finance-service-1\.0\.0-SNAPSHOT\.jar/) + assert.match(deployScript, /FINANCE_DB_URL FINANCE_DB_USERNAME FINANCE_DB_PASSWORD/) +})