Merge branch 'develop'

This commit is contained in:
kittapath 2024-12-19 14:28:34 +07:00
commit c074d88cae
6 changed files with 273 additions and 77 deletions

View file

@ -1,46 +0,0 @@
# use for local build with act
name: build-local
run-name: build-local ${{ github.actor }}
on:
workflow_dispatch:
env:
REGISTRY: docker.frappet.com
IMAGE_NAME: demo/bma-ehr-metadata-service
jobs:
# act workflow_dispatch -W .github/workflows/build-local.yaml --input IMAGE_VER=test-v6.1
build-local:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# skip Set up QEMU because it fail on act and container
- name: Gen Version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER='${GITHUB_REF/refs\/tags\//}'
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo '::set-output name=image_ver::'$IMAGE_VER
- name: Test Version
run: |
echo $GITHUB_REF
echo ${{ steps.gen_ver.outputs.image_ver }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# - name: Login in to registry
# uses: docker/login-action@v2
# with:
# registry: ${{env.REGISTRY}}
# username: ${{secrets.DOCKER_USER}}
# password: ${{secrets.DOCKER_PASS}}
- name: Build and load local docker image
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64
load: true
tags: ${{env.REGISTRY}}/${{env.IMAGE_NAME}}:${{ steps.gen_ver.outputs.image_ver }},${{env.REGISTRY}}/${{env.IMAGE_NAME}}:latest

View file

@ -1,43 +1,44 @@
name: release-dev
run-name: release-dev ${{ github.actor }}
name: release
run-name: release ${{ github.actor }}
on:
# push:
# tags:
# - 'v[0-9]+.[0-9]+.[0-9]+'
# tags-ignore:
# - '2.*'
# Allow run workflow manually from Action tab
push:
tags:
- "version-[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
env:
REGISTRY: docker.frappet.com
IMAGE_NAME: ehr/bma-ehr-recruit-service
DEPLOY_HOST: frappet.com
DEPLOY_PORT: 10102
COMPOSE_PATH: /home/frappet/docker/bma/bma-ehr-recruit
jobs:
# act workflow_dispatch -W .github/workflows/release.yaml --input IMAGE_VER=latest -s DOCKER_USER=admin -s DOCKER_PASS=FPTadmin2357 -s SSH_PASSWORD=FPTadmin2357
release-dev:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# skip Set up QEMU because it fail on act and container
# Gen Version try to get version from tag or inut
- name: Set output tags
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Gen Version
id: gen_ver
run: |
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER='${GITHUB_REF/refs\/tags\//}'
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo '::set-output name=image_ver::'$IMAGE_VER
- name: Test Version
if [[ $GITHUB_REF == 'refs/tags/'* ]]; then
IMAGE_VER=${{ steps.vars.outputs.tag }}
else
IMAGE_VER=${{ github.event.inputs.IMAGE_VER }}
fi
if [[ $IMAGE_VER == '' ]]; then
IMAGE_VER='test-vBeta'
fi
echo '::set-output name=image_ver::'$IMAGE_VER
- name: Check Version
run: |
echo $GITHUB_REF
echo ${{ steps.gen_ver.outputs.image_ver }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login in to registry
@ -46,7 +47,7 @@ jobs:
registry: ${{env.REGISTRY}}
username: ${{secrets.DOCKER_USER}}
password: ${{secrets.DOCKER_PASS}}
- name: Build and load local docker image
- name: Build and push docker image
uses: docker/build-push-action@v3
with:
context: .
@ -60,9 +61,46 @@ jobs:
host: ${{env.DEPLOY_HOST}}
username: frappet
password: ${{ secrets.SSH_PASSWORD }}
port: ${{env.DEPLOY_PORT}}
port: 10102
script: |
cd "${{env.COMPOSE_PATH}}"
docker compose pull
docker compose up -d
echo "${{ steps.gen_ver.outputs.image_ver }}"> success
- name: Notify Discord Success
if: success()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d '{
"embeds": [{
"title": "✅ Deployment Success!",
"description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Deployed by: `${{github.actor}}`",
"color": 3066993,
"footer": {
"text": "Release Notification",
"icon_url": "https://example.com/success-icon.png"
},
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
}]
}' \
${{ secrets.DISCORD_WEBHOOK }}
- name: Notify Discord Failure
if: failure()
run: |
curl -H "Content-Type: application/json" \
-X POST \
-d '{
"embeds": [{
"title": "❌ Deployment Failed!",
"description": "**Details:**\n- Image: `${{env.IMAGE_NAME}}`\n- Version: `${{ steps.gen_ver.outputs.image_ver }}`\n- Attempted by: `${{github.actor}}`",
"color": 15158332,
"footer": {
"text": "Release Notification",
"icon_url": "https://example.com/failure-icon.png"
},
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
}]
}' \
${{ secrets.DISCORD_WEBHOOK }}

View file

