RrunningW```
930d7404创建于 1月6日历史提交

Fountain 使用手册

快速开始

概述

简介

本指南提供了 Fountain 如何帮助您加速应用程序开发的示例。当您阅读更多相关指南时,您将看到 Fountain 的更多用例。本指南旨在让您快速体验 Fountain。

您将构建什么

您将使用 Fountain 构建一个简单的 Web 应用程序,并为其添加一些有用的服务。

您需要什么

  • 时间要求:约20分钟
  • 开发工具:文本编辑器或 IDE(推荐:VSCode)
  • 所需依赖:
    • 仓颉SDK:cangjie-sdk-linux-x64-1.0.3
    • 仓颉Stdx:cangjie-stdx-linux-x64-1.0.3.1

如何完成本指南

与大多数技术入门指南一样,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到可运行的代码。


Linux 环境配置

环境准备

仓颉环境配置

  1. 参考 仓颉SDK配置 进行配置。

Stdx 环境配置

  1. 本项目依赖仓颉Stdx包,参考 仓颉stdx配置 进行配置。

Fountain 环境配置

  1. 克隆 Fountain 源代码至本地环境,执行命令:git clone https://gitcode.com/Cangjie-SIG/fountain.git
  2. 安装 fboot 项目依赖,在 fboot 目录下执行:cjpm install
  3. 将 Fountain 动态链接库所在的路径加入环境变量,执行命令:export FOUNTAIN_HOME=~/.cjpm/libs/fboot
  4. 将编译后的动态链接库加入环境变量,执行命令:export LD_LIBRARY_PATH=$FOUNTAIN_HOME:$LD_LIBRARY_PATH

环境配置参考(.bashrc)

export CANGJIE_HOME="~/cangjie"
export CANGJIE_STDX_PATH="~/stdx/1.0.3.1/linux_x86_64_llvm"
export CANGJIE_STDX_DYNAMIC_PATH="~/cangjie/stdx/1.0.3.1/linux_x86_64_llvm/dynamic/stdx"
export FOUNTAIN_HOME="~/.cjpm/libs/fboot"
export LD_LIBRARY_PATH=$FOUNTAIN_HOME:$LD_LIBRARY_PATH

快速搭建基于 Fountain 的项目

本指南详细介绍了如何快速搭建一个使用 Fountain 框架并以动态库(Dynamic Library)形式构建的项目。

1. 项目初始化和目录结构

首先,创建项目根目录并初始化主项目(project_name)和子模块(hello),具体步骤如下表:

步骤 命令 描述
1 fboot workspace project_name 使用project_name在当前路径创建新项目并初始化成workspace fboot workspace会把fountain和stdx的依赖添加到cjpm.toml
1 fboot module hello 在当前路径用hello创建动态链接库模块,并把模块加入当前目录下的cjpm.toml

2. 配置 cjpm.toml 文件(fountain会自动配置)

项目中有两个 cjpm.toml 文件需要配置:主项目(fountain_demo/cjpm.toml)和子模块(fountain_demo/user/cjpm.toml)。

2.1 主项目配置

[dependencies]
  # 配置 fountain 项目依赖
  fountain = {git = "https://gitcode.com/Cangjie-SIG/fountain.git", branch="master"}

[workspace]
  version = "1.0.0"
  # 声明 hello 目录为一个 workspace 成员
  members=["hello"]
  # 通用的编译选项
  compile-option = "-O2 --dy-std -Woff unused"
  override-compile-option = "-O2 --dy-std -Woff unused"

[profile.build]
  incremental = true
[profile.build.combined]
  # 将 fdemo 库类型设置为 dynamic
  fdemo = "dynamic"

[target]
# --- 针对不同操作系统的配置 ---
 
[target.x86_64-unknown-linux-gnu]
  compile-option = "-O2 --lto=full --dy-std -Woff unused"
  override-compile-option = "-O2 --lto=full --dy-std -Woff unused"
[target.x86_64-unknown-linux-gnu.bin-dependencies]
  path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
 
[target.aarch64-unknown-linux-gnu]
  compile-option = "-O2 --lto=full --dy-std -Woff unused"
  override-compile-option = "-O2 --lto=full --dy-std -Woff unused"
[target.aarch64-unknown-linux-gnu.bin-dependencies]
  path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
 
[target.x86_64-w64-mingw32]
  compile-option = "-O2 --dy-std -Woff unused"
  override-compile-option = "-O2 --dy-std -Woff unused"
[target.x86_64-w64-mingw32.bin-dependencies]
  path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
 
[target.x86_64-apple-darwin]
  compile-option = "-O2 --dy-std -Woff unused"
  override-compile-option = "-O2 --dy-std -Woff unused"
[target.x86_64-apple-darwin.bin-dependencies]
  path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
 
[target.aarch64-apple-darwin]
  compile-option = "-O2 --dy-std -Woff unused"
  override-compile-option = "-O2 --dy-std -Woff unused"
[target.aarch64-apple-darwin.bin-dependencies]
  path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
[target.aarch64-linux-ohos]
  compile-option = "-O2 --dy-std -ldl"
  override-compile-option = "-O2 --dy-std -ldl"
[target.aarch64-linux-ohos.bin-dependencies]
  path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]
[target.x86_64-linux-ohos]
  compile-option = "-O2 --dy-std -ldl"
  override-compile-option = "-O2 --dy-std -ldl"
[target.x86_64-linux-ohos.bin-dependencies]
  path-option = ["${CANGJIE_STDX_DYNAMIC_PATH}"]

2.2 子模块配置

[package]
  cjc-version = "1.0.0"
  name = "hello"
  description = "Fountain hello module"
  version = "1.0.0"
  target-dir = ""
  src-dir = ""
  output-type = "dynamic"
  compile-option = "-O2 --dy-std"
  override-compile-option = "-O2 --dy-std"
  link-option = ""
  package-configuration = {}

3. 创建控制器文件

在 hello 模块中创建源代码文件,实现一个简单的 Web 控制器,具体步骤如下:

步骤 命令 描述
1 cd hello/src 进入 hello 模块的源代码目录。
2 touch hello/src/HellowordController.cj 创建控制器源文件。

将以下内容写入 project_name/hello/src/HelloWordController.cj:

package hello
 
import fountain.mvc.*
import fountain.mvc.macros.*
 
@Controller
public class HelloWordController {
    
    /**
     * 定义一个 GET 请求处理器,路径为 /
     * 生产纯文本内容
     * 忽略安全检查(仅作示例,实际项目中请谨慎使用)
     */
    @GetMapping[path:"/", produces:'text/plain']
    @IgnoreSecurity
    public func helloWorld(): String {
        return "hello Fountain!"
    }
}

4. 构建和运行项目

在项目根目录 (project_name) 下执行构建和运行命令。

步骤 命令 描述
4.1 fboot build out 构建项目,并将编译后的结果输出到名为 out 的目录中。
4.2 fboot run ./ --dylibPattern='hello' 基于 Fountain 框架运行当前项目(默认使用 out 目录中的编译产物)。参数说明:--dylibPattern='hello' 指定要加载的动态库匹配模式。在本例中,它指示 Fountain 仅加载名称中包含 hello 的动态库(即 hello 模块编译生成的动态库)。

4.3 验证结果 项目成功启动后,您可以通过访问以下地址来验证控制器是否正常工作:

常用的注解与宏的使用和使用案例

1. MVC相关注解

  • @Controller: 标记类为控制器
  • @RequestMapping: 定义API路由
  • @RequestBody: 表示从请求体获取数据
  • @RequestParam: 获取请求参数
  • @RequestHeader: 绑定请求头

Example: @fdemo/user/src/controller/UserController.cj

@Controller
public class HelloController {
    @GetMapping[path:'/hello',consumes:'application/x-www-form-urlencoded', produces:'text/plain']
    @IgnoreSecurity  //这个注解功能是忽略jwt的认证
    public func register( @RequestParam username: String, @RequestParam password: String): String {
        println("${username}, ${password}")
    }
}

2. 数据模型相关注解

  • @DataAssist: 辅助生成数据类的通用方法(如equals, hashCode, toString等)
  • @QueryMappersGenerator: 生成查询映射器
  • @ORMField: ORM字段映射
  • @DAO: ORM字段映射

Example 1:

@DataAssist[props fields]//生成get/set的方法,返回包含所有字段的元组
@QueryMappersGenerator[med_allocation]//用于生成数据库查询映射的注解
public class MedAllocationPO {
    @ORMField[true 'id']//对象关系映射(ORM)的字段注解
    private var id: Int64 = 0
    @ORMField['pre_amount']
    private var preAmount: Decimal = Decimal.zero
}

Example 2: @fdemo/user/src/dao/UserDAO.cj

@DAO
public interface UserDAO <: RootDAO {
    func register(username: String, password: String): Int64 {
      executor.setSql('''
          insert into user_info(
                      username,
                      password)
               values(${arg(username)}, 
                      ${arg(password)}) 
          returning id'''
      ).insert
  }
  ...
}

3. Bean相关注解

  • @Bean: 将类标记为管理的 Bean,使其可以被依赖注入。
  • @BeanMeta: 为 Bean 添加额外的元数据,通常与 @src/bean/bean.cj 一起使用,用于配置 Bean 的元信息

Example:

package user.service.impl
@Bean
@BeanMeta
public class UserServiceImpl <: UserService {
  ...
}

package user.controller
@Controller
public class UserController {
    private let userService = lookup<UserService>()
    ...
}

4. AOP切面

  • @AspectRoute - 修饰切面实现类

Example:

@AspectRoute(route = ExecutionRouteRule("MyService", "doSomething", "String", "String"))
public class LoggingAspect <: Aspect {
    // 在目标方法执行前调用
    public func before(funcInfo: InvocationFuncInfo): Unit {
        println("方法调用前 - 方法名: " + funcInfo.methodName)
        println("参数: " + funcInfo.args.toString())
    }
    
    // 在目标方法执行后调用
    public func after(funcInfo: InvocationFuncInfo, result: Any): Any {
        println("方法返回后 - 方法名: " + funcInfo.methodName)
        println("返回值: " + result.toString())
        return result
    }
    
    // 环绕通知,可以控制目标方法的执行
    public func around(funcInfo: InvocationFuncInfo, point: (Array<Any>) -> Any): Any {
        println("环绕通知 - 方法执行前: " + funcInfo.methodName)
        try {
            let result = point(funcInfo.args)
            println("环绕通知 - 方法执行后,结果: " + result.toString())
            return result
        } catch (e) {
            println("环绕通知 - 方法执行异常: " + e.message)
            throw e
        }
    }
    
    // 异常处理
    public func throwing(funcInfo: InvocationFuncInfo, e: Exception): Exception {
        println("方法执行异常 - 方法名: " + funcInfo.methodName)
        println("异常信息: " + e.message)
        return e
    }
    
    // 最终通知,无论是否发生异常都会执行
    public func final(funcInfo: InvocationFuncInfo): Unit {
        println("最终通知 - 方法名: " + funcInfo.methodName)
    }
}

5. 测试相关

  • @fdemo/user/src/util/auth/TickTockTest.cj - 测试类注解
  • @TestCase - 测试用例
  • @Assert - 断言,断言失败时能够快速定位问题

Example:

@Test
public class MyTest {
    @TestCase  // 标记这是一个测试方法
    public func testAddition(): Unit {
        // 测试代码
        @Assert(1 + 1, 2)  // 断言,用于验证代码行为是否符合预期。
    }
}