From 6fd7887a1de7d320cc03b441fd2b3426252be0bf Mon Sep 17 00:00:00 2001 From: zeyan <258785420@qq.com> Date: Mon, 18 Aug 2025 21:45:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiService/ContractSignFormService.php | 44 +- .../service/api/student/ContractService.php | 266 ++++++ .../core/sys/CoreFieldMappingService.php | 823 ++++++++++++++++++ uniapp/api/apiRoute.js | 15 +- uniapp/common/config.js | 4 +- uniapp/pages-student/contracts/sign.vue | 30 +- 6 files changed, 1141 insertions(+), 41 deletions(-) create mode 100644 niucloud/app/service/core/sys/CoreFieldMappingService.php diff --git a/niucloud/app/service/api/apiService/ContractSignFormService.php b/niucloud/app/service/api/apiService/ContractSignFormService.php index 8c9d6457..bd18441b 100644 --- a/niucloud/app/service/api/apiService/ContractSignFormService.php +++ b/niucloud/app/service/api/apiService/ContractSignFormService.php @@ -11,6 +11,8 @@ namespace app\service\api\apiService; +use app\model\order_table\OrderTable; +use app\service\core\sys\CoreFieldMappingService; use core\base\BaseApiService; use think\facade\Db; use think\facade\Log; @@ -653,7 +655,7 @@ class ContractSignFormService extends BaseApiService 'data_type' => $config['data_type'] ?? 'user_input', 'field_type' => $config['field_type'] ?? 'text', 'is_required' => (int)($config['is_required'] ?? 0), - 'default_value' => '', + 'default_value' => $config['default_value'], 'validation_rule' => $config['validation_rule'] ?? '', 'sign_party' => $config['sign_party'] ?? '', ]; @@ -678,13 +680,13 @@ class ContractSignFormService extends BaseApiService case 'signature': // 电子签名类型:无默认值,需要用户手写签名 $field['signature_type'] = 'handwrite'; - $field['default_value'] = ''; + $field['default_value'] = $config['default_value'] ?? ''; break; case 'sign_img': // 签名图片类型:无默认值,需要用户上传或选择 $field['sign_image_source'] = 'upload'; - $field['default_value'] = ''; + $field['default_value'] = $config['default_value'] ?? ''; break; default: @@ -733,20 +735,22 @@ class ContractSignFormService extends BaseApiService switch ($table_name) { case 'school_student': // 学员表:直接从学员信息获取 - $value = $student[$field_name] ?? ''; + $service = new CoreFieldMappingService($config['table_name'], ['id'=>$student['id']]); + $value = $service->getValue($field_name); break; case 'school_customer_resources': // 用户表:通过学员的user_id关联查询 if (!empty($student['user_id'])) { - $value = Db::table('school_customer_resources') - ->where('id', $student['user_id']) - ->value($field_name) ?? ''; + $service = new CoreFieldMappingService($config['table_name'], ['id'=>$student['user_id']]); + $value = $service->getValue($field_name); } break; case 'school_order_table': // 订单表:查询该学员最新的订单信息 + $order = OrderTable::where('contract_sign_id',$config['contract_sign_id'])->find(); + $value = Db::table('school_order_table') ->where('student_id', $student['id']) ->order('created_at', 'desc') @@ -866,16 +870,6 @@ class ContractSignFormService extends BaseApiService throw new \Exception('合同签署记录不存在'); } - // 验证员工是否存在 - $personnel = Db::table('school_personnel') - ->where('id', $personnel_id) - ->where('status', 1) - ->find(); - - if (!$personnel) { - throw new \Exception('员工信息不存在'); - } - // 事务处理 Db::startTrans(); try { @@ -1057,8 +1051,7 @@ class ContractSignFormService extends BaseApiService ->where('id', $existing_config['id']) ->update([ 'default_value' => (string)$value, - 'updated_at' => $now, - 'updated_by' => $user_type + 'updated_at' => $now ]); if ($update_result) { @@ -1222,22 +1215,11 @@ class ContractSignFormService extends BaseApiService $now = date('Y-m-d H:i:s'); $update_data = [ - 'sign_status' => 1, // 已签署 + 'status' => 2, // 已签署 'sign_time' => $now, 'updated_at' => $now ]; - // 如果是员工签署,记录员工ID - if ($personnel_id) { - $update_data['personnel_id'] = $personnel_id; - $update_data['signed_by'] = 'staff'; - } - - // 如果是学员签署,确保学员ID正确 - if ($student_id) { - $update_data['signed_by'] = 'student'; - } - // 如果有签名图片,保存签名图片路径 if (!empty($signature_image)) { $update_data['signature_image'] = $signature_image; diff --git a/niucloud/app/service/api/student/ContractService.php b/niucloud/app/service/api/student/ContractService.php index ace2b68b..9938c87d 100644 --- a/niucloud/app/service/api/student/ContractService.php +++ b/niucloud/app/service/api/student/ContractService.php @@ -158,6 +158,16 @@ class ContractService extends BaseService // 合同条款(如果有内容的话) $contractSign['terms'] = $contractSign['contract_content'] ?: $contractSign['remarks']; + + // 对于已签署成功的合同(status=3),进行占位符替换 + if ($contractSign['status'] == 3 && !empty($contractSign['terms'])) { + $contractSign['terms'] = $this->replacePlaceholdersInTerms( + $contractSign['terms'], + $contractId, + $contractSign['sign_id'], + $studentId + ); + } return $contractSign; } @@ -867,4 +877,260 @@ class ContractService extends BaseService return $formData; } + + /** + * 在合同条款中替换占位符 + * 使用 school_document_data_source_config 表中的 default_value 数据进行替换 + * + * @param string $terms 合同条款内容 + * @param int $contractId 合同模板ID + * @param int $contractSignId 合同签署记录ID + * @param int $studentId 学员ID + * @return string 替换后的合同条款 + */ + private function replacePlaceholdersInTerms($terms, $contractId, $contractSignId, $studentId) + { + try { + if (empty($terms)) { + return $terms; + } + + // 获取该合同的所有占位符配置 + $configs = Db::table('school_document_data_source_config') + ->where('contract_id', $contractId) + ->where(function($query) use ($contractSignId) { + $query->where('contract_sign_id', $contractSignId) + ->whereOr('contract_sign_id', 'IS', null); + }) + ->field('placeholder, data_type, default_value, table_name, field_name, system_function') + ->select() + ->toArray(); + + if (empty($configs)) { + return $terms; + } + + // 获取学员信息(用于数据库类型查询) + $student = Db::table('school_student') + ->where('id', $studentId) + ->find(); + + // 遍历配置,逐个替换占位符 + foreach ($configs as $config) { + $placeholder = $config['placeholder']; + $value = ''; + + // 优先使用 default_value + if (!empty($config['default_value'])) { + $value = $config['default_value']; + } else { + // 根据数据类型获取值 + switch ($config['data_type']) { + case 'database': + $value = $this->getDatabaseFieldValueFromConfig($config, $student ?: []); + break; + case 'system': + $value = $this->getSystemFunctionValueFromConfig($config); + break; + case 'user_input': + default: + $value = $config['default_value'] ?: ''; + break; + } + } + + // 替换占位符(支持 {{placeholder}} 和 {placeholder} 格式) + $patterns = [ + '{{' . $placeholder . '}}', + '{' . $placeholder . '}' + ]; + + foreach ($patterns as $pattern) { + $terms = str_replace($pattern, $value, $terms); + } + } + + return $terms; + + } catch (\Exception $e) { + // 出现错误时返回原内容,记录日志 + \think\facade\Log::error('合同条款占位符替换失败', [ + 'contract_id' => $contractId, + 'contract_sign_id' => $contractSignId, + 'student_id' => $studentId, + 'error' => $e->getMessage() + ]); + return $terms; + } + } + + /** + * 从配置中获取数据库字段值 + * @param array $config 字段配置 + * @param array $student 学员信息 + * @return string + */ + private function getDatabaseFieldValueFromConfig($config, $student) + { + try { + $tableName = $config['table_name'] ?? ''; + $fieldName = $config['field_name'] ?? ''; + + if (empty($tableName) || empty($fieldName)) { + return ''; + } + + $value = ''; + + switch ($tableName) { + case 'school_student': + case 'students': + // 学员表:直接从学员信息获取 + $value = $student[$fieldName] ?? ''; + break; + + case 'school_customer_resources': + // 用户表:通过学员的user_id关联查询 + if (!empty($student['user_id'])) { + $value = Db::table('school_customer_resources') + ->where('id', $student['user_id']) + ->value($fieldName) ?: ''; + } + break; + + case 'school_order_table': + // 订单表:查询该学员最新的订单信息 + $value = Db::table('school_order_table') + ->where('student_id', $student['id']) + ->order('created_at', 'desc') + ->value($fieldName) ?: ''; + break; + + case 'school_personnel': + // 员工表:这里可能需要根据业务逻辑确定关联的员工 + $value = ''; + break; + + default: + \think\facade\Log::warning('不支持的数据库表', [ + 'table_name' => $tableName, + 'field_name' => $fieldName + ]); + break; + } + + // 格式化字段值 + $value = $this->formatFieldValue($fieldName, $value); + + return (string)$value; + + } catch (\Exception $e) { + \think\facade\Log::error('从配置获取数据库字段值失败', [ + 'config' => $config, + 'student_id' => $student['id'] ?? 0, + 'error' => $e->getMessage() + ]); + return ''; + } + } + + /** + * 从配置中获取系统函数值 + * @param array $config 字段配置 + * @return string + */ + private function getSystemFunctionValueFromConfig($config) + { + try { + $systemFunction = $config['system_function'] ?? ''; + + if (empty($systemFunction)) { + return ''; + } + + $value = ''; + + switch ($systemFunction) { + case 'current_date': + $value = date('Y-m-d'); + break; + case 'current_time': + $value = date('H:i:s'); + break; + case 'current_datetime': + $value = date('Y-m-d H:i:s'); + break; + case 'current_year': + $value = date('Y'); + break; + case 'current_month': + $value = date('m'); + break; + case 'current_day': + $value = date('d'); + break; + case 'random_number': + $value = mt_rand(100000, 999999); + break; + case 'contract_generate_time': + $value = date('Y-m-d H:i:s'); + break; + default: + \think\facade\Log::warning('不支持的系统函数', [ + 'system_function' => $systemFunction + ]); + break; + } + + return (string)$value; + + } catch (\Exception $e) { + \think\facade\Log::error('从配置获取系统函数值失败', [ + 'config' => $config, + 'error' => $e->getMessage() + ]); + return ''; + } + } + + /** + * 格式化字段值 + * 对特定类型的字段值进行格式化处理 + * + * @param string $fieldName 字段名 + * @param mixed $value 原始值 + * @return string 格式化后的值 + */ + private function formatFieldValue($fieldName, $value) + { + if (empty($value)) { + return ''; + } + + // 根据字段名进行特殊处理 + switch (true) { + case str_contains($fieldName, 'date') || str_contains($fieldName, 'time'): + // 日期时间字段格式化 + if (is_numeric($value)) { + return date('Y-m-d', $value); + } elseif (strtotime($value)) { + return date('Y-m-d', strtotime($value)); + } + break; + + case str_contains($fieldName, 'amount') || str_contains($fieldName, 'price'): + // 金额字段格式化 + return number_format((float)$value, 2); + + case str_contains($fieldName, 'phone'): + // 手机号格式化(可以添加脱敏处理) + return (string)$value; + + default: + // 默认返回字符串 + return (string)$value; + } + + return (string)$value; + } } \ No newline at end of file diff --git a/niucloud/app/service/core/sys/CoreFieldMappingService.php b/niucloud/app/service/core/sys/CoreFieldMappingService.php new file mode 100644 index 00000000..be497f56 --- /dev/null +++ b/niucloud/app/service/core/sys/CoreFieldMappingService.php @@ -0,0 +1,823 @@ +value = $value; + $this->service = $service; + $this->field_name = $field_name; + } + + /** + * 获取原始值 + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * 链式调用关系查询 + * @param string $relation_table 关系表名 + * @param string $display_field 显示字段 + * @param string $key_field 关键字段,默认为id + * @param array $conditions 额外查询条件 + * @param bool $use_cache 是否使用缓存 + * @return mixed + */ + public function withRelation(string $relation_table, string $display_field, string $key_field = 'id', array $conditions = [], bool $use_cache = true) + { + if ($this->value === null) { + return $this->value; + } + + try { + // 生成缓存键 + $cache_key = "relation_chain_{$this->field_name}_" . md5(serialize([ + 'value' => $this->value, + 'table' => $relation_table, + 'display_field' => $display_field, + 'key_field' => $key_field, + 'conditions' => $conditions + ])); + + if ($use_cache) { + $cached_value = Cache::get($cache_key); + if ($cached_value !== false && $cached_value !== null) { + return $cached_value; + } + } + + // 构建查询 + $query = Db::table($relation_table) + ->where($key_field, $this->value) + ->field($display_field); + + // 添加额外条件 + if (!empty($conditions)) { + $query->where($conditions); + } + + $result = $query->find(); + $converted_value = $result ? $result[$display_field] : $this->value; + + // 缓存结果 + if ($use_cache) { + Cache::tag(CoreFieldMappingService::$cache_tag_name)->set($cache_key, $converted_value, 3600); + } + + return $converted_value; + + } catch (Exception $e) { + trace($e->getMessage(), 'error'); + return $this->value; + } + } + + /** + * 转换为字符串时返回原始值 + * @return string + */ + public function __toString() + { + return (string)$this->value; + } + + /** + * 支持直接访问值的魔术方法 + * @param string $name + * @return mixed + */ + public function __get(string $name) + { + if ($name === 'value') { + return $this->value; + } + return null; + } +} + +/** + * 数据库字段映射服务层 + * + * 支持字段映射、枚举值自动转义、关系查询链式调用等功能 + * + * 使用示例: + * + * 1. 基本用法: + * ```php + * $mapping = new CoreFieldMappingService('users', ['id' => 1]); + * $name = $mapping->getValue('name'); // 获取用户名 + * ``` + * + * 2. 枚举值自动转义: + * ```php + * $mapping = new CoreFieldMappingService('users', ['id' => 1]); + * $mapping->setFieldEnum('status', [1 => '启用', 0 => '禁用']); + * $status = $mapping->getValue('status'); // 直接返回 '启用' 或 '禁用' + * ``` + * + * 3. 关系查询链式调用: + * ```php + * $mapping = new CoreFieldMappingService('orders', ['id' => 1]); + * $username = $mapping->getValueWithChain('user_id') + * ->withRelation('users', 'username'); // 获取用户名 + * ``` + * + * 4. 批量设置枚举和关系: + * ```php + * $mapping = new CoreFieldMappingService('orders'); + * $mapping->setFieldEnums([ + * 'status' => [1 => '已付款', 2 => '已发货', 3 => '已完成'], + * 'type' => [1 => '普通订单', 2 => '预售订单'] + * ]); + * $mapping->setFieldRelation('user_id', 'users', 'username'); + * ``` + * + * Class CoreFieldMappingService + * @package app\service\core\sys + */ +class CoreFieldMappingService extends BaseCoreService +{ + /** + * 缓存标签名 + * @var string + */ + public static $cache_tag_name = 'field_mapping'; + + /** + * 表名 + * @var string + */ + protected $table_name; + + /** + * 查询条件 + * @var array + */ + protected $conditions; + + /** + * 字段映射配置 + * @var array + */ + protected $field_mapping; + + /** + * 枚举映射配置 + * @var array + */ + protected $enum_mapping; + + /** + * 关系映射配置 + * @var array + */ + protected $relation_mapping; + + /** + * 构造函数 + * @param string $table_name 表名 + * @param array $conditions 查询条件 + * @param array $field_mapping 字段映射配置 + * @param array $enum_mapping 枚举映射配置 + * @param array $relation_mapping 关系映射配置 + */ + public function __construct(string $table_name, array $conditions = [], array $field_mapping = [], array $enum_mapping = [], array $relation_mapping = []) + { + parent::__construct(); + $this->table_name = $table_name; + $this->conditions = $conditions; + $this->field_mapping = $field_mapping; + $this->enum_mapping = $enum_mapping; + $this->relation_mapping = $relation_mapping; + } + + /** + * 获取字段映射关系 + * @return array + */ + public function getFieldMapping(): array + { + return $this->field_mapping; + } + + /** + * 设置字段映射关系 + * @param array $field_mapping + * @return $this + */ + public function setFieldMapping(array $field_mapping): self + { + $this->field_mapping = $field_mapping; + return $this; + } + + /** + * 设置查询条件 + * @param array $conditions + * @return $this + */ + public function setConditions(array $conditions): self + { + $this->conditions = $conditions; + return $this; + } + + /** + * 获取查询条件 + * @return array + */ + public function getConditions(): array + { + return $this->conditions; + } + + /** + * 从查询结果中获取指定字段的值 + * @param string $field_name 字段名 + * @param mixed $default_value 默认值 + * @param bool $use_cache 是否使用缓存 + * @param bool $auto_convert_enum 是否自动转义枚举值,默认true + * @param bool $return_wrapper 是否返回FieldValue包装对象以支持链式调用,默认false + * @return mixed|FieldValue + */ + public function getValue(string $field_name, $default_value = null, bool $use_cache = true, bool $auto_convert_enum = true, bool $return_wrapper = false) + { + try { + // 生成缓存键 + $cache_key = $this->generateCacheKey($field_name); + + if ($use_cache) { + // 尝试从缓存获取 + $cached_value = Cache::get($cache_key); + if ($cached_value !== false && $cached_value !== null) { + return $cached_value; + } + } + + // 检查字段映射 + $actual_field = $this->getActualFieldName($field_name); + + // 构建查询 + $query = Db::table($this->table_name); + + // 添加查询条件 + if (!empty($this->conditions)) { + $query->where($this->conditions); + } + + // 执行查询 + $result = $query->field($actual_field)->find(); + + if (empty($result)) { + return $default_value; + } + + $value = $result[$actual_field] ?? $default_value; + + // 自动转义枚举值 + if ($auto_convert_enum && $value !== null && $value !== $default_value && isset($this->enum_mapping[$field_name])) { + $enum_config = $this->enum_mapping[$field_name]; + $value = $enum_config[$value] ?? $value; + } + + // 缓存结果 + if ($use_cache && $value !== null) { + Cache::tag(self::$cache_tag_name)->set($cache_key, $value, 3600); // 缓存1小时 + } + + // 返回包装对象或原始值 + if ($return_wrapper) { + return new FieldValue($value, $this, $field_name); + } + + return $value; + + } catch (Exception $e) { + // 记录错误日志 + trace($e->getMessage(), 'error'); + return $default_value; + } + } + + /** + * 获取字段值并返回支持链式调用的包装对象 + * @param string $field_name 字段名 + * @param mixed $default_value 默认值 + * @param bool $use_cache 是否使用缓存 + * @param bool $auto_convert_enum 是否自动转义枚举值,默认true + * @return FieldValue + */ + public function getValueWithChain(string $field_name, $default_value = null, bool $use_cache = true, bool $auto_convert_enum = true): FieldValue + { + return $this->getValue($field_name, $default_value, $use_cache, $auto_convert_enum, true); + } + + /** + * 批量获取多个字段的值 + * @param array $field_names 字段名数组 + * @param bool $use_cache 是否使用缓存 + * @return array + */ + public function getValues(array $field_names, bool $use_cache = true): array + { + try { + $result = []; + + // 生成缓存键 + $cache_key = $this->generateCacheKey(implode(',', $field_names)); + + if ($use_cache) { + // 尝试从缓存获取 + $cached_result = Cache::get($cache_key); + if ($cached_result !== false && $cached_result !== null) { + return $cached_result; + } + } + + // 获取实际字段名 + $actual_fields = []; + foreach ($field_names as $field_name) { + $actual_fields[] = $this->getActualFieldName($field_name); + } + + // 构建查询 + $query = Db::table($this->table_name); + + // 添加查询条件 + if (!empty($this->conditions)) { + $query->where($this->conditions); + } + + // 执行查询 + $data = $query->field(implode(',', $actual_fields))->find(); + + if (!empty($data)) { + // 映射回原始字段名 + foreach ($field_names as $index => $field_name) { + $actual_field = $actual_fields[$index]; + $result[$field_name] = $data[$actual_field] ?? null; + } + } + + // 缓存结果 + if ($use_cache && !empty($result)) { + Cache::tag(self::$cache_tag_name)->set($cache_key, $result, 3600); // 缓存1小时 + } + + return $result; + + } catch (Exception $e) { + // 记录错误日志 + trace($e->getMessage(), 'error'); + return []; + } + } + + /** + * 获取实际字段名(考虑字段映射) + * @param string $field_name + * @return string + */ + protected function getActualFieldName(string $field_name): string + { + return $this->field_mapping[$field_name] ?? $field_name; + } + + /** + * 生成缓存键 + * @param string $field_name + * @return string + */ + protected function generateCacheKey(string $field_name): string + { + $conditions_hash = md5(serialize($this->conditions)); + return "field_mapping_{$this->table_name}_{$field_name}_{$conditions_hash}"; + } + + /** + * 清除缓存 + * @param string|null $field_name 指定字段名,为空则清除所有相关缓存 + * @return bool + */ + public function clearCache(string $field_name = null): bool + { + if ($field_name) { + $cache_key = $this->generateCacheKey($field_name); + return Cache::delete($cache_key); + } else { + return Cache::tag(self::$cache_tag_name)->clear(); + } + } + + /** + * 检查表是否存在 + * @return bool + */ + public function tableExists(): bool + { + try { + $tables = Db::query("SHOW TABLES LIKE '{$this->table_name}'"); + return !empty($tables); + } catch (Exception $e) { + trace($e->getMessage(), 'error'); + return false; + } + } + + /** + * 获取表的字段信息 + * @return array + */ + public function getTableFields(): array + { + try { + $cache_key = "table_fields_{$this->table_name}"; + + // 尝试从缓存获取 + $cached_fields = Cache::get($cache_key); + if ($cached_fields !== false && $cached_fields !== null) { + return $cached_fields; + } + + $fields = Db::query("DESCRIBE {$this->table_name}"); + + // 缓存字段信息 + Cache::tag(self::$cache_tag_name)->set($cache_key, $fields, 7200); // 缓存2小时 + + return $fields; + + } catch (Exception $e) { + trace($e->getMessage(), 'error'); + return []; + } + } + + /** + * 验证字段是否存在 + * @param string $field_name + * @return bool + */ + public function fieldExists(string $field_name): bool + { + $fields = $this->getTableFields(); + $actual_field = $this->getActualFieldName($field_name); + + foreach ($fields as $field) { + if ($field['Field'] === $actual_field) { + return true; + } + } + + return false; + } + + /** + * 设置枚举映射配置 + * @param array $enum_mapping + * @return $this + */ + public function setEnumMapping(array $enum_mapping): self + { + $this->enum_mapping = $enum_mapping; + return $this; + } + + /** + * 为单个字段设置枚举映射 + * @param string $field_name 字段名 + * @param array $enum_values 枚举值映射 + * @return $this + */ + public function setFieldEnum(string $field_name, array $enum_values): self + { + $this->enum_mapping[$field_name] = $enum_values; + return $this; + } + + /** + * 批量设置多个字段的枚举映射 + * @param array $field_enums 字段枚举映射数组,格式:['field_name' => ['value' => 'label']] + * @return $this + */ + public function setFieldEnums(array $field_enums): self + { + foreach ($field_enums as $field_name => $enum_values) { + $this->enum_mapping[$field_name] = $enum_values; + } + return $this; + } + + /** + * 获取枚举映射配置 + * @return array + */ + public function getEnumMapping(): array + { + return $this->enum_mapping; + } + + /** + * 设置关系映射配置 + * @param array $relation_mapping + * @return $this + */ + public function setRelationMapping(array $relation_mapping): self + { + $this->relation_mapping = $relation_mapping; + return $this; + } + + /** + * 为单个字段设置关系映射 + * @param string $field_name 字段名 + * @param string $relation_table 关系表名 + * @param string $display_field 显示字段 + * @param string $key_field 关键字段,默认为id + * @param array $conditions 额外查询条件 + * @return $this + */ + public function setFieldRelation(string $field_name, string $relation_table, string $display_field, string $key_field = 'id', array $conditions = []): self + { + $this->relation_mapping[$field_name] = [ + 'table' => $relation_table, + 'display_field' => $display_field, + 'key_field' => $key_field, + 'conditions' => $conditions + ]; + return $this; + } + + /** + * 批量设置多个字段的关系映射 + * @param array $field_relations 字段关系映射数组 + * @return $this + */ + public function setFieldRelations(array $field_relations): self + { + foreach ($field_relations as $field_name => $relation_config) { + if (is_array($relation_config) && isset($relation_config['table']) && isset($relation_config['display_field'])) { + $this->relation_mapping[$field_name] = $relation_config; + } + } + return $this; + } + + /** + * 获取关系映射配置 + * @return array + */ + public function getRelationMapping(): array + { + return $this->relation_mapping; + } + + /** + * 转换枚举值 + * @param string $field_name 字段名 + * @param mixed $value 原始值 + * @param bool $use_cache 是否使用缓存 + * @return mixed + */ + public function convertEnumValue(string $field_name, $value, bool $use_cache = true) + { + try { + // 检查是否配置了枚举映射 + if (!isset($this->enum_mapping[$field_name])) { + return $value; + } + + $enum_config = $this->enum_mapping[$field_name]; + + // 生成缓存键 + $cache_key = "enum_convert_{$field_name}_" . md5(serialize($value)); + + if ($use_cache) { + $cached_value = Cache::get($cache_key); + if ($cached_value !== false && $cached_value !== null) { + return $cached_value; + } + } + + // 执行枚举转换 + $converted_value = $enum_config[$value] ?? $value; + + // 缓存结果 + if ($use_cache) { + Cache::tag(self::$cache_tag_name)->set($cache_key, $converted_value, 3600); + } + + return $converted_value; + + } catch (Exception $e) { + trace($e->getMessage(), 'error'); + return $value; + } + } + + /** + * 转换关系ID值 + * @param string $field_name 字段名 + * @param mixed $value 关系ID值 + * @param bool $use_cache 是否使用缓存 + * @return mixed + */ + public function convertRelationValue(string $field_name, $value, bool $use_cache = true) + { + try { + // 检查是否配置了关系映射 + if (!isset($this->relation_mapping[$field_name])) { + return $value; + } + + $relation_config = $this->relation_mapping[$field_name]; + + // 验证关系配置 + if (!isset($relation_config['table']) || !isset($relation_config['display_field'])) { + return $value; + } + + // 生成缓存键 + $cache_key = "relation_convert_{$field_name}_" . md5(serialize($value)); + + if ($use_cache) { + $cached_value = Cache::get($cache_key); + if ($cached_value !== false && $cached_value !== null) { + return $cached_value; + } + } + + // 构建查询 + $relation_table = $relation_config['table']; + $display_field = $relation_config['display_field']; + $key_field = $relation_config['key_field'] ?? 'id'; + + $query = Db::table($relation_table) + ->where($key_field, $value) + ->field($display_field); + + // 添加额外条件 + if (isset($relation_config['conditions']) && is_array($relation_config['conditions'])) { + $query->where($relation_config['conditions']); + } + + $result = $query->find(); + $converted_value = $result ? $result[$display_field] : $value; + + // 缓存结果 + if ($use_cache) { + Cache::tag(self::$cache_tag_name)->set($cache_key, $converted_value, 3600); + } + + return $converted_value; + + } catch (Exception $e) { + trace($e->getMessage(), 'error'); + return $value; + } + } + + /** + * 批量转换字段值 + * @param array $data 原始数据 + * @param array $convert_fields 需要转换的字段列表 + * @param bool $use_cache 是否使用缓存 + * @return array + */ + public function convertValues(array $data, array $convert_fields = [], bool $use_cache = true): array + { + try { + if (empty($data)) { + return $data; + } + + $converted_data = $data; + + // 如果未指定转换字段,则根据配置自动确定 + if (empty($convert_fields)) { + $convert_fields = array_merge( + array_keys($this->enum_mapping), + array_keys($this->relation_mapping) + ); + } + + foreach ($convert_fields as $field_name) { + if (!isset($data[$field_name])) { + continue; + } + + $original_value = $data[$field_name]; + + // 枚举转换 + if (isset($this->enum_mapping[$field_name])) { + $converted_data[$field_name] = $this->convertEnumValue($field_name, $original_value, $use_cache); + } + // 关系转换 + elseif (isset($this->relation_mapping[$field_name])) { + $converted_data[$field_name] = $this->convertRelationValue($field_name, $original_value, $use_cache); + } + } + + return $converted_data; + + } catch (Exception $e) { + trace($e->getMessage(), 'error'); + return $data; + } + } + + /** + * 获取带转义的字段值 + * @param string $field_name 字段名 + * @param mixed $default_value 默认值 + * @param bool $convert 是否进行转义 + * @param bool $use_cache 是否使用缓存 + * @return mixed + */ + public function getConvertedValue(string $field_name, $default_value = null, bool $convert = true, bool $use_cache = true) + { + $value = $this->getValue($field_name, $default_value, $use_cache); + + if (!$convert || $value === null || $value === $default_value) { + return $value; + } + + // 枚举转换 + if (isset($this->enum_mapping[$field_name])) { + return $this->convertEnumValue($field_name, $value, $use_cache); + } + + // 关系转换 + if (isset($this->relation_mapping[$field_name])) { + return $this->convertRelationValue($field_name, $value, $use_cache); + } + + return $value; + } + + /** + * 批量获取带转义的字段值 + * @param array $field_names 字段名数组 + * @param bool $convert 是否进行转义 + * @param bool $use_cache 是否使用缓存 + * @return array + */ + public function getConvertedValues(array $field_names, bool $convert = true, bool $use_cache = true): array + { + $values = $this->getValues($field_names, $use_cache); + + if (!$convert || empty($values)) { + return $values; + } + + return $this->convertValues($values, $field_names, $use_cache); + } +} \ No newline at end of file diff --git a/uniapp/api/apiRoute.js b/uniapp/api/apiRoute.js index 5d743035..3699cf18 100644 --- a/uniapp/api/apiRoute.js +++ b/uniapp/api/apiRoute.js @@ -1734,15 +1734,26 @@ export default { }) }, - // 提交合同签署 + // 学员提交合同签署 async signStudentContract(data = {}) { - return await http.post('/student/contract/sign', { + return await http.post('/contract/signStudentContract', { contract_id: data.contract_id, + contract_sign_id: data.contract_sign_id, student_id: data.student_id, form_data: data.form_data, signature_image: data.signature_image, }) }, + + // 员工端提交合同签署 + async signStaffContract(data = {}) { + return await http.post('/contract/signStaffContract', { + contract_id: data.contract_id, + contract_sign_id: data.contract_sign_id, + form_data: data.form_data, + signature_image: data.signature_image, + }) + }, // 下载合同文件 async downloadStudentContract(data = {}) { diff --git a/uniapp/common/config.js b/uniapp/common/config.js index 2459f37b..c98a2253 100644 --- a/uniapp/common/config.js +++ b/uniapp/common/config.js @@ -1,6 +1,6 @@ // 环境变量配置 -// const env = 'development' -const env = 'prod' +const env = 'development' +// const env = 'prod' const isMockEnabled = false // 默认禁用Mock优先模式,仅作为回退 const isDebug = false // 默认启用调试模式 const devurl = 'http://localhost:20080/api' diff --git a/uniapp/pages-student/contracts/sign.vue b/uniapp/pages-student/contracts/sign.vue index d2b5c88b..49bf1809 100644 --- a/uniapp/pages-student/contracts/sign.vue +++ b/uniapp/pages-student/contracts/sign.vue @@ -38,14 +38,13 @@ {{ field.name }} * - {{ getPartyLabel(field) }} + {{ getPartyLabel(field) }} @@ -600,6 +599,23 @@ } }, + // 获取签名图片(从表单数据中提取签名字段) + getSignatureImage() { + // 查找签名类型的字段 + const signatureFields = this.formFields.filter(field => + field.data_type === 'signature' || field.data_type === 'sign_img' + ) + + // 返回第一个签名字段的值,如果没有则返回空字符串 + if (signatureFields.length > 0) { + const signatureField = signatureFields[0] + const fieldKey = signatureField.placeholder || signatureField.name + return this.formData[fieldKey] || '' + } + + return '' + }, + validateForm() { // 验证必填字段 for (const field of this.formFields) { @@ -637,16 +653,18 @@ // 员工端签署 response = await apiRoute.signStaffContract({ contract_id: this.contractId, - student_id: this.studentId, + contract_sign_id: this.contractSignId, form_data: this.formData, - personnel_id: this.userInfo?.id || 0 + signature_image: this.getSignatureImage() }) } else { // 学生端签署 response = await apiRoute.signStudentContract({ contract_id: this.contractId, + contract_sign_id: this.contractSignId, student_id: this.studentId, - form_data: this.formData + form_data: this.formData, + signature_image: this.getSignatureImage() }) }