@ -1,5 +1,9 @@
using Amazon.S3.Model;
// using BMA.EHR.Extensions;
using BMA.EHR.MetaData.Service.Models;
using BMA.EHR.Profile.Service.Models.HR;
//using BMA.EHR.Extensions;
using BMA.EHR.Recruit.Service.Core;
using BMA.EHR.Recruit.Service.Data;
using BMA.EHR.Recruit.Service.Extensions;
@ -12,6 +16,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MySqlConnector;
using MySqlX.XDevAPI.Common;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OfficeOpenXml;
@ -23,6 +28,7 @@ using System.Net;
using System.Net.WebSockets;
using System.Security.Claims;
using System.Text;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace BMA.EHR.Recruit.Service.Controllers
{
@ -44,7 +50,8 @@ namespace BMA.EHR.Recruit.Service.Controllers
private readonly PermissionRepository _permission;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<RecruitController> _logger;
//private readonly DateTimeExtension;
#endregion
#region " Constructor and Destructor "
@ -56,7 +63,9 @@ namespace BMA.EHR.Recruit.Service.Controllers
RecruitService recruitService,
IHttpContextAccessor httpContextAccessor,
ILogger<RecruitController> logger,
PermissionRepository permission)
PermissionRepository permission
//DateTimeExtension extensions
)
{
_context = context;
_contextMetadata = contextMetadata;
@ -66,6 +75,7 @@ namespace BMA.EHR.Recruit.Service.Controllers
_httpContextAccessor = httpContextAccessor;
_logger = logger;
_permission = permission;
//_extensions = extensions;
}
#endregion
@ -189,6 +199,15 @@ namespace BMA.EHR.Recruit.Service.Controllers
}
}
private class ExamInfo
{
public string PositionName { get; set; }
public string Gender { get; set; }
public int Age { get; set; }
public string Degree { get; set; }
public string Result { get; set; }
}
#endregion
#region " Ex. Upload, Download and Delete file "
@ -2200,6 +2219,186 @@ namespace BMA.EHR.Recruit.Service.Controllers
#endregion
#region " Report "
/// <summary>
/// รายงานจำนวนผู้เข้าสอบแข่งขันเพื่อบรรจุเข้ารับราชการเป็นข้าราชการ กทม. สามัญ
/// </summary>
/// <param name="year">ปีงบประมาณ</param>
/// <returns></returns>
/// <response code="200">เมื่อทำการอ่านข้อมูลจำนวนผู้เข้าสอบแข่งขันเพื่อบรรจุเข้ารับราชการเป็นข้าราชการ กทม. สามัญสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("report1"), DisableRequestSizeLimit]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> report1(string year)
{
try
{
var year_ = int.Parse(year) - 543;
List<ExamInfo> header = new List<ExamInfo>();
using (var cmd = _context.Database.GetDbConnection().CreateCommand())
{
var sb = new StringBuilder();
cmd.CommandTimeout = 0;
cmd.Parameters.Clear();
sb.Clear();
sb.Append(" SELECT * ");
sb.Append(" FROM exam_info ");
sb.Append(" Where score_year = @year");
sb.Append(" ORDER BY Position_name ASC");
cmd.CommandText = sb.ToString();
cmd.Parameters.Add(new MySqlParameter("@year", MySqlDbType.Int32) { Value = year_ });
_context.Database.OpenConnection();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var examInfo = new ExamInfo
{
PositionName = reader["Position_name"].ToString(),
Gender = reader["Gender"].ToString(),
Age = DateTimeExtension.CalculateAge(Convert.ToDateTime(reader["Dateofbirth"]), 0, 0),
Degree = reader["Degree"].ToString(),
};
header.Add(examInfo);
}
}
}
var groupedResult = header
.GroupBy(x => new
{
x.PositionName,
x.Gender,
x.Age,
x.Degree
})
.Select(group => new
{
positionName = group.Key.PositionName,
gender = group.Key.Gender,
age = group.Key.Age.ToString().ToThaiNumber(),
degree = group.Key.Degree,
amount = group.Count().ToString().ToThaiNumber()
})
.ToList();
var result = new
{
template = "recruit-officer-pre",
reportName = "xlsx-report",
data = new {
year = year.ToString().ToThaiNumber(),
date = DateTime.Now.ToThaiShortDateWithPrefix().ToThaiNumber(),
list = groupedResult
}
};
return Success(result);
}
catch (Exception ex)
{
return Error(ex);
}
}
/// <summary>
/// รายงานจำนวนผู้สอบผ่านแข่งขันเพื่อบรรจุเข้ารับราชการเป็นข้าราชการ กทม. สามัญ
/// </summary>
/// <param name="year">ปีงบประมาณ</param>
/// <returns></returns>
/// <response code="200">เมื่อทำการอ่านข้อมูลจำนวนผู้สอบผ่านแข่งขันเพื่อบรรจุเข้ารับราชการเป็นข้าราชการ กทม. สามัญสำเร็จ</response>
/// <response code="401">ไม่ได้ Login เข้าระบบ</response>
/// <response code="500">เมื่อเกิดข้อผิดพลาดในการทำงาน</response>
[HttpGet("report2"), DisableRequestSizeLimit]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<ResponseObject>> report2(string year)
{
try
{
var year_ = int.Parse(year) - 543;
List<ExamInfo> header = new List<ExamInfo>();
using (var cmd = _context.Database.GetDbConnection().CreateCommand())
{
var sb = new StringBuilder();
cmd.CommandTimeout = 0;
cmd.Parameters.Clear();
sb.Clear();
sb.Append(" SELECT * ");
sb.Append(" FROM exam_info ");
sb.Append(" Where score_year = @year");
sb.Append(" AND result = 'ผ่าน'");
sb.Append(" ORDER BY Position_name ASC");
cmd.CommandText = sb.ToString();
cmd.Parameters.Add(new MySqlParameter("@year", MySqlDbType.Int32) { Value = year_ });
_context.Database.OpenConnection();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var examInfo = new ExamInfo
{
PositionName = reader["Position_name"].ToString(),
Gender = reader["Gender"].ToString(),
Age = DateTimeExtension.CalculateAge(Convert.ToDateTime(reader["Dateofbirth"]), 0, 0),
Degree = reader["Degree"].ToString(),
Result = reader["Result"].ToString()
};
header.Add(examInfo);
}
}
}
var groupedResult = header
.GroupBy(x => new
{
x.PositionName,
x.Gender,
x.Age,
x.Degree,
x.Result
})
.Select(group => new
{
positionName = group.Key.PositionName,
gender = group.Key.Gender,
age = group.Key.Age.ToString().ToThaiNumber(),
degree = group.Key.Degree,
result = group.Key.Result,
amount = group.Count().ToString().ToThaiNumber()
})
.ToList();
var result = new
{
template = "recruit-officer-pass",
reportName = "xlsx-report",
data = new {
year = year.ToString().ToThaiNumber(),
date = DateTime.Now.ToThaiShortDateWithPrefix().ToThaiNumber(),
list = groupedResult
}
};
return Success(result);
}
catch (Exception ex)
{
return Error(ex);
}
}
#endregion
#endregion
}
}

View file

@ -66,6 +66,11 @@ namespace BMA.EHR.Recruit.Service.Extensions
var yy = value.Year < 2400 ? value.Year + 543 : value.Year;
return $"{value.Day} {value.ToString("MMM", _culture.DateTimeFormat)} {yy.ToString().Right(2)}";
}
public static string ToThaiShortDateWithPrefix(this DateTime value)
{
var yy = value.Year < 2400 ? value.Year + 543 : value.Year;
return $"วันที่ {value.Day} {value.ToString("MMM", _culture.DateTimeFormat)} {yy.ToString().Right(2)}";
}
public static string ToThaiDate(this DateTime value)
{

View file

@ -74,12 +74,12 @@ BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String));
BsonSerializer.RegisterSerializer(new DateTimeSerializer(BsonType.String));
// Register DbContext
var defaultConnection = builder.Configuration.GetConnectionString("DefaultConnection");
var recruitConnection = builder.Configuration.GetConnectionString("RecruitConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(defaultConnection, ServerVersion.AutoDetect(defaultConnection)), ServiceLifetime.Transient);
var metadataConnection = builder.Configuration.GetConnectionString("MetadataConnection");
options.UseMySql(recruitConnection, ServerVersion.AutoDetect(recruitConnection)), ServiceLifetime.Transient);
var defaultConnection = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<MetadataDbContext>(options =>
options.UseMySql(metadataConnection, ServerVersion.AutoDetect(metadataConnection)), ServiceLifetime.Transient);
options.UseMySql(defaultConnection, ServerVersion.AutoDetect(defaultConnection)), ServiceLifetime.Transient);
// Add config CORS
builder.Services.AddCors(options => options.AddDefaultPolicy(builder =>

View file

@ -14,8 +14,8 @@
"AllowedHosts": "*",
"ConnectionStrings": {
"MongoConnection": "mongodb://admin:adminVM123@127.0.0.1:27017",
"DefaultConnection": "server=192.168.1.80;user=root;password=adminVM123;port=3306;database=bma_recruit_demo;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;",
"MetadataConnection": "server=192.168.1.80;user=root;password=adminVM123;database=bma_ehr_demo;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;"
"DefaultConnection": "server=192.168.1.80;user=root;password=adminVM123;port=3306;database=bma_ehr_demo;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;",
"RecruitConnection": "server=192.168.1.80;user=root;password=adminVM123;database=bma_recruit_demo;Convert Zero Datetime=True;Allow User Variables=true;Pooling=True;"
},
"Jwt": {
"Key": "HP-FnQMUj9msHMSD3T9HtdEnphAKoCJLEl85CIqROFI",