refactor(app): 优化邮件模板

master
cody 4 weeks ago
parent c70f62703e
commit b273386bc2

@ -290,9 +290,9 @@ class CourseContentController extends BaseController
if (!in_array('课程主题', $keyList)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '课程主题字段不存在']);
}
if (!in_array('主题方向', $keyList)) {
return $this->fail([ResponseCode::ERROR_BUSINESS, '主题方向字段不存在']);
}
// if (!in_array('主题方向', $keyList)) {
// return $this->fail([ResponseCode::ERROR_BUSINESS, '主题方向字段不存在']);
// }
// if (!in_array('上课地点', $keyList)) {
// return $this->fail([ResponseCode::ERROR_BUSINESS, '上课地点字段不存在']);
// }

@ -17,6 +17,7 @@ use App\Models\CourseContentCheck;
use App\Models\CourseContentEvaluation;
use App\Models\CourseContentEvaluationForm;
use App\Models\CourseSign;
use App\Models\CourseType;
use App\Models\Notice;
use App\Models\Order;
use App\Models\User;
@ -553,13 +554,13 @@ class CourseController extends CommonController
{
$all = \request()->all();
$messages = [
// 'longitude.required' => '经度必填',
// 'latitude.required' => '纬度必填',
// 'longitude.required' => '经度必填',
// 'latitude.required' => '纬度必填',
'course_content_id.required' => '课表id必填',
];
$validator = Validator::make($all, [
// 'longitude' => 'required',
// 'latitude' => 'required',
// 'longitude' => 'required',
// 'latitude' => 'required',
'course_content_id' => 'required'
], $messages);
if ($validator->fails()) {
@ -588,8 +589,8 @@ class CourseController extends CommonController
'course_content_id' => $all['course_content_id'],
'course_id' => $courseContent->course_id,
'user_id' => $this->getUserId(),
'longitude' => $all['longitude']??'',
'latitude' => $all['latitude']??'',
'longitude' => $all['longitude'] ?? '',
'latitude' => $all['latitude'] ?? '',
]);
// 同日期课程批量签到
$batch_sign = request('batch_sign', 0);
@ -639,12 +640,12 @@ class CourseController extends CommonController
$all = \request()->all();
$messages = [
'longitude.required' => '经度必填',
// 'latitude.required' => '纬度必填',
// 'course_id.required' => '课表id必填',
// 'latitude.required' => '纬度必填',
// 'course_id.required' => '课表id必填',
];
$validator = Validator::make($all, [
// 'longitude' => 'required',
// 'latitude' => 'required',
// 'longitude' => 'required',
// 'latitude' => 'required',
'course_id' => 'required'
], $messages);
if ($validator->fails()) {
@ -675,8 +676,8 @@ class CourseController extends CommonController
CourseContentCheck::create([
'course_id' => $all['course_id'],
'user_id' => $this->getUserId(),
'longitude' => $all['longitude']??'',
'latitude' => $all['latitude']??'',
'longitude' => $all['longitude'] ?? '',
'latitude' => $all['latitude'] ?? '',
]);
return $this->success('课程签到成功');
}
@ -798,11 +799,32 @@ class CourseController extends CommonController
}
});
if (isset($all['type']) && $all['type'] == 2) {
$list = $list->orderBy('letter')
->paginate(10);
$list = $list->orderBy('letter')->paginate(10);
} else {
$list = $list->orderBy('letter')->paginate(20);
}
// 获取当前用户参与报名
$userCourseSigns = $this->getUser()->courseSigns->where('status', 1)->get();
// 获取当前用户允许的课程体系
$open_course_types = explode(',', $this->getUser()->open_course_types);
foreach ($list as $user) {
$user->open_mobile = false;
// 只对开放的课程体系和本班人员开放手机号
// 获取本班的课程
$courseIds = $user->courseSigns->where('status', 1)->pluck('course_id')->toArray();
// 判断当前用户和$user课程是否存在交集
if (count(array_intersect($courseIds, $userCourseSigns->pluck('course_id')->toArray())) > 0) {
$user->open_mobile = true;
continue;
}
// 获取用户的课程体系
$coursesTypeIds = Course::whereIn('id', $userCourseSigns->pluck('course_id'))->pluck('type')->toArray();
// 判断当前用户和$user课程体系是否存在交集
if (count(array_intersect($coursesTypeIds, $open_course_types)) > 0) {
$user->open_mobile = true;
}
}
$teacher = [];
if (isset($all['type']) && $all['type'] == 2) {
$course = Course::find($all['course_id']);

@ -11,6 +11,7 @@ use App\Models\AppointmentType;
use App\Models\Banner;
use App\Models\Company;
use App\Models\Config;
use App\Models\CourseType;
use App\Repositories\YuanheRepository;
use Illuminate\Support\Facades\Validator;
@ -40,7 +41,9 @@ class OtherController extends CommonController
})->where('show_front', 1)->get();
// 场地类型
$appointment_type = AppointmentType::get();
return $this->success(compact('config', 'appointment', 'appointment_type'));
// 获取开放手机号的课程体系
$course_types_open_mobile = CourseType::where('open_mobile', 1)->get();
return $this->success(compact('config', 'appointment', 'appointment_type', 'course_types_open_mobile'));
}
/**

@ -154,6 +154,7 @@ class UserController extends CommonController
* @OA\Parameter(name="idcard", in="query", @OA\Schema(type="string"), description="身份证号码"),
* @OA\Parameter(name="plate", in="query", @OA\Schema(type="string"), description="车牌号多个英文逗号分隔"),
* @OA\Parameter(name="type", in="query", @OA\Schema(type="string"), description="人才类型"),
* @OA\Parameter(name="open_course_types", in="query", @OA\Schema(type="string"), description="开放手机号的课程体系多个英文逗号分隔"),
* @OA\Response(
* response=200,
* description="操作成功"

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('course_types', function (Blueprint $table) {
// 是否开放手机号
$table->boolean('open_mobile')->default(false)->nullable()->comment('是否开放手机号');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('course_types', function (Blueprint $table) {
//
});
}
};

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('open_course_types')->nullable()->comment('开放手机号的课程体系');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
//
});
}
};

@ -1,96 +0,0 @@
### 供需发布模块数据结构与功能分析
#### 一、数据结构
- **表:`supply_demands`(供需主表)**
- 字段:`id`, `user_id`, `title`, `type`(1=供应,2=需求), `content`, `tag`, `wechat`, `mobile`, `email`, `status`(0待审核/1通过/2拒绝/3退回修改/4永久隐藏), `view_count`, `contact_count`, `expire_time`, `public_way`(布尔实际业务含义为1/2/3), `file_ids`(JSON), `contact_name`, `timestamps`, `deleted_at`
- 关系:
- `user`: 发布者(`hasOne User(id=user_id)`
- `keeps`: 收藏(`hasMany SupplyDemandKeep(supply_demand_id=id)`
- 虚拟属性:`files`(根据 `file_ids` 关联 `Upload` 列表)
- **表:`supply_demand_keeps`(收藏表)**
- 字段:`id`, `user_id`, `supply_demand_id`, `timestamps`, `deleted_at`
- 关系:
- `user`: 收藏者(`hasOne User(id=user_id)`
- `supplyDemand`: 被收藏的供需(`hasOne SupplyDemand(id=supply_demand_id)`
- **表:`dialogues`(会话表)**
- 字段:`id`, `user_id`, `to_user_id`, `supply_demand_id`, `last_content`, `last_datetime`, `timestamps`, `deleted_at`
- 关系:
- `user`: 会话发起方(`hasOne User(id=user_id)`
- `toUser`: 会话接收方(`hasOne User(id=to_user_id)`
- `supplyDemand`: 关联供需(`hasOne SupplyDemand(id=supply_demand_id)`
- **表:`messages`(消息表)**
- 字段:`id`, `dialogue_id`, `user_id`, `to_user_id`, `supply_demand_id`, `content`, `is_read`, `timestamps`, `deleted_at`
- 关系:
- `user`/`toUser`: 发送方/接收方(`hasOne User`
- `dialogue`: 所属会话(`hasOne Dialogue(id=dialogue_id)`
- `supplyDemand`: 关联供需(`hasOne SupplyDemand(id=supply_demand_id)`
- **表:`uploads`(附件表)**
- 字段:`id`, `belongs_type`, `belongs_id`, `original_name`, `folder`, `name`, `extension`, `size`, `creator_type`, `creator_id`, `timestamps`, `deleted_at`
- 用途:`supply_demands.file_ids` 存放附件 `id` 列表,模型通过 `files` 访问器取回 `Upload` 集合
说明:`public_way` 迁移中为 boolean但注释为 1/2/3 三种模式1直接公开/2私信后公开/3不公开。当前以 0/1 存储,若需完整三态应在后续迁移中改为 tinyInteger。
#### 二、功能说明(`SupplyDemandController`
- **列表 `index`**:按类型、状态、关键词、是否只看自己、有效期(有效/失效)筛选,分页排序;关联返回 `user` 基本信息。
- **详情 `detail`**:按 `id` 查询,返回 `user`,并自增 `view_count`;附带当前用户对该供需的已发私信次数。
- **保存 `save`**:新增或更新(新增时绑定当前用户并短信通知管理员),使用事务保存,支持 `file_ids`、`expire_time`、`contact_name` 等字段。
- **删除 `destroy`**:按 `id` 软删除。
- **发私信 `sendMessage`**
- 若带 `supply_demand_id` 则自增浏览量;
- 无会话则创建会话;
- 限流:每天发送条数不超过配置 `message_limit`
- 反骚扰:自己连续发送后需等待对方回复;
- 保存消息并更新会话最后内容与时间。
- **消息列表 `messageList`**:按 `to_user_id` 定位当前和对方的会话,分页返回消息(含双方用户信息)。
- **会话列表 `dialogues`**:返回与当前用户相关的会话(发起或接收),含双方用户与关联供需。
- **收藏相关**
- `keepIndex`:我的收藏列表;
- `keepSupplyDemand`:收藏,去重创建;
- `unKeepSupplyDemand`:取消收藏。
#### 三、核心业务要点
- 审核机制:多状态闭环(待审/通过/拒绝/退回/隐藏)。
- 私信策略:限流 + 反骚扰(需对方回复后再发)。
- 有效期:支持有效/失效筛选。
- 公开模式当前存储为布尔0/1业务含义为三态后续建议迁移调整。
- 数据统计:浏览数 `view_count`、联系数 `contact_count`(可按消息交互推导)。
- 附件:`file_ids` JSON + `files` 访问器联表读取。
#### 四、测试数据生成目标
`supply_demands` 为起点,为每条主记录自动生成:
- 合理的 `uploads` 附件0~3 个),`file_ids` 同步写入;
- 真实的会话 `dialogues`0~2 个),双方用户随机;
- 合法的消息序列 `messages`1~5 条),严格交替往来确保不违反“需对方回复”约束;
- 合理的收藏 `supply_demand_keeps`0~5 条,去重);
- 统计字段:`view_count` ≥ 消息条数,`contact_count` 依据消息往来推导。
#### 五、两种生成方式对比
- **方式A数据库填充Seeder**
- 优点:
- 一次性执行、可集成到 CI 或初始化流程;
- 可与 `DatabaseSeeder` 串联;
- 便于多环境批量重置数据。
- 缺点:
- 运行参数(数量、用户规模)固定或需改代码;
- 无交互,临时性需求需改代码或 env。
- **方式BArtisan 命令Console Command**
- 优点:
- 支持运行参数(如 `--count`、`--users`),灵活生成规模;
- 可多次按需执行,便于演示或局部补数;
- 缺点:
- 需要单独维护命令逻辑;
- 不会被自动纳入 `db:seed` 的全局流程。
推荐:开发/演示期使用命令B灵活试验集成测试或初始化环境使用 SeederA
Loading…
Cancel
Save