From f972cd9932f6b5ec8884ce5b79ee5dd9204412f0 Mon Sep 17 00:00:00 2001 From: Searises Hao Wang Date: Tue, 5 Aug 2025 17:23:04 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=8D=87=E7=BA=A7=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/webapi/config/SwaggerConfig.java | 14 +- .../example/webapi/entity/OnceWeighing.java | 62 ++++++ .../postgresql/OnceWeighingPostgreSQL.java | 62 ++++++ .../repository/OnceWeighingRepository.java | 16 ++ .../pg/OnceWeighingPostgreSQLRepository.java | 17 ++ .../webapi/service/DataSyncService.java | 186 ++++++++++++++++++ .../webapi/service/ScheduledSyncService.java | 45 ++++- src/main/resources/application-xining.yml | 76 +++++++ start.sh | 17 +- 9 files changed, 477 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/example/webapi/entity/OnceWeighing.java create mode 100644 src/main/java/com/example/webapi/entity/postgresql/OnceWeighingPostgreSQL.java create mode 100644 src/main/java/com/example/webapi/repository/OnceWeighingRepository.java create mode 100644 src/main/java/com/example/webapi/repository/pg/OnceWeighingPostgreSQLRepository.java create mode 100644 src/main/resources/application-xining.yml diff --git a/src/main/java/com/example/webapi/config/SwaggerConfig.java b/src/main/java/com/example/webapi/config/SwaggerConfig.java index 1dca415..0390803 100644 --- a/src/main/java/com/example/webapi/config/SwaggerConfig.java +++ b/src/main/java/com/example/webapi/config/SwaggerConfig.java @@ -43,7 +43,7 @@ public class SwaggerConfig { .tags(new springfox.documentation.service.Tag("车辆管理", "车辆信息相关接口")) .tags(new springfox.documentation.service.Tag("称重记录", "称重记录相关接口")); } - + /** * API信息配置 */ @@ -55,7 +55,7 @@ public class SwaggerConfig { .contact(new Contact("开发团队", "http://www.example.com", "dev@example.com")) .build(); } - + /** * 获取全局请求参数 * 注意:在Swagger UI中这些参数设置为非必需,以便能够正常访问Swagger界面 @@ -63,7 +63,7 @@ public class SwaggerConfig { */ private List getGlobalParameters() { List parameters = new ArrayList<>(); - + // AK (Access Key) - 在Swagger中设为非必需,便于测试 Parameter akParam = new ParameterBuilder() .name("ak") @@ -73,7 +73,7 @@ public class SwaggerConfig { .required(false) // 改为false,让Swagger UI可以正常访问 .build(); parameters.add(akParam); - + // Timestamp - 在Swagger中设为非必需,便于测试 Parameter timestampParam = new ParameterBuilder() .name("timestamp") @@ -83,7 +83,7 @@ public class SwaggerConfig { .required(false) // 改为false,让Swagger UI可以正常访问 .build(); parameters.add(timestampParam); - + // Signature - 在Swagger中设为非必需,便于测试 Parameter signatureParam = new ParameterBuilder() .name("signature") @@ -93,7 +93,7 @@ public class SwaggerConfig { .required(false) // 改为false,让Swagger UI可以正常访问 .build(); parameters.add(signatureParam); - + return parameters; } -} +} diff --git a/src/main/java/com/example/webapi/entity/OnceWeighing.java b/src/main/java/com/example/webapi/entity/OnceWeighing.java new file mode 100644 index 0000000..e5dca0a --- /dev/null +++ b/src/main/java/com/example/webapi/entity/OnceWeighing.java @@ -0,0 +1,62 @@ +package com.example.webapi.entity; + +import lombok.Data; +import javax.persistence.*; +import java.util.Date; + +@Data +@Entity +@Table(name = "TB_ONCEWEIGHING") +public class OnceWeighing { + @Id + @Column(name = "NODEID", length = 22, nullable = false) + private String nodeId; + + @Column(name = "LISTID", length = 18) + private String listId; + + @Column(name = "WEIGHT") + private Double weight; + + @Column(name = "SPEED") + private Double speed; + + @Column(name = "NODETIME") + private Date nodeTime; + + @Column(name = "OPERATORID", length = 8) + private String operatorId; + + @Column(name = "VEHICLETYPE", length = 10) + private String vehicleType; + + @Column(name = "VEHICLEID", length = 10) + private String vehicleId; + + @Column(name = "MANAGEFLAG") + private Boolean manageFlag; + + @Column(name = "SNAPPICTIME", length = 14) + private String snapPicTime; + + @Column(name = "UPLOADFLAG") + private Boolean uploadFlag; + + @Column(name = "USELESSFLAG") + private Boolean uselessFlag; + + @Column(name = "DEMO", length = 50) + private String demo; + + @Column(name = "SCALEID", length = 10) + private String scaleId; + + @Column(name = "GROSSORTARE", length = 1) + private String grossOrTare; + + @Column(name = "BEFOREANDAFTERSLANTCARRY") + private Double beforeAndAfterSlantCarry; + + @Column(name = "LEFTANDRIGHTLANTCARRY") + private Double leftAndRightLantCarry; +} \ No newline at end of file diff --git a/src/main/java/com/example/webapi/entity/postgresql/OnceWeighingPostgreSQL.java b/src/main/java/com/example/webapi/entity/postgresql/OnceWeighingPostgreSQL.java new file mode 100644 index 0000000..2090158 --- /dev/null +++ b/src/main/java/com/example/webapi/entity/postgresql/OnceWeighingPostgreSQL.java @@ -0,0 +1,62 @@ +package com.example.webapi.entity.postgresql; + +import lombok.Data; +import javax.persistence.*; +import java.util.Date; + +@Data +@Entity +@Table(name = "tb_once_weighing") +public class OnceWeighingPostgreSQL { + @Id + @Column(name = "node_id", length = 22, nullable = false) + private String nodeId; + + @Column(name = "list_id", length = 18) + private String listId; + + @Column(name = "weight") + private Double weight; + + @Column(name = "speed") + private Double speed; + + @Column(name = "node_time") + private Date nodeTime; + + @Column(name = "operator_id", length = 8) + private String operatorId; + + @Column(name = "vehicle_type", length = 10) + private String vehicleType; + + @Column(name = "vehicle_id", length = 10) + private String vehicleId; + + @Column(name = "manage_flag") + private Boolean manageFlag; + + @Column(name = "snap_pic_time", length = 14) + private String snapPicTime; + + @Column(name = "upload_flag") + private Boolean uploadFlag; + + @Column(name = "useless_flag") + private Boolean uselessFlag; + + @Column(name = "demo", length = 50) + private String demo; + + @Column(name = "scale_id", length = 10) + private String scaleId; + + @Column(name = "gross_or_tare", length = 1) + private String grossOrTare; + + @Column(name = "before_and_after_slant_carry") + private Double beforeAndAfterSlantCarry; + + @Column(name = "left_and_right_lant_carry") + private Double leftAndRightLantCarry; +} \ No newline at end of file diff --git a/src/main/java/com/example/webapi/repository/OnceWeighingRepository.java b/src/main/java/com/example/webapi/repository/OnceWeighingRepository.java new file mode 100644 index 0000000..fa6c964 --- /dev/null +++ b/src/main/java/com/example/webapi/repository/OnceWeighingRepository.java @@ -0,0 +1,16 @@ +package com.example.webapi.repository; + +import com.example.webapi.entity.OnceWeighing; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; +import java.util.List; + +@Repository("onceWeighingRepository") +public interface OnceWeighingRepository extends JpaRepository, JpaSpecificationExecutor { + List findByUploadFlag(Boolean uploadFlag); + List findByNodeTimeAfter(java.util.Date nodeTime); + Page findByNodeTimeNotNullOrderByNodeTimeDesc(Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/com/example/webapi/repository/pg/OnceWeighingPostgreSQLRepository.java b/src/main/java/com/example/webapi/repository/pg/OnceWeighingPostgreSQLRepository.java new file mode 100644 index 0000000..a6526cb --- /dev/null +++ b/src/main/java/com/example/webapi/repository/pg/OnceWeighingPostgreSQLRepository.java @@ -0,0 +1,17 @@ +package com.example.webapi.repository.pg; + +import com.example.webapi.entity.postgresql.OnceWeighingPostgreSQL; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import java.util.List; + +@Repository +public interface OnceWeighingPostgreSQLRepository extends JpaRepository { + @Query("SELECT o FROM OnceWeighingPostgreSQL o WHERE o.uploadFlag = :uploadFlag") + List findByUploadFlag(@Param("uploadFlag") Boolean uploadFlag); + + @Query("SELECT MAX(o.nodeTime) FROM OnceWeighingPostgreSQL o WHERE o.nodeTime IS NOT NULL") + java.util.Date findMaxNodeTime(); +} \ No newline at end of file diff --git a/src/main/java/com/example/webapi/service/DataSyncService.java b/src/main/java/com/example/webapi/service/DataSyncService.java index 32ac36f..924439f 100644 --- a/src/main/java/com/example/webapi/service/DataSyncService.java +++ b/src/main/java/com/example/webapi/service/DataSyncService.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Service public class DataSyncService { @@ -34,6 +35,13 @@ public class DataSyncService { @Qualifier("twiceWeighingPostgreSQLRepository") private TwiceWeighingPostgreSQLRepository twiceWeighingPostgreSQLRepository; + @Autowired + @Qualifier("onceWeighingRepository") + private com.example.webapi.repository.OnceWeighingRepository onceWeighingRepository; + + @Autowired + private com.example.webapi.repository.pg.OnceWeighingPostgreSQLRepository onceWeighingPostgreSQLRepository; + /** * 同步单条数据 */ @@ -349,4 +357,182 @@ public class DataSyncService { target.setIronNumber(source.getIronNumber()); target.setDirection(source.getDirection()); } + + /** + * 一次过磅-同步所有未上传的数据 - 分批处理 + */ + @Transactional(transactionManager = "postgresqlTransactionManager") + public int syncOnceUnuploadedRecords() { + return syncOnceUnuploadedRecords(500); + } + + /** + * 一次过磅-同步所有未上传的数据 - 指定批次大小 + */ + @Transactional(transactionManager = "postgresqlTransactionManager") + public int syncOnceUnuploadedRecords(int batchSize) { + try { + List unuploadedRecords = onceWeighingRepository.findByUploadFlag(false); + int totalRecords = unuploadedRecords.size(); + int successCount = 0; + int batchCount = 0; + for (int i = 0; i < totalRecords; i += batchSize) { + int endIndex = Math.min(i + batchSize, totalRecords); + List batchRecords = unuploadedRecords.subList(i, endIndex); + batchCount++; + int batchSuccessCount = processOnceBatch(batchRecords); + successCount += batchSuccessCount; + } + return successCount; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 一次过磅-全量同步 + */ + @Transactional(transactionManager = "postgresqlTransactionManager") + public int syncOnceAllData() { + return syncOnceAllData(500); + } + + @Transactional(transactionManager = "postgresqlTransactionManager") + public int syncOnceAllData(int batchSize) { + try { + List allRecords = onceWeighingRepository.findAll(); +// org.springframework.data.domain.PageRequest pageRequest = org.springframework.data.domain.PageRequest.of(0, 500, org.springframework.data.domain.Sort.by(org.springframework.data.domain.Sort.Direction.DESC, "nodeTime")); +// List allRecords = onceWeighingRepository.findByNodeTimeNotNullOrderByNodeTimeDesc(pageRequest).getContent(); + int totalRecords = allRecords.size(); + int successCount = 0; + int batchCount = 0; + for (int i = 0; i < totalRecords; i += batchSize) { + int endIndex = Math.min(i + batchSize, totalRecords); + List batchRecords = allRecords.subList(i, endIndex); + batchCount++; + int batchSuccessCount = processOnceBatch(batchRecords); + successCount += batchSuccessCount; + } + return successCount; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 一次过磅-增量同步(基于node_time,数据库层面过滤) + */ + @Transactional(transactionManager = "postgresqlTransactionManager") + public int syncOnceIncrementalData() { + try { + // 获取PostgreSQL中最大的node_time(数据库层面) + java.util.Date maxNodeTime = onceWeighingPostgreSQLRepository.findMaxNodeTime(); + if (maxNodeTime == null) { + return syncOnceAllData(); + } + // 数据库层面过滤增量数据 + List incrementalRecords = onceWeighingRepository.findByNodeTimeAfter(maxNodeTime); + int successCount = 0; + for (com.example.webapi.entity.OnceWeighing source : incrementalRecords) { + try { + com.example.webapi.entity.postgresql.OnceWeighingPostgreSQL target = convertOnceToPostgreSQL(source); + java.util.Optional existingRecord = onceWeighingPostgreSQLRepository.findById(source.getNodeId()); + if (existingRecord.isPresent()) { + com.example.webapi.entity.postgresql.OnceWeighingPostgreSQL existing = existingRecord.get(); + updateOncePostgreSQLRecord(existing, source); + onceWeighingPostgreSQLRepository.saveAndFlush(existing); + } else { + onceWeighingPostgreSQLRepository.saveAndFlush(target); + } + successCount++; + } catch (Exception e) { + System.err.println("同步记录失败,NODEID: " + source.getNodeId() + ", 错误: " + e.getMessage()); + } + } + return successCount; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 一次过磅-批量处理 + */ + private int processOnceBatch(List batchRecords) { + int successCount = 0; + List entitiesToSave = new java.util.ArrayList<>(); + for (com.example.webapi.entity.OnceWeighing source : batchRecords) { + try { + com.example.webapi.entity.postgresql.OnceWeighingPostgreSQL target = convertOnceToPostgreSQL(source); + entitiesToSave.add(target); + successCount++; + } catch (Exception e) { + System.err.println("处理记录失败,NODEID: " + source.getNodeId() + ", 错误: " + e.getMessage()); + } + } + if (!entitiesToSave.isEmpty()) { + try { + onceWeighingPostgreSQLRepository.saveAllAndFlush(entitiesToSave); + } catch (Exception e) { + for (com.example.webapi.entity.postgresql.OnceWeighingPostgreSQL entity : entitiesToSave) { + try { + onceWeighingPostgreSQLRepository.save(entity); + } catch (Exception ex) { + System.err.println("单条插入失败,NODEID: " + entity.getNodeId() + ", 错误: " + ex.getMessage()); + } + } + } + } + return successCount; + } + + /** + * 一次过磅-SQLServer实体转PostgreSQL实体 + */ + private com.example.webapi.entity.postgresql.OnceWeighingPostgreSQL convertOnceToPostgreSQL(com.example.webapi.entity.OnceWeighing source) { + com.example.webapi.entity.postgresql.OnceWeighingPostgreSQL target = new com.example.webapi.entity.postgresql.OnceWeighingPostgreSQL(); + target.setNodeId(source.getNodeId()); + target.setListId(source.getListId()); + target.setWeight(source.getWeight()); + target.setSpeed(source.getSpeed()); + target.setNodeTime(source.getNodeTime()); + target.setOperatorId(source.getOperatorId()); + target.setVehicleType(source.getVehicleType()); + target.setVehicleId(source.getVehicleId()); + target.setManageFlag(source.getManageFlag()); + target.setSnapPicTime(source.getSnapPicTime()); + target.setUploadFlag(source.getUploadFlag()); + target.setUselessFlag(source.getUselessFlag()); + target.setDemo(source.getDemo()); + target.setScaleId(source.getScaleId()); + target.setGrossOrTare(source.getGrossOrTare()); + target.setBeforeAndAfterSlantCarry(source.getBeforeAndAfterSlantCarry()); + target.setLeftAndRightLantCarry(source.getLeftAndRightLantCarry()); + return target; + } + + /** + * 一次过磅-更新PostgreSQL记录 + */ + private void updateOncePostgreSQLRecord(com.example.webapi.entity.postgresql.OnceWeighingPostgreSQL target, com.example.webapi.entity.OnceWeighing source) { + target.setListId(source.getListId()); + target.setWeight(source.getWeight()); + target.setSpeed(source.getSpeed()); + target.setNodeTime(source.getNodeTime()); + target.setOperatorId(source.getOperatorId()); + target.setVehicleType(source.getVehicleType()); + target.setVehicleId(source.getVehicleId()); + target.setManageFlag(source.getManageFlag()); + target.setSnapPicTime(source.getSnapPicTime()); + target.setUploadFlag(source.getUploadFlag()); + target.setUselessFlag(source.getUselessFlag()); + target.setDemo(source.getDemo()); + target.setScaleId(source.getScaleId()); + target.setGrossOrTare(source.getGrossOrTare()); + target.setBeforeAndAfterSlantCarry(source.getBeforeAndAfterSlantCarry()); + target.setLeftAndRightLantCarry(source.getLeftAndRightLantCarry()); + } } diff --git a/src/main/java/com/example/webapi/service/ScheduledSyncService.java b/src/main/java/com/example/webapi/service/ScheduledSyncService.java index 8ea0be8..abdef9a 100644 --- a/src/main/java/com/example/webapi/service/ScheduledSyncService.java +++ b/src/main/java/com/example/webapi/service/ScheduledSyncService.java @@ -34,25 +34,54 @@ public class ScheduledSyncService implements CommandLineRunner { } } +// /** +// * 每天凌晨2点执行全量同步检查 +// */ +// @Scheduled(cron = "0 0 2 * * ?") +// public void dailyFullSync() { +// try { +// logger.info("开始每日全量同步检查..."); +// int count = dataSyncService.syncIncrementalData(); +// logger.info("每日全量同步检查完成,成功同步 {} 条记录", count); +// } catch (Exception e) { +// logger.error("每日全量同步检查过程中发生错误: {}", e.getMessage(), e); +// } +// } + /** - * 每天凌晨2点执行全量同步检查 + * 每5分钟执行一次一次过磅增量同步 */ - @Scheduled(cron = "0 0 2 * * ?") - public void dailyFullSync() { + @Scheduled(fixedRate = 300000) // 5分钟 = 300000毫秒 + public void syncOnceIncrementalData() { try { - logger.info("开始每日全量同步检查..."); - int count = dataSyncService.syncIncrementalData(); - logger.info("每日全量同步检查完成,成功同步 {} 条记录", count); + logger.info("开始定时一次过磅增量同步数据..."); + int count = dataSyncService.syncOnceIncrementalData(); + logger.info("定时一次过磅增量同步完成,成功同步 {} 条记录", count); } catch (Exception e) { - logger.error("每日全量同步检查过程中发生错误: {}", e.getMessage(), e); + logger.error("定时一次过磅增量同步过程中发生错误: {}", e.getMessage(), e); } } +// /** +// * 每天凌晨2点执行一次过磅全量同步检查 +// */ +// @Scheduled(cron = "0 5 2 * * ?") // 比二次过磅晚5分钟 +// public void dailyOnceFullSync() { +// try { +// logger.info("开始每日一次过磅全量同步检查..."); +// int count = dataSyncService.syncOnceAllData(); +// logger.info("每日一次过磅全量同步检查完成,成功同步 {} 条记录", count); +// } catch (Exception e) { +// logger.error("每日一次过磅全量同步检查过程中发生错误: {}", e.getMessage(), e); +// } +// } + @Override public void run(String... args) throws Exception { System.out.println("项目启动后执行增量同步------------------"); try { - syncIncrementalData(); +// syncIncrementalData(); + syncOnceIncrementalData(); } catch (Exception e) { logger.error("启动时增量同步失败: {}", e.getMessage(), e); } diff --git a/src/main/resources/application-xining.yml b/src/main/resources/application-xining.yml new file mode 100644 index 0000000..d5beaea --- /dev/null +++ b/src/main/resources/application-xining.yml @@ -0,0 +1,76 @@ +server: + port: 8806 + servlet: + context-path: /fuquanapi + +spring: + application: + name: fuquanapi + main: + allow-bean-definition-overriding: true + datasource: + # SQL Server数据源配置 + sqlserver: + # 西宁火车煤 - 备用配置 + url: jdbc:sqlserver://192.168.35.216:1433;SelectMethod=cursor;rewriteBatchedStatements=true;DatabaseName=WY_DATAMANAGE;encrypt=false;trustServerCertificate=true + username: sa + password: sa + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://112.33.47.173:6789;SelectMethod=cursor;rewriteBatchedStatements=true;DatabaseName=WY_DATAMANAGE20250731;encrypt=false;trustServerCertificate=true +# username: admin +# password: admin@Erp2021 +# driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver + # 连接池配置 + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + + # PostgreSQL数据源配置 + postgresql: + url: jdbc:postgresql://192.168.35.10:5435/auseft_web + username: postgres + password: Auseft@2025 + driver-class-name: org.postgresql.Driver + # 连接池配置 + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + jpa: + hibernate: + ddl-auto: none + show-sql: true + properties: + hibernate: + dialect: org.hibernate.dialect.SQLServer2008Dialect + format_sql: true + physical_naming_strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl + use_sql_comments: true + # 禁用默认数据源自动配置 + open-in-view: false + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + mvc: + pathmatch: + matching-strategy: ant_path_matcher + +# API客户端配置 +api: + client: + ak: 2760cd7c-cc0e-4034-86f3-d58e89f03d80 + sk: e2c0e9c1aaa72a0b1444f879c86a9034bf2f03be31a58b1e5b3b077948da75cf + +# 日志配置 +logging: + level: + com.example.webapi: DEBUG + pattern: + console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" + +# Swagger配置 - Swagger2不需要额外配置 diff --git a/start.sh b/start.sh index 69f5dd6..3e5db61 100644 --- a/start.sh +++ b/start.sh @@ -12,8 +12,6 @@ APP_CONTEXT_PATH="/fuquanapi" # JVM配置 JVM_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -XX:+UseStringDeduplication" -JVM_OPTS="$JVM_OPTS -Dserver.port=$APP_PORT" -JVM_OPTS="$JVM_OPTS -Dserver.servlet.context-path=$APP_CONTEXT_PATH" # 日志配置 LOG_DIR="./logs" @@ -55,6 +53,16 @@ check_jar() { print_message $GREEN "找到应用JAR文件: $APP_JAR" } +# 检查配置文件 +check_config() { + if [ -f "./application.yml" ]; then + print_message $GREEN "找到外部配置文件: ./application.yml" + print_message $BLUE "Spring Boot将自动使用外部配置文件" + else + print_message $YELLOW "未找到外部配置文件 ./application.yml,将使用JAR包内的默认配置" + fi +} + # 创建日志目录 create_log_dir() { if [ ! -d "$LOG_DIR" ]; then @@ -81,6 +89,9 @@ check_running() { # 启动应用 start_app() { print_message $BLUE "正在启动 $APP_NAME..." + + # 检查配置文件 + check_config # 启动应用 nohup java $JVM_OPTS -jar "$APP_JAR" > "$LOG_FILE" 2>&1 & @@ -236,4 +247,4 @@ main() { } # 执行主函数 -main "$@" +main "$@" \ No newline at end of file