liziyu 发布的文章

Go 包的概念

  1. 把相同的功能放到一个目录,称之为包
  2. 包可以被其他的包引用
  3. main包用来生成可执行文件,每个程序只有一个main包
  4. 包可以提高代码的可复用性

Go 包 的特征

一个文件夹下只能有一个package。

• import后面的其实是GOPATH开始的相对目录路径,包括最后一段。但由于一个目录下只能有一个package,所以import一个路径就等于是import了这个路径下的包。
• 注意,这里指的是“直接包含”的go文件。如果有子目录,那么子目录的父目录是完全两个包。
• 比如你实现了一个计算器package,名叫calc,位于calc目录下;但又想给别人一个使用范例,于是在calc下可以建个example子目录(calc/example/),这个子目录里有个example.go(calc/example/example.go)。此时,example.go可以是main包,里面还可以有个main函数。

一个package的文件不能在多个文件夹下。

在 Golang 的文档中,Language Specification 页面,Package clause 下,指明了 A set of
files sharing the same PackageName form the implementation of a
package. An implementation may require that all source files for a
package inhabit the same directory.也就是说,一个包所有的文件,必须位于同一个目录下

•如果多个文件夹下有重名的package,它们其实是彼此无关的package。
•如果一个go文件需要同时使用不同目录下的同名package,需要在import这些目录时为每个目录指定一个package的别名。

包名自然可以和文件夹名不一样,毕竟一个是导入路径,一个是包名 但不建议这么做,这样容易造成调用这个包的人,无法快速知道这个包的名称是什么
至于为什么不用目录名作为包名,我想也正如大家所说,为了避免目录中出现奇怪的字符,也为了调用者方便使用 在 Golang 的文档中,
Language Specification 页面,Import declarations 下,有这样的说明 在Go
语言规范官方文档中有对PackageName和ImportPath的具体描述:
ImportDecl       = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec       = [ "." | PackageName ] ImportPath .
ImportPath       = string_lit .
The PackageName is used in qualified identifiers to access exported
identifiers of the package within the importing source file. It is
declared in the file block. If the PackageName is omitted, it defaults
to the identifier specified in the package clause of the imported
package. If an explicit period (.) appears instead of a name, all the
package’s exported identifiers declared in that package’s package
block will be declared in the importing source file’s file block and
must be accessed without a qualifier.
也就是说,在执行导入的时候,若不手动定义包名,则从导入路径的源码文件中的 package 行获取包名,也即目录名和包名没有直接的关系。

结论

1、import 导入的参数是路径,而非包名。
2、尽管习惯将包名和目录名保证一致,但这不是强制规定;
3、在代码中引用包成员时,使用包名而非目录名;
4、同一目录下,所有源文件必须使用相同的包名称(因为导入时使用绝对路径,所以在搜索路径下,包必须有唯一路径,但无须是唯一名字);
5、至于文件名,更没啥限制(扩展名为.go);

中间件代码

func Cors() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 这里可以用*,也可以用你指定的域名
        c.Header("Access-Control-Allow-Origin", "*")
        // 允许头部参数
        c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token")
        // 允许的方法
        c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
        c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
        c.Header("Access-Control-Allow-Credentials", "true")

        method := c.Request.Method
        //放行OPTIONS方法
        if method == "OPTIONS" {
            c.AbortWithStatus(http.StatusOK)
        }
        // 处理请求
        c.Next()
    }
}

使用

// g : *gin.Engine
g.Use(Cors())




<?php
use PhpOffice\PhpSpreadsheet\IOFactory as PHPExcel_IOFactory;
/**
 * 读取excel文件内容
 * @param string $filename  完整的文件路径
 * @return array 读取表格的结果数据
 */
function readExcelFile($filename)
{
    $fileParts = pathinfo($filename);
    $filetype = strtolower($fileParts['extension']);
    if (strtolower($filetype)=='xls') {
        $objReader = PHPExcel_IOFactory::createReader('Xls');
    } elseif (strtolower($filetype)=='xlsx') {
        $objReader = PHPExcel_IOFactory::createReader('Xlsx');
    } elseif (strtolower($filetype)=='csv') {
        $objReader = PHPExcel_IOFactory::createReader('Csv')
                        ->setDelimiter(',')
                        ->setInputEncoding('GBK') //处理csv读取中文异常问题
                        ->setEnclosure('"');
    }
    $objReader->setReadDataOnly(true);
    $objPHPExcel = $objReader->load($filename);
    $objWorksheet = $objPHPExcel->getActiveSheet();
    $highestRow = $objWorksheet->getHighestRow(); // 获取总行数
    $highestColumn = $objWorksheet->getHighestColumn();// 获取最大列号
    $excelResult = [];
    // 从第2行开始读取
    $startRow = 2;
    for ($j = $startRow; $j <= $highestRow; $j++) {
        // 从A列读取数据
        for ($k = 'A'; $k <= $highestColumn; $k++) {
            // 读取单元格
            $excelResult[$j][$k] = (string)$objWorksheet->getCell("$k$j")->getValue();
        }
    }
    return $excelResult;
}

在 Go 中,defer 语句和 return 语句的执行顺序是相对固定的。当一个函数中同时存在 defer 语句和 return 语句时,它们的执行顺序如下:

1、当遇到 defer 语句时,会先计算并保存相关的表达式和参数(如果有的话),但不会立即执行这些被延迟的操作。相当于将这些操作入栈,以便稍后执行。
2、接下来,return 语句会计算返回值(如果有的话),但不会立即返回函数调用者。
3、程序会执行被延迟的 defer 语句(按照后进先出的顺序),这些被延迟的操作可能会修改函数的返回值。
4、最后,函数会返回到调用者,并将之前计算的返回值传递给调用者。
简而言之,defer 语句和 return 语句的执行顺序是 defer -> return -> defer 中的延迟操作。

让我们通过一个示例来说明这个执行顺序:

func example() int {
    defer fmt.Println("defer 1")
    defer fmt.Println("defer 2")

    fmt.Println("before return")
    return 42
}

在这个示例中,当调用 example() 函数时,输出的顺序将如下:

before return
defer 2
defer 1

我们可以看到,fmt.Println("before return") 语句会在 return 语句之前执行。然后,被延迟的 defer 语句按照后进先出的顺序执行,即先执行 "defer 2",然后执行 "defer 1"。

需要注意的是,虽然 defer 语句在 return 语句之前执行,但它们可以访问并修改函数中的变量。这是因为 defer 语句中的匿名函数会捕获并保存相关的变量引用,以便在被执行时使用。因此,当 defer 语句中的函数修改变量时,这些修改会在最终的返回值中反映出来。

背景:

在工作中遇到按位或组合权限串。一直不是特别明白。今天终于花了半个下午的时间搞明白其中的道理。
首先每一个权限数都是2的N次方数
如:

k1=2 ; //添加
k2=4 ; //删除
k3=8; //修改
...

如此定义功能权限数,当需要组合权限时,就需要对各个所拥有的权限数按位或了。
如:

purview = k2|k3; // 赋给添加和删除权限

当需要判断在权限串中是否拥有某一权限时,就需要进行按位与。
如:

 if((purview & k1) >0)//判断此权限串是否拥有添加权限,结果>0 则进入if语句代码块中

  {

         ....

  } 


说到这里肯定会有疑问了,别急我来细细讲解。

第一,2的8位二进制值为00000010

4的8位二进制值为00000100
8的8位二进制值为00001000

第二,当对8和4进行按位或操作后,结果为:

4|8 = 12
00000100 |00001000 = 00001100

为什么会是这样呢?在进行按位或操作时:
00000001|00000001=00000001;
00000001|00000000=00000001;
00000000|00000000=00000000
也就是说除了0|0结果是0外,其它运算结果的都是1
所以:
00000100 |00001000 = 00001100
也就说上面的 purview = k2|k3 的二进制值结果是:
purview =00000100 |00001000 =00001100了

第三,当对8和4进行按位与操作后,结果为:

4&8=0
00000100 &00001000 = 00000000

同样为什么会是这样呢?在进行按位与操作时:
00000001&00000001=00000001;
00000001&00000000=00000000;
00000000&00000000=00000000;
也就是说除了1&1结果是1外,其它运算结果的都是0
所以:
00000100 &00001000 = 00000000

上面的if((purview & k1) >0)的结果就是 00001100&00000010 =00000000 也就是(0>0)=false
同样if((purview & k2) >0)的结果就是 00001100&00000100 =00000100=4=k2 也就是 (4>0)=true

这样我们就理解组合权限串和判断权限的原理了,说白了就是对2N次方数的按位与和按位或。

应用场景

比如在做一些社交类的需求中,经常会遇到那种点赞,推荐,明天再加个置顶的,简直没完没了,总不能今天加个字段,明天再加个吧;这时候位运算就派上用用场了,每个属性一个2N次方数,枚举定义下去,这样一个字段即可解决,新来一个属性再加个枚举即可。

核心代码

public class Test
{
    /// <summary>
    /// 计算权限总和
    /// </summary>
    /// <param name="OldStatus">数据库现有权限总和</param>
    /// <param name="Action">取消或设置操作(0取消,1设置)</param>
    /// <param name="StatusVal">设置操作对应的状态值</param>
    /// <returns></returns>
    public static int ReturnStatusTotal(int OldStatus, int Action, int StatusVal)
    {
        int NewStatus = OldStatus;
        if (Action == 1)
        {
            //判断此权限串是否拥有相应操作,不包括时加入权限
            if ((OldStatus & StatusVal) <= 0)
            {
                NewStatus = OldStatus | StatusVal;
            }
        }
        else
        {
            //判断此权限串是否拥有相应操作,有权限时移除权限
            if ((OldStatus & StatusVal) > 0)
            {
                NewStatus = OldStatus & ~StatusVal;
            }
        }

        return NewStatus;
    }
}

NewStatus = CommonUtilities.ReturnStatusTotal(OldStatus, objRequest.Action, (int)StatusEnum.ZhiDing);

转自: 【程序员编程日记】公众号

默认使用方式是需要两个文件:模型文件,规则文件。这里可以直接抄代码
Editor | Casbin,这里选择RBAC模型的。
在写管理系统时,不会将规则保存在文件中,会存入数据库中使用。这个时候可能就需要我们自己写一个适配器来转换它的存储方式Policy的存储 | Casbin

QQ20231129-101300@2x.png

在进行匹配判断时,是将g类型的规则转换为p类型的规则,图中的例子,就是(g,42,1)= >(p,1,xxx)。

作者:往昔不悔
链接:https://juejin.cn/post/7164667666773311501

在Java项目中,通常会使用一种基于分层架构的设计模式,将代码按照不同的职责划分到不同的包或模块中。在这种设计模式中,常见的包括DAO(数据访问对象)、Service(服务层)和Domain(领域模型)。

1、DAO(数据访问对象):DAO层负责与数据源(如数据库)进行交互,并提供对数据的持久化和访问操作。它封装了对数据的增删改查等数据库操作,提供了一种面向对象的方式来访问和操作数据,隐藏了底层数据库的细节。DAO层通常包含与数据源交互的接口和实现类,可以通过接口定义一组标准的数据访问方法,而具体的实现类则负责实现这些方法。

2、Service(服务层):Service层是业务逻辑的处理层,负责封装具体的业务逻辑,并协调不同的DAO操作来完成特定的任务。它作为DAO层和表示层(如控制器或界面)之间的中间层,提供一个统一的接口供表示层调用。Service层可以处理数据的验证、事务管理、权限控制等与业务相关的逻辑。

3、Domain(领域模型):Domain层代表了项目中的领域模型或实体对象,它是项目中的核心部分。领域模型通常对应于真实世界中的概念,如用户、订单、产品等,它们具有特定的属性和行为。Domain层通常包含与领域模型相关的类和接口,用于定义和表示领域模型的结构和行为。

这种分层架构的设计模式可以帮助提高代码的可维护性、可扩展性和可重用性,使不同的职责得到清晰的划分,并且各层之间通过接口进行解耦,降低了模块之间的依赖性。

4种资源:

资源拥有者(小明);
客户端(软文系统);
授权服务(微信公众号平台);
受保护资源(小明公众号内的文章);

auth2.0.png

QQ20231122-234339@2x.png

QQ20231123-000141@2x.png

问题:

微信小程序使用 echarts 时,控制台报错
[Component] : canvas 2d 接口支持同层渲染且性能更佳,建议切换使用。详见文档

原因:

echarts内部原因

解决办法:

1、修改源码 ec-canvas/ec-canvas.js

// before 
  data: {
    isUseNewCanvas: false
  },

// after  
  data: {
    isUseNewCanvas: true
  },

修改如上所示即可。

2、同时,需要组件调用方不能设置 force-use-old-canvas=“true”

<ec-canvas id="mychart-dom-bar" canvas-id="mychart-bar"   ec="{{ ec }}"></ec-canvas>

转自:https://blog.csdn.net/qubes/article/details/130660989

php.8.2.png

动态属性的使用

弃用动态属性创建,除非类选择使用 #[\AllowDynamicProperties] 注解。stdClass 允许动态属性。__get()/__set() 魔术方法不受此更改的影响。解决动态属性弃用警告,可以通过以下方式:

  1. 声明属性(首选)。
  2. 将 #[\AllowDynamicProperties] 添加到 #[\AllowDynamicProperties](这也适用于所有子类)。
  3. 如果需要将附加数据于不属于自己的对象相关联,则使用 WeakMap。

WeakMap方法比较常用,因为1与2是需要添加类中,这个类有可能不是自已写的,比如框架的类则不能修改,这时需要用到第3条,用法如下:

<?php
$wm = new WeakMap();

$o = new stdClass;

class A {
    public function __destruct() {
        echo "Dead!\n";
    }
}

$wm[$o] = new A;

var_dump(count($wm));
echo "Unsetting...\n";
unset($o);
echo "Done\n";
var_dump(count($wm));


https://www.php.net/manual/zh/migration82.deprecated.